@fw-components/formula-editor 2.0.7-formula-editor.35 → 2.0.7-formula-editor.37
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.
|
@@ -21,13 +21,13 @@ let FormulaEditor = class FormulaEditor extends LitElement {
|
|
|
21
21
|
*/
|
|
22
22
|
this.content = "";
|
|
23
23
|
this.placeholder = "Type your formula...";
|
|
24
|
+
this.recommendationLabels = new Map();
|
|
24
25
|
this.variables = new Map();
|
|
25
26
|
this.minSuggestionLen = 2;
|
|
26
27
|
this.errorString = "";
|
|
27
28
|
this.formulaRegex = /'[^']*'|[A-Za-z0-9_#@]+|[-+(),*^/\s]/g;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.editor.focus();
|
|
29
|
+
this.allowedNumbers = true;
|
|
30
|
+
this.allowedOperators = new Set(["^", "+", "-", "*", "/"]);
|
|
31
31
|
}
|
|
32
32
|
updated(_changedProperties) {
|
|
33
33
|
if (_changedProperties.has("content")) {
|
|
@@ -37,7 +37,7 @@ let FormulaEditor = class FormulaEditor extends LitElement {
|
|
|
37
37
|
this._adjustTextAreaHeight();
|
|
38
38
|
}
|
|
39
39
|
if (_changedProperties.has("variables")) {
|
|
40
|
-
this._parser = new Parser(this.variables, this.formulaRegex, this.minSuggestionLen);
|
|
40
|
+
this._parser = new Parser(this.variables, this.formulaRegex, this.allowedNumbers, this.allowedOperators, this.variableType, this.minSuggestionLen);
|
|
41
41
|
this.recommendations = Array.from(this.variables.keys());
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -151,6 +151,7 @@ let FormulaEditor = class FormulaEditor extends LitElement {
|
|
|
151
151
|
.recommendations=${this.recommendations}
|
|
152
152
|
.currentSelection=${this._selectedRecommendation}
|
|
153
153
|
.onRecommendationClick=${this.onRecommendationClick.bind(this)}
|
|
154
|
+
.recommendationLabels=${this.recommendationLabels}
|
|
154
155
|
></suggestion-menu>`
|
|
155
156
|
: ''}
|
|
156
157
|
`;
|
|
@@ -177,6 +178,9 @@ __decorate([
|
|
|
177
178
|
__decorate([
|
|
178
179
|
property()
|
|
179
180
|
], FormulaEditor.prototype, "placeholder", void 0);
|
|
181
|
+
__decorate([
|
|
182
|
+
property()
|
|
183
|
+
], FormulaEditor.prototype, "recommendationLabels", void 0);
|
|
180
184
|
__decorate([
|
|
181
185
|
property()
|
|
182
186
|
], FormulaEditor.prototype, "label", void 0);
|
|
@@ -192,6 +196,15 @@ __decorate([
|
|
|
192
196
|
__decorate([
|
|
193
197
|
property()
|
|
194
198
|
], FormulaEditor.prototype, "formulaRegex", void 0);
|
|
199
|
+
__decorate([
|
|
200
|
+
property()
|
|
201
|
+
], FormulaEditor.prototype, "allowedNumbers", void 0);
|
|
202
|
+
__decorate([
|
|
203
|
+
property()
|
|
204
|
+
], FormulaEditor.prototype, "allowedOperators", void 0);
|
|
205
|
+
__decorate([
|
|
206
|
+
property()
|
|
207
|
+
], FormulaEditor.prototype, "variableType", void 0);
|
|
195
208
|
__decorate([
|
|
196
209
|
query("#wysiwyg-editor")
|
|
197
210
|
], FormulaEditor.prototype, "editor", void 0);
|
|
@@ -11,6 +11,7 @@ let SuggestionMenu = class SuggestionMenu extends LitElement {
|
|
|
11
11
|
constructor() {
|
|
12
12
|
super(...arguments);
|
|
13
13
|
this.recommendations = [];
|
|
14
|
+
this.recommendationLabels = new Map();
|
|
14
15
|
this.onRecommendationClick = () => { };
|
|
15
16
|
this._selectedRecommendationIndex = -1;
|
|
16
17
|
}
|
|
@@ -49,7 +50,7 @@ let SuggestionMenu = class SuggestionMenu extends LitElement {
|
|
|
49
50
|
${this.recommendations.map((recommendation, index) => html `<li
|
|
50
51
|
class="${this._selectedRecommendationIndex === index ? "selected" : ""}"
|
|
51
52
|
@click=${(e) => this.handleRecommendationSelect(index)}
|
|
52
|
-
>${recommendation}</li>`)}
|
|
53
|
+
>${this.recommendationLabels.get(recommendation) ?? recommendation}</li>`)}
|
|
53
54
|
</ul>
|
|
54
55
|
`;
|
|
55
56
|
}
|
|
@@ -57,6 +58,9 @@ let SuggestionMenu = class SuggestionMenu extends LitElement {
|
|
|
57
58
|
__decorate([
|
|
58
59
|
property()
|
|
59
60
|
], SuggestionMenu.prototype, "recommendations", void 0);
|
|
61
|
+
__decorate([
|
|
62
|
+
property()
|
|
63
|
+
], SuggestionMenu.prototype, "recommendationLabels", void 0);
|
|
60
64
|
__decorate([
|
|
61
65
|
property()
|
|
62
66
|
], SuggestionMenu.prototype, "onRecommendationClick", void 0);
|
|
@@ -3,13 +3,21 @@ import { Recommender } from "./recommendor.js";
|
|
|
3
3
|
import { Stack } from "./stack.js";
|
|
4
4
|
import { Queue } from "./queue.js";
|
|
5
5
|
import { Expectation } from "../types";
|
|
6
|
-
import {
|
|
6
|
+
import { operatorPrecedence, unaryOperators } from "./constants.js";
|
|
7
7
|
import { getFormulaTokens } from "./get-formula-tokens.js";
|
|
8
8
|
export class Parser {
|
|
9
|
-
constructor(variables, formulaRegex, minSuggestionLen) {
|
|
9
|
+
constructor(variables, formulaRegex, allowedNumbers, allowedOperators, variableType, minSuggestionLen) {
|
|
10
10
|
this.variables = variables;
|
|
11
11
|
this.formulaRegex = formulaRegex;
|
|
12
12
|
this._recommender = new Recommender(Array.from(this.variables.keys()), minSuggestionLen);
|
|
13
|
+
this.allowedNumbers = allowedNumbers;
|
|
14
|
+
this.allowedOperators = allowedOperators;
|
|
15
|
+
this.variableType = variableType;
|
|
16
|
+
}
|
|
17
|
+
isNumber(value) {
|
|
18
|
+
if (!this.allowedNumbers || value.trim() === "")
|
|
19
|
+
return false;
|
|
20
|
+
return !Number.isNaN(Number(value));
|
|
13
21
|
}
|
|
14
22
|
parseInput(formula, prevCurPos = null, recommendation = null) {
|
|
15
23
|
const tokens = getFormulaTokens(formula, this.formulaRegex);
|
|
@@ -30,8 +38,8 @@ export class Parser {
|
|
|
30
38
|
return parseOutput;
|
|
31
39
|
}
|
|
32
40
|
tokens?.forEach((token) => {
|
|
33
|
-
let isNumber =
|
|
34
|
-
const isOperator =
|
|
41
|
+
let isNumber = this.variables.has(token) || this.isNumber(token);
|
|
42
|
+
const isOperator = this.allowedOperators.has(token);
|
|
35
43
|
const isSpace = token.trim() === "";
|
|
36
44
|
const isBracket = token === "(" || token === ")";
|
|
37
45
|
if (isSpace) {
|
|
@@ -48,7 +56,7 @@ export class Parser {
|
|
|
48
56
|
if (currentPosition <= prevCurPos && currentPosition + token.length >= prevCurPos) {
|
|
49
57
|
if (recommendation) {
|
|
50
58
|
isNumber = true;
|
|
51
|
-
if (
|
|
59
|
+
if (this.allowedOperators.has(token)) {
|
|
52
60
|
const updatedTokenString = `${token} ${recommendation}`;
|
|
53
61
|
parseOutput.formattedString += updatedTokenString;
|
|
54
62
|
currentPosition += updatedTokenString.length;
|
|
@@ -67,14 +75,21 @@ export class Parser {
|
|
|
67
75
|
/**
|
|
68
76
|
* Error checks
|
|
69
77
|
* skip error check if there is one already
|
|
70
|
-
|
|
78
|
+
*/
|
|
71
79
|
if (expectation != Expectation.UNDEFINED) {
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Unknown symbol/variable/word
|
|
82
|
+
*/
|
|
83
|
+
if (!(isNumber || isOperator || isBracket || isSpace)) {
|
|
84
|
+
parseOutput.errorString = `${this.variableType} : ${token} does not exist`;
|
|
85
|
+
expectation = Expectation.UNDEFINED;
|
|
86
|
+
}
|
|
87
|
+
else if (this.allowedOperators.has(previousToken) && isOperator) {
|
|
88
|
+
parseOutput.errorString = `Use ${this.variableType}${this.allowedNumbers ? " or numbers" : ""} after ${previousToken}. Pls do not use consecutive two mathametical operators (+,-,*,/,^)`;
|
|
74
89
|
expectation = Expectation.UNDEFINED;
|
|
75
90
|
}
|
|
76
91
|
else if (parentheses.isEmpty() && token === ")") {
|
|
77
|
-
parseOutput.errorString =
|
|
92
|
+
parseOutput.errorString = "Unexpected closing bracket. Make sure all opening brackets '(' have matching closing brackets ')'.";
|
|
78
93
|
expectation = Expectation.UNDEFINED;
|
|
79
94
|
}
|
|
80
95
|
/**
|
|
@@ -82,36 +97,29 @@ export class Parser {
|
|
|
82
97
|
* No error for Unary `+` and `-` as they might represent a positive or negative number respectively
|
|
83
98
|
*/
|
|
84
99
|
else if (expectation === Expectation.VARIABLE && !isNumber && !isSpace && token != "("
|
|
85
|
-
&& !((unaryOperators.includes(token)) && (!parsedString.trim() || previousToken === "(" ||
|
|
86
|
-
parseOutput.errorString = `
|
|
100
|
+
&& !((unaryOperators.includes(token)) && (!parsedString.trim() || previousToken === "(" || this.allowedOperators.has(previousToken)))) {
|
|
101
|
+
parseOutput.errorString = `Use ${this.variableType}${this.allowedNumbers ? " or numbers" : ""} after ${previousToken}.`;
|
|
87
102
|
expectation = Expectation.UNDEFINED;
|
|
88
103
|
}
|
|
89
104
|
/**
|
|
90
105
|
* Multiple number/variable together without operator
|
|
91
106
|
*/
|
|
92
107
|
else if (expectation === Expectation.OPERATOR && !isOperator && !isSpace && token != ")") {
|
|
93
|
-
parseOutput.errorString = `
|
|
94
|
-
expectation = Expectation.UNDEFINED;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Unknown symbol/variable/word
|
|
98
|
-
*/
|
|
99
|
-
else if (!(isNumber || isOperator || isBracket || isSpace)) {
|
|
100
|
-
parseOutput.errorString = `Unknown word at position ${currentPosition}`;
|
|
108
|
+
parseOutput.errorString = `Use mathametical operators (${Array.from(this.allowedOperators).join(",")}) after ${previousToken}.`;
|
|
101
109
|
expectation = Expectation.UNDEFINED;
|
|
102
110
|
}
|
|
103
111
|
/**
|
|
104
112
|
* division by zero
|
|
105
113
|
*/
|
|
106
114
|
else if (isNumber && previousToken === "/" && (this.variables.get(token) === 0 || Number(token) === 0)) {
|
|
107
|
-
parseOutput.errorString = `Division by zero
|
|
115
|
+
parseOutput.errorString = `Division by zero is not possible`;
|
|
108
116
|
expectation = Expectation.UNDEFINED;
|
|
109
117
|
}
|
|
110
118
|
/**
|
|
111
119
|
* Empty brackets
|
|
112
120
|
*/
|
|
113
121
|
else if (previousToken === "(" && token === ")") {
|
|
114
|
-
parseOutput.errorString = `
|
|
122
|
+
parseOutput.errorString = `Pls do not use empty brackets ().`;
|
|
115
123
|
expectation = Expectation.UNDEFINED;
|
|
116
124
|
}
|
|
117
125
|
}
|
|
@@ -140,14 +148,14 @@ export class Parser {
|
|
|
140
148
|
parseOutput.formattedString += recommendation;
|
|
141
149
|
previousToken = recommendation;
|
|
142
150
|
}
|
|
143
|
-
if (
|
|
151
|
+
if (this.allowedOperators.has(previousToken) || !previousToken.trim().length) {
|
|
144
152
|
parseOutput.recommendations = !parseOutput.errorString?.length ? Array.from(this.variables.keys()) : [];
|
|
145
153
|
}
|
|
146
|
-
if (
|
|
147
|
-
parseOutput.errorString = `
|
|
154
|
+
if (this.allowedOperators.has(previousToken)) {
|
|
155
|
+
parseOutput.errorString = `Pls do not use mathametical operators (${Array.from(this.allowedOperators).join(",")}) at the end.`;
|
|
148
156
|
}
|
|
149
157
|
if (!parentheses.isEmpty()) {
|
|
150
|
-
parseOutput.errorString =
|
|
158
|
+
parseOutput.errorString = "Unexpected opening bracket. Make sure all closing brackets ')' have matching opening brackets '('.";
|
|
151
159
|
}
|
|
152
160
|
return parseOutput;
|
|
153
161
|
}
|
|
@@ -161,7 +169,7 @@ export class Parser {
|
|
|
161
169
|
let currentTokens = "";
|
|
162
170
|
// Check if variables include unary operators `-` and `+`.
|
|
163
171
|
for (const token of tokens) {
|
|
164
|
-
if ((unaryOperators.includes(token)) && (!currentTokens.trim() || previousToken === "(" ||
|
|
172
|
+
if ((unaryOperators.includes(token)) && (!currentTokens.trim() || previousToken === "(" || this.allowedOperators.has(previousToken))) {
|
|
165
173
|
carriedToken = token;
|
|
166
174
|
}
|
|
167
175
|
else if (carriedToken) {
|
|
@@ -189,8 +197,8 @@ export class Parser {
|
|
|
189
197
|
}
|
|
190
198
|
operatorStack.pop();
|
|
191
199
|
}
|
|
192
|
-
else if (
|
|
193
|
-
while (
|
|
200
|
+
else if (this.allowedOperators.has(token)) {
|
|
201
|
+
while (this.allowedOperators.has(operatorStack.top()) && operatorPrecedence[token] <= operatorPrecedence[operatorStack.top()]) {
|
|
194
202
|
outputQueue.enqueue(operatorStack.pop());
|
|
195
203
|
}
|
|
196
204
|
operatorStack.push(token);
|
|
@@ -267,7 +275,7 @@ export class Parser {
|
|
|
267
275
|
const calcStack = new Stack();
|
|
268
276
|
while (!formulaRPN.isEmpty()) {
|
|
269
277
|
const frontItem = formulaRPN.dequeue();
|
|
270
|
-
if (!
|
|
278
|
+
if (!this.allowedOperators.has(frontItem)) {
|
|
271
279
|
const [sign, variableKey] = /^[+-]/.test(frontItem) ? [frontItem[0], frontItem.slice(1)] : ["", frontItem];
|
|
272
280
|
const operandValue = Number.parseFloat(this.variables.get(variableKey)?.toString() ?? variableKey);
|
|
273
281
|
const number = Number.parseFloat(sign + "1") * operandValue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fw-components/formula-editor",
|
|
3
|
-
"version": "2.0.7-formula-editor.
|
|
3
|
+
"version": "2.0.7-formula-editor.37",
|
|
4
4
|
"description": "A WYSIWYG type formula editor",
|
|
5
5
|
"main": "dist/formula-editor/src/formula-editor.js",
|
|
6
6
|
"exports": {
|
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
"@types/big.js": "^6.1.6",
|
|
32
32
|
"es-dev-server": "^2.1.0"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "a9c51fbd40797afbd0fc3b65ee780656edb1114f"
|
|
35
35
|
}
|