@fw-components/formula-editor 2.0.7-formula-editor-enhancements.11 → 2.0.7-formula-editor.21
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/dist/formula-editor/src/formula-editor.js +64 -179
- package/dist/formula-editor/src/styles/{formula-editor-styles.js → editor.js} +2 -2
- package/dist/formula-editor/src/styles/suggestion-menu.js +58 -0
- package/dist/formula-editor/src/suggestion-menu.js +72 -86
- package/dist/formula-editor/src/{helpers/types.js → types/index.js} +6 -0
- package/dist/formula-editor/src/utils/constants.js +8 -0
- package/dist/formula-editor/src/{parser.js → utils/parser.js} +92 -135
- package/dist/formula-editor/src/utils/queue.js +28 -0
- package/dist/formula-editor/src/utils/recommendor.js +15 -0
- package/dist/formula-editor/src/utils/stack.js +20 -0
- package/package.json +9 -3
- package/dist/formula-editor/src/cursor.js +0 -142
- package/dist/formula-editor/src/formula-builder.js +0 -139
- package/dist/formula-editor/src/formula-creator.js +0 -83
- package/dist/formula-editor/src/helpers.js +0 -54
- package/dist/formula-editor/src/recommendor.js +0 -18
- package/dist/formula-editor/src/sub-components/operator-input.js +0 -24
- package/dist/styles/src/button-styles.js +0 -419
|
@@ -6,89 +6,41 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
};
|
|
7
7
|
import { html, LitElement } from "lit";
|
|
8
8
|
import { customElement, property, state, query } from "lit/decorators.js";
|
|
9
|
-
import { FormulaEditorStyles } from "./styles/formula-editor-styles.js";
|
|
10
|
-
import { Parser } from "./parser.js";
|
|
11
9
|
import "./suggestion-menu.js";
|
|
10
|
+
import { Parser } from "./utils/parser.js";
|
|
11
|
+
import { FormulaEditorStyles } from "./styles/editor.js";
|
|
12
12
|
let FormulaEditor = class FormulaEditor extends LitElement {
|
|
13
13
|
constructor() {
|
|
14
|
-
super();
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.recommendations = [];
|
|
16
|
+
this.currentCursorPosition = 0;
|
|
17
|
+
this.isFocused = false;
|
|
15
18
|
/**
|
|
16
|
-
*
|
|
17
|
-
* because TS doesn't support that.
|
|
18
|
-
* @see https://github.com/lit/lit-element/issues/414
|
|
19
|
+
* Text area input value
|
|
19
20
|
*/
|
|
20
|
-
this._formattedContent = null;
|
|
21
|
-
this._recommendations = null;
|
|
22
|
-
this._calculatedResult = undefined;
|
|
23
|
-
/**
|
|
24
|
-
* If `parseInput` is called to add a recommendation, say by clicking,
|
|
25
|
-
* browser removes focus from the input box. In that case, we have no way
|
|
26
|
-
* of knowing where the cursor previously was, other than storing it somewhere.
|
|
27
|
-
*/
|
|
28
|
-
this.currentCursorPosition = null;
|
|
29
|
-
this.currentCursorRect = undefined;
|
|
30
|
-
this.lastInputType = "";
|
|
31
|
-
this.isFocus = false;
|
|
32
21
|
this.content = "";
|
|
33
22
|
this.placeholder = "Type your formula...";
|
|
34
23
|
this.variables = new Map();
|
|
35
24
|
this.minSuggestionLen = 2;
|
|
36
|
-
this.errorString =
|
|
37
|
-
this._parser = new Parser(this.variables, this.minSuggestionLen);
|
|
25
|
+
this.errorString = "";
|
|
38
26
|
}
|
|
39
27
|
firstUpdated(_changedProperties) {
|
|
40
|
-
this._parser = new Parser(this.variables, this.minSuggestionLen);
|
|
41
|
-
let editor = this.shadowRoot?.getElementById("wysiwyg-editor");
|
|
42
28
|
const inputListener = this.handleContentUpdate.bind(this);
|
|
43
|
-
editor.addEventListener("input", inputListener);
|
|
44
|
-
editor.focus();
|
|
29
|
+
this.editor.addEventListener("input", inputListener);
|
|
30
|
+
this.editor.focus();
|
|
45
31
|
}
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const newIndex = direction === "ArrowDown"
|
|
51
|
-
? (currentIndex + 1) % this._recommendations.length
|
|
52
|
-
: direction === "ArrowUp"
|
|
53
|
-
? (currentIndex - 1 + this._recommendations.length) % this._recommendations.length
|
|
54
|
-
: currentIndex;
|
|
55
|
-
this._selectedRecommendation = this._recommendations[newIndex];
|
|
56
|
-
this.scrollToSelectedRecommendation(newIndex);
|
|
57
|
-
}
|
|
58
|
-
scrollToSelectedRecommendation(index) {
|
|
59
|
-
const suggestionMenu = this.shadowRoot?.querySelector("suggestion-menu");
|
|
60
|
-
if (suggestionMenu) {
|
|
61
|
-
const listItem = suggestionMenu.shadowRoot?.querySelectorAll("li")[index];
|
|
62
|
-
if (listItem) {
|
|
63
|
-
listItem.scrollIntoView({
|
|
64
|
-
block: "nearest",
|
|
65
|
-
inline: "nearest",
|
|
66
|
-
});
|
|
32
|
+
updated(_changedProperties) {
|
|
33
|
+
if (_changedProperties.has("content")) {
|
|
34
|
+
if (!this.content?.trim()) {
|
|
35
|
+
this.recommendations = Array.from(this.variables.keys());
|
|
67
36
|
}
|
|
37
|
+
this._adjustTextAreaHeight();
|
|
68
38
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (event.code == "Tab" && this._recommendations?.length == 1) {
|
|
72
|
-
this._selectedRecommendation = null;
|
|
73
|
-
event.preventDefault();
|
|
74
|
-
this.parseInput(this._recommendations[0]);
|
|
75
|
-
}
|
|
76
|
-
else if (event.code === "ArrowDown" || event.code === "ArrowUp") {
|
|
77
|
-
if (this._recommendations)
|
|
78
|
-
event.preventDefault();
|
|
79
|
-
this.navigateRecommendations(event.code);
|
|
80
|
-
this.requestUpdate();
|
|
81
|
-
}
|
|
82
|
-
else if (event.code === "Enter" && this._selectedRecommendation) {
|
|
83
|
-
event.preventDefault();
|
|
84
|
-
this.parseInput(this._selectedRecommendation);
|
|
85
|
-
this._selectedRecommendation = null;
|
|
39
|
+
if (_changedProperties.has("variables") && this.variables.size) {
|
|
40
|
+
this._parser = new Parser(this.variables, this.minSuggestionLen);
|
|
86
41
|
}
|
|
87
42
|
}
|
|
88
43
|
onClickRecommendation(recommendation) {
|
|
89
|
-
let editor = this.shadowRoot?.getElementById("wysiwyg-editor");
|
|
90
|
-
if (!editor)
|
|
91
|
-
return;
|
|
92
44
|
this.parseInput(recommendation);
|
|
93
45
|
}
|
|
94
46
|
handleContentUpdate(event) {
|
|
@@ -97,149 +49,94 @@ let FormulaEditor = class FormulaEditor extends LitElement {
|
|
|
97
49
|
this.content = event.target.value;
|
|
98
50
|
this.parseInput();
|
|
99
51
|
}
|
|
100
|
-
|
|
101
|
-
const editor = this.shadowRoot?.getElementById("wysiwyg-editor");
|
|
52
|
+
_adjustTextAreaHeight() {
|
|
102
53
|
if (!this.content)
|
|
103
|
-
editor.style.height = "var(--fe-height, 30px)";
|
|
104
|
-
if (editor.scrollHeight > editor.clientHeight)
|
|
105
|
-
editor.style.height = (editor.scrollHeight + 5)
|
|
54
|
+
this.editor.style.height = "var(--fe-height, 30px)";
|
|
55
|
+
if (this.editor.scrollHeight > this.editor.clientHeight)
|
|
56
|
+
this.editor.style.height = String(this.editor.scrollHeight + 5).concat("px");
|
|
106
57
|
}
|
|
107
58
|
/**
|
|
108
|
-
*
|
|
109
59
|
* @param recommendation The recommendation which needs to be inserted
|
|
110
60
|
* at the current cursor position
|
|
111
|
-
* @param manageCursor Whether or not cursor management is needed. Not
|
|
112
|
-
* needed when manual insertion of text is required (eg: during initialization)
|
|
113
61
|
* @returns void
|
|
114
62
|
*/
|
|
115
|
-
parseInput(recommendation =
|
|
116
|
-
|
|
117
|
-
if (!editor)
|
|
118
|
-
return;
|
|
119
|
-
this.currentCursorPosition = editor.selectionStart;
|
|
63
|
+
parseInput(recommendation = "") {
|
|
64
|
+
this.currentCursorPosition = this.editor.selectionStart;
|
|
120
65
|
const parseOutput = this._parser.parseInput(this.content, this.currentCursorPosition, recommendation);
|
|
121
|
-
this.
|
|
122
|
-
this._formattedContent = parseOutput.formattedContent;
|
|
66
|
+
this.recommendations = parseOutput.recommendations;
|
|
123
67
|
this.errorString = parseOutput.errorString;
|
|
124
68
|
/**
|
|
125
69
|
* Don't modify the text stream manually if the text is being composed,
|
|
126
|
-
* unless the user manually chooses to do so by selecting a
|
|
70
|
+
* unless the user manually chooses to do so by selecting a recommendation.
|
|
127
71
|
* @see https://github.com/w3c/input-events/issues/86
|
|
128
72
|
* @see https://github.com/w3c/input-events/pull/122
|
|
129
73
|
* @see https://bugs.chromium.org/p/chromium/issues/detail?id=689541
|
|
130
|
-
|
|
74
|
+
*/
|
|
131
75
|
if (this.lastInputType !== "insertCompositionText" || recommendation) {
|
|
132
|
-
|
|
133
|
-
this.content = formattedString;
|
|
76
|
+
this.content = parseOutput.formattedString;
|
|
134
77
|
}
|
|
135
|
-
if (recommendation) {
|
|
136
|
-
this.
|
|
78
|
+
if (Boolean(recommendation)) {
|
|
79
|
+
this.recommendations = [];
|
|
137
80
|
this.currentCursorPosition = parseOutput.newCursorPosition;
|
|
81
|
+
/**
|
|
82
|
+
* update cursor position in text area
|
|
83
|
+
*/
|
|
138
84
|
setTimeout(() => {
|
|
139
|
-
editor.setSelectionRange(this.currentCursorPosition, this.currentCursorPosition);
|
|
85
|
+
this.editor.setSelectionRange(this.currentCursorPosition, this.currentCursorPosition);
|
|
140
86
|
}, 0);
|
|
141
87
|
}
|
|
142
88
|
this.dispatchEvent(new CustomEvent("fw-formula-content-changed", {
|
|
143
89
|
detail: {
|
|
144
90
|
formulaString: this.content,
|
|
145
91
|
error: this.errorString,
|
|
146
|
-
recommendations: this.
|
|
92
|
+
recommendations: this.recommendations,
|
|
147
93
|
},
|
|
148
94
|
bubbles: true,
|
|
149
95
|
}));
|
|
150
|
-
editor.focus();
|
|
151
96
|
}
|
|
152
|
-
|
|
153
|
-
if (
|
|
97
|
+
formatFormula() {
|
|
98
|
+
if (!Boolean(this.content))
|
|
154
99
|
return;
|
|
155
|
-
}
|
|
156
|
-
const calculatedResult = this._parser.calculate(this.content);
|
|
157
100
|
this.content = this._parser.addParentheses(this.content) ?? this.content;
|
|
158
101
|
this.parseInput();
|
|
159
|
-
this.
|
|
160
|
-
this.errorString = calculatedResult.errorString;
|
|
161
|
-
this._recommendations = null;
|
|
162
|
-
this.requestUpdate();
|
|
102
|
+
this.recommendations = [];
|
|
163
103
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
this.content = this._parser.addParentheses(this.content) ?? this.content;
|
|
169
|
-
this.parseInput();
|
|
170
|
-
this._recommendations = null;
|
|
171
|
-
this.requestUpdate();
|
|
172
|
-
}
|
|
173
|
-
async updated(_changedProperties) {
|
|
174
|
-
if (_changedProperties.has("content")) {
|
|
175
|
-
if (!this.content.trim()) {
|
|
176
|
-
this._recommendations = Array.from(this.variables.keys());
|
|
177
|
-
}
|
|
178
|
-
this._adjustInputHeight();
|
|
179
|
-
}
|
|
180
|
-
if (_changedProperties.has("variables")) {
|
|
181
|
-
this._parser = new Parser(this.variables, this.minSuggestionLen);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
handleFocusOut(e) {
|
|
185
|
-
this.isFocus = false;
|
|
186
|
-
this._recommendations = null;
|
|
187
|
-
this.requestUpdate();
|
|
188
|
-
}
|
|
189
|
-
handleFocus(e) {
|
|
190
|
-
this.isFocus = true;
|
|
191
|
-
if (!this.content.trim()) {
|
|
192
|
-
this._recommendations = Array.from(this.variables.keys());
|
|
193
|
-
}
|
|
104
|
+
handleFocus(focus) {
|
|
105
|
+
this.isFocused = focus;
|
|
194
106
|
}
|
|
195
107
|
render() {
|
|
196
108
|
return html `
|
|
197
|
-
<style>
|
|
198
|
-
|
|
199
|
-
</
|
|
109
|
+
<style>${FormulaEditorStyles}</style>
|
|
110
|
+
|
|
111
|
+
${this.label ? html `<label for="wysiwyg-editor" class="editor-label">${this.label}</label>` : ""}
|
|
112
|
+
|
|
113
|
+
<textarea
|
|
114
|
+
id="wysiwyg-editor"
|
|
115
|
+
class=${this.errorString?.length ? "error" : ""}
|
|
116
|
+
.value=${this.content}
|
|
117
|
+
.placeholder=${this.placeholder}
|
|
118
|
+
spellcheck="false"
|
|
119
|
+
autocomplete="off"
|
|
120
|
+
@blur=${() => this.handleFocus(false)}
|
|
121
|
+
@focus=${() => this.handleFocus(true)}
|
|
122
|
+
></textarea>
|
|
200
123
|
|
|
201
|
-
|
|
202
|
-
? html `<
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
placeholder=${this.placeholder}
|
|
209
|
-
id="wysiwyg-editor"
|
|
210
|
-
class=${this.errorString?.length ? "error" : ""}
|
|
211
|
-
.value=${this.content}
|
|
212
|
-
@keydown=${this.handleKeyboardEvents}
|
|
213
|
-
@blur=${this.handleFocusOut}
|
|
214
|
-
@focus=${this.handleFocus}
|
|
215
|
-
></textarea>
|
|
216
|
-
${this._recommendations && this.isFocus
|
|
217
|
-
? html ` <suggestion-menu
|
|
218
|
-
.recommendations=${this._recommendations}
|
|
219
|
-
.currentSelection=${this._selectedRecommendation}
|
|
220
|
-
.onClickRecommendation=${(e) => this.onClickRecommendation(e)}
|
|
221
|
-
@mousedown=${(e) => e.preventDefault()}
|
|
222
|
-
></suggestion-menu>`
|
|
223
|
-
: html ``}
|
|
224
|
-
<p>${this._calculatedResult}</p>
|
|
124
|
+
${this.isFocused && this.recommendations?.length
|
|
125
|
+
? html `<suggestion-menu
|
|
126
|
+
.recommendations=${this.recommendations}
|
|
127
|
+
.currentSelection=${this._selectedRecommendation}
|
|
128
|
+
.onClickRecommendation=${this.onClickRecommendation.bind(this)}
|
|
129
|
+
></suggestion-menu>`
|
|
130
|
+
: ''}
|
|
225
131
|
`;
|
|
226
132
|
}
|
|
227
133
|
};
|
|
228
134
|
__decorate([
|
|
229
135
|
state()
|
|
230
|
-
], FormulaEditor.prototype, "
|
|
231
|
-
__decorate([
|
|
232
|
-
state()
|
|
233
|
-
], FormulaEditor.prototype, "_recommendations", void 0);
|
|
234
|
-
__decorate([
|
|
235
|
-
state()
|
|
236
|
-
], FormulaEditor.prototype, "_calculatedResult", void 0);
|
|
136
|
+
], FormulaEditor.prototype, "recommendations", void 0);
|
|
237
137
|
__decorate([
|
|
238
138
|
state()
|
|
239
139
|
], FormulaEditor.prototype, "currentCursorPosition", void 0);
|
|
240
|
-
__decorate([
|
|
241
|
-
state()
|
|
242
|
-
], FormulaEditor.prototype, "currentCursorRect", void 0);
|
|
243
140
|
__decorate([
|
|
244
141
|
state()
|
|
245
142
|
], FormulaEditor.prototype, "lastInputType", void 0);
|
|
@@ -248,7 +145,7 @@ __decorate([
|
|
|
248
145
|
], FormulaEditor.prototype, "_selectedRecommendation", void 0);
|
|
249
146
|
__decorate([
|
|
250
147
|
state()
|
|
251
|
-
], FormulaEditor.prototype, "
|
|
148
|
+
], FormulaEditor.prototype, "isFocused", void 0);
|
|
252
149
|
__decorate([
|
|
253
150
|
property()
|
|
254
151
|
], FormulaEditor.prototype, "content", void 0);
|
|
@@ -259,19 +156,7 @@ __decorate([
|
|
|
259
156
|
property()
|
|
260
157
|
], FormulaEditor.prototype, "label", void 0);
|
|
261
158
|
__decorate([
|
|
262
|
-
property(
|
|
263
|
-
type: (Map),
|
|
264
|
-
converter: {
|
|
265
|
-
fromAttribute: (value) => {
|
|
266
|
-
if (value) {
|
|
267
|
-
return new Map(JSON.parse(value));
|
|
268
|
-
}
|
|
269
|
-
},
|
|
270
|
-
toAttribute: (value) => {
|
|
271
|
-
return JSON.stringify(Array.from(value.entries()));
|
|
272
|
-
},
|
|
273
|
-
},
|
|
274
|
-
})
|
|
159
|
+
property()
|
|
275
160
|
], FormulaEditor.prototype, "variables", void 0);
|
|
276
161
|
__decorate([
|
|
277
162
|
property()
|
|
@@ -280,7 +165,7 @@ __decorate([
|
|
|
280
165
|
property()
|
|
281
166
|
], FormulaEditor.prototype, "errorString", void 0);
|
|
282
167
|
__decorate([
|
|
283
|
-
query("wysiwyg-editor")
|
|
168
|
+
query("#wysiwyg-editor")
|
|
284
169
|
], FormulaEditor.prototype, "editor", void 0);
|
|
285
170
|
FormulaEditor = __decorate([
|
|
286
171
|
customElement("formula-editor")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { css } from "lit";
|
|
2
2
|
export const FormulaEditorStyles = css `
|
|
3
|
-
.
|
|
3
|
+
.editor-label {
|
|
4
4
|
display: block;
|
|
5
5
|
font-size: var(--fe-label-font-size, 0.8rem);
|
|
6
6
|
color: var(--fe-label-color, #515151);
|
|
@@ -13,7 +13,7 @@ export const FormulaEditorStyles = css `
|
|
|
13
13
|
padding: var(--fe-padding, 4px);
|
|
14
14
|
caret-color: var(--fe-caret-color, #fff);
|
|
15
15
|
color: var(--fe-text-color, #f7f1ff);
|
|
16
|
-
font-size: var(--fe-text-font-size,
|
|
16
|
+
font-size: var(--fe-text-font-size, 14px);
|
|
17
17
|
min-width: 100%;
|
|
18
18
|
min-height: 30px;
|
|
19
19
|
height: var(--fe-height, 30px);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
export const SuggestionMenuStyles = css `
|
|
3
|
+
ul {
|
|
4
|
+
position: relative;
|
|
5
|
+
border: 1px solid var(--fe-suggestion-color, white);
|
|
6
|
+
color: var(--fe-suggestion-color, #bab6c0);
|
|
7
|
+
background-color: var(--fe-suggestion-background-color, white);
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
width: var(--fe-suggestion-width, 20vw);
|
|
10
|
+
max-height: 25vh;
|
|
11
|
+
overflow-x: auto;
|
|
12
|
+
overflow-y: auto;
|
|
13
|
+
list-style-type: none;
|
|
14
|
+
padding: 0;
|
|
15
|
+
margin: 0;
|
|
16
|
+
box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.13);
|
|
17
|
+
border-radius: 5px;
|
|
18
|
+
z-index: 99999;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
li {
|
|
22
|
+
margin: 0;
|
|
23
|
+
padding: 0.5em 1rem;
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
font-family: var(--theme-font);
|
|
26
|
+
font-size: var(--secondary-font-size, 16px);
|
|
27
|
+
color: var(--secondary-color, #bab6c0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
li:hover, li:focus-visible, li.selected {
|
|
31
|
+
color: var(--fe-suggestion-focus-color, #69676c);
|
|
32
|
+
background: rgba(var(--fe-suggestion-focus-background, 86, 86, 86), 0.1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
/* Scrollbar styling */
|
|
37
|
+
::-webkit-scrollbar {
|
|
38
|
+
width: 10px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
::-webkit-scrollbar-track {
|
|
42
|
+
background: transparent;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
::-webkit-scrollbar-thumb {
|
|
46
|
+
background: #ccc;
|
|
47
|
+
border-radius: 5px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
::-webkit-scrollbar-thumb:hover {
|
|
51
|
+
background: #aaa;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Optional shadow for the dropdown */
|
|
55
|
+
.content {
|
|
56
|
+
box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.13);
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
@@ -4,101 +4,81 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import {
|
|
8
|
-
import { customElement, property } from "lit/decorators.js";
|
|
7
|
+
import { html, LitElement } from "lit";
|
|
8
|
+
import { customElement, property, query, state } from "lit/decorators.js";
|
|
9
|
+
import { SuggestionMenuStyles } from "./styles/suggestion-menu";
|
|
9
10
|
let SuggestionMenu = class SuggestionMenu extends LitElement {
|
|
10
11
|
constructor() {
|
|
11
12
|
super(...arguments);
|
|
12
13
|
this.recommendations = [];
|
|
13
14
|
this.onClickRecommendation = (recommendation) => { };
|
|
14
|
-
this.
|
|
15
|
+
this._selectedRecommendationIndex = 0;
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
/* Scrollbar styling */
|
|
60
|
-
::-webkit-scrollbar {
|
|
61
|
-
width: 10px;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
::-webkit-scrollbar-track {
|
|
65
|
-
background: transparent;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
::-webkit-scrollbar-thumb {
|
|
69
|
-
background: #ccc;
|
|
70
|
-
border-radius: 5px;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
::-webkit-scrollbar-thumb:hover {
|
|
74
|
-
background: #aaa;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/* Optional shadow for the dropdown */
|
|
78
|
-
.content {
|
|
79
|
-
box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.13);
|
|
80
|
-
}
|
|
81
|
-
`; }
|
|
82
|
-
handleKeydown(event, recommendation) {
|
|
83
|
-
if (event.code == "Enter") {
|
|
17
|
+
firstUpdated(_changedProperties) {
|
|
18
|
+
this.abortController = new AbortController();
|
|
19
|
+
const handleKeydown = this.handleKeydown.bind(this);
|
|
20
|
+
document.addEventListener("keydown", handleKeydown, { signal: this.abortController?.signal });
|
|
21
|
+
}
|
|
22
|
+
scrollToSelectedRecommendation(index) {
|
|
23
|
+
const listItem = this.suggestionList?.querySelectorAll("li")[index];
|
|
24
|
+
if (!listItem)
|
|
25
|
+
return;
|
|
26
|
+
listItem.scrollIntoView({
|
|
27
|
+
block: "nearest",
|
|
28
|
+
inline: "nearest",
|
|
29
|
+
behavior: "smooth"
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
navigate(direction) {
|
|
33
|
+
if (!this.recommendations)
|
|
34
|
+
return;
|
|
35
|
+
let newIndex = this._selectedRecommendationIndex;
|
|
36
|
+
if (direction === "down")
|
|
37
|
+
newIndex = (this._selectedRecommendationIndex + 1) % this.recommendations.length;
|
|
38
|
+
else if (direction === "up")
|
|
39
|
+
newIndex = (this._selectedRecommendationIndex - 1 + this.recommendations.length) % this.recommendations.length;
|
|
40
|
+
this._selectedRecommendationIndex = newIndex;
|
|
41
|
+
this.scrollToSelectedRecommendation(newIndex);
|
|
42
|
+
}
|
|
43
|
+
handleKeydown(event) {
|
|
44
|
+
if (event.code === "Tab") {
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
if (this.recommendations?.length === 1) {
|
|
47
|
+
this._handleRecommendationSelect();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const direction = event.shiftKey ? "up" : "down";
|
|
51
|
+
this.navigate(direction);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if (event.code === "ArrowDown" || event.code === "ArrowUp") {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
const direction = event.code === "ArrowDown" ? "down" : "up";
|
|
57
|
+
this.navigate(direction);
|
|
58
|
+
}
|
|
59
|
+
else if (event.code === "Enter") {
|
|
84
60
|
event.preventDefault();
|
|
85
|
-
|
|
86
|
-
this.onClickRecommendation(recommendation);
|
|
61
|
+
this._handleRecommendationSelect();
|
|
87
62
|
}
|
|
88
63
|
}
|
|
64
|
+
_handleRecommendationSelect(index = this._selectedRecommendationIndex) {
|
|
65
|
+
const recommendation = this.recommendations[index];
|
|
66
|
+
if (!recommendation)
|
|
67
|
+
return;
|
|
68
|
+
this.onClickRecommendation(recommendation);
|
|
69
|
+
this._selectedRecommendationIndex = -1;
|
|
70
|
+
}
|
|
71
|
+
disconnectedCallback() {
|
|
72
|
+
this.abortController?.abort();
|
|
73
|
+
}
|
|
89
74
|
render() {
|
|
90
75
|
return html `
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
@keydown=${(e) => this.handleKeydown(e, recommendation)}
|
|
98
|
-
>
|
|
99
|
-
${recommendation}
|
|
100
|
-
</li>`;
|
|
101
|
-
})}
|
|
76
|
+
<style>${SuggestionMenuStyles}</style>
|
|
77
|
+
<ul class="wysiwyg-suggestion-menu" @mousedown=${(e) => e.preventDefault()}>
|
|
78
|
+
${this.recommendations.map((recommendation, index) => html `<li
|
|
79
|
+
class="${this._selectedRecommendationIndex === index ? "selected" : ""}"
|
|
80
|
+
@click=${(e) => this._handleRecommendationSelect(index)}
|
|
81
|
+
>${recommendation}</li>`)}
|
|
102
82
|
</ul>
|
|
103
83
|
`;
|
|
104
84
|
}
|
|
@@ -110,8 +90,14 @@ __decorate([
|
|
|
110
90
|
property()
|
|
111
91
|
], SuggestionMenu.prototype, "onClickRecommendation", void 0);
|
|
112
92
|
__decorate([
|
|
113
|
-
|
|
114
|
-
], SuggestionMenu.prototype, "
|
|
93
|
+
state()
|
|
94
|
+
], SuggestionMenu.prototype, "_selectedRecommendationIndex", void 0);
|
|
95
|
+
__decorate([
|
|
96
|
+
state()
|
|
97
|
+
], SuggestionMenu.prototype, "abortController", void 0);
|
|
98
|
+
__decorate([
|
|
99
|
+
query(".wysiwyg-suggestion-menu")
|
|
100
|
+
], SuggestionMenu.prototype, "suggestionList", void 0);
|
|
115
101
|
SuggestionMenu = __decorate([
|
|
116
102
|
customElement("suggestion-menu")
|
|
117
103
|
], SuggestionMenu);
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export var Expectation;
|
|
2
|
+
(function (Expectation) {
|
|
3
|
+
Expectation[Expectation["VARIABLE"] = 0] = "VARIABLE";
|
|
4
|
+
Expectation[Expectation["OPERATOR"] = 1] = "OPERATOR";
|
|
5
|
+
Expectation[Expectation["UNDEFINED"] = 2] = "UNDEFINED";
|
|
6
|
+
})(Expectation || (Expectation = {}));
|
|
1
7
|
export var Operator;
|
|
2
8
|
(function (Operator) {
|
|
3
9
|
Operator["PLUS"] = "+";
|