@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
- firstUpdated(_changedProperties) {
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);
@@ -1,5 +1,7 @@
1
1
  export function getFormulaTokens(formulaString, formulaRegex) {
2
2
  if (!formulaString?.length)
3
3
  return [];
4
+ if (!Boolean(formulaRegex))
5
+ return formulaString.split(/(\s+)/) || [];
4
6
  return formulaString.match(formulaRegex) || [];
5
7
  }
@@ -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 { mathematicalOperators, operatorPrecedence, unaryOperators } from "./constants.js";
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 = token.trim() !== "" && (this.variables.has(token) || !Number.isNaN(Number(token)));
34
- const isOperator = mathematicalOperators.has(token);
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 (mathematicalOperators.has(token)) {
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
- if (mathematicalOperators.has(previousToken) && isOperator) {
73
- parseOutput.errorString = `Multiple operators at position ${currentPosition}`;
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 = `Unexpected ')' at position ${currentPosition}`;
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 === "(" || mathematicalOperators.has(previousToken)))) {
86
- parseOutput.errorString = `Expected variable/number at position ${currentPosition}`;
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 = `Expected mathematical operator at position ${currentPosition}`;
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 at position ${currentPosition}`;
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 = `Empty brackets at position ${currentPosition}`;
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 (mathematicalOperators.has(previousToken) || !previousToken.trim().length) {
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 (mathematicalOperators.has(previousToken)) {
147
- parseOutput.errorString = `Unexpected ending with mathematical operator at position: ${currentPosition}`;
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 = `Unclosed '(' at position: ${parentheses.top()}`;
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 === "(" || mathematicalOperators.has(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 (mathematicalOperators.has(token)) {
193
- while (mathematicalOperators.has(operatorStack.top()) && operatorPrecedence[token] <= operatorPrecedence[operatorStack.top()]) {
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 (!mathematicalOperators.has(frontItem)) {
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.35",
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": "ef64463d26814b5eb6389aa3a1f9ce12f26c46a6"
34
+ "gitHead": "a9c51fbd40797afbd0fc3b65ee780656edb1114f"
35
35
  }