@fw-components/formula-editor 2.0.7-formula-editor.34 → 2.0.7-formula-editor.36

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.
@@ -9,6 +9,7 @@ import { customElement, property, state, query } from "lit/decorators.js";
9
9
  import "./suggestion-menu.js";
10
10
  import { Parser } from "./utils/parser.js";
11
11
  import { FormulaEditorStyles } from "./styles/editor.js";
12
+ import { getFormulaTokens } from "./utils/get-formula-tokens.js";
12
13
  let FormulaEditor = class FormulaEditor extends LitElement {
13
14
  constructor() {
14
15
  super(...arguments);
@@ -20,12 +21,13 @@ let FormulaEditor = class FormulaEditor extends LitElement {
20
21
  */
21
22
  this.content = "";
22
23
  this.placeholder = "Type your formula...";
24
+ this.recommendationLabels = new Map();
23
25
  this.variables = new Map();
24
26
  this.minSuggestionLen = 2;
25
27
  this.errorString = "";
26
- }
27
- firstUpdated(_changedProperties) {
28
- this.editor.focus();
28
+ this.formulaRegex = /'[^']*'|[A-Za-z0-9_#@]+|[-+(),*^/\s]/g;
29
+ this.allowedNumbers = true;
30
+ this.allowedOperators = new Set(["^", "+", "-", "*", "/"]);
29
31
  }
30
32
  updated(_changedProperties) {
31
33
  if (_changedProperties.has("content")) {
@@ -35,7 +37,7 @@ let FormulaEditor = class FormulaEditor extends LitElement {
35
37
  this._adjustTextAreaHeight();
36
38
  }
37
39
  if (_changedProperties.has("variables")) {
38
- this._parser = new Parser(this.variables, this.minSuggestionLen);
40
+ this._parser = new Parser(this.variables, this.formulaRegex, this.allowedNumbers, this.allowedOperators, this.minSuggestionLen);
39
41
  this.recommendations = Array.from(this.variables.keys());
40
42
  }
41
43
  }
@@ -87,6 +89,7 @@ let FormulaEditor = class FormulaEditor extends LitElement {
87
89
  formulaString: this.content,
88
90
  error: this.errorString,
89
91
  recommendations: this.recommendations,
92
+ formulaTokens: getFormulaTokens(this.content || "", this.formulaRegex)
90
93
  },
91
94
  bubbles: true,
92
95
  }));
@@ -148,6 +151,7 @@ let FormulaEditor = class FormulaEditor extends LitElement {
148
151
  .recommendations=${this.recommendations}
149
152
  .currentSelection=${this._selectedRecommendation}
150
153
  .onRecommendationClick=${this.onRecommendationClick.bind(this)}
154
+ .recommendationLabels=${this.recommendationLabels}
151
155
  ></suggestion-menu>`
152
156
  : ''}
153
157
  `;
@@ -174,6 +178,9 @@ __decorate([
174
178
  __decorate([
175
179
  property()
176
180
  ], FormulaEditor.prototype, "placeholder", void 0);
181
+ __decorate([
182
+ property()
183
+ ], FormulaEditor.prototype, "recommendationLabels", void 0);
177
184
  __decorate([
178
185
  property()
179
186
  ], FormulaEditor.prototype, "label", void 0);
@@ -186,6 +193,15 @@ __decorate([
186
193
  __decorate([
187
194
  property()
188
195
  ], FormulaEditor.prototype, "errorString", void 0);
196
+ __decorate([
197
+ property()
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);
189
205
  __decorate([
190
206
  query("#wysiwyg-editor")
191
207
  ], 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);
@@ -0,0 +1,7 @@
1
+ export function getFormulaTokens(formulaString, formulaRegex) {
2
+ if (!formulaString?.length)
3
+ return [];
4
+ if (!Boolean(formulaRegex))
5
+ return formulaString.split(/(\s+)/) || [];
6
+ return formulaString.match(formulaRegex) || [];
7
+ }
@@ -3,19 +3,23 @@ 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
+ import { getFormulaTokens } from "./get-formula-tokens.js";
7
8
  export class Parser {
8
- constructor(variables, minSuggestionLen) {
9
+ constructor(variables, formulaRegex, allowedNumbers, allowedOperators, minSuggestionLen) {
9
10
  this.variables = variables;
11
+ this.formulaRegex = formulaRegex;
10
12
  this._recommender = new Recommender(Array.from(this.variables.keys()), minSuggestionLen);
13
+ this.allowedNumbers = allowedNumbers;
14
+ this.allowedOperators = allowedOperators;
11
15
  }
12
- getFormulaTokens(formulaString) {
13
- if (!formulaString?.length)
14
- return [];
15
- return formulaString.match(/'[^']*'|[A-Za-z0-9_#@]+|[-+(),*^/\s]/g);
16
+ isNumber(value) {
17
+ if (!this.allowedNumbers || value.trim() === "")
18
+ return false;
19
+ return !Number.isNaN(Number(value));
16
20
  }
17
21
  parseInput(formula, prevCurPos = null, recommendation = null) {
18
- const tokens = this.getFormulaTokens(formula);
22
+ const tokens = getFormulaTokens(formula, this.formulaRegex);
19
23
  const parentheses = new Stack();
20
24
  let expectation = Expectation.VARIABLE;
21
25
  let currentPosition = 0;
@@ -33,8 +37,8 @@ export class Parser {
33
37
  return parseOutput;
34
38
  }
35
39
  tokens?.forEach((token) => {
36
- let isNumber = token.trim() !== "" && (this.variables.has(token) || !Number.isNaN(Number(token)));
37
- const isOperator = mathematicalOperators.has(token);
40
+ let isNumber = this.variables.has(token) || this.isNumber(token);
41
+ const isOperator = this.allowedOperators.has(token);
38
42
  const isSpace = token.trim() === "";
39
43
  const isBracket = token === "(" || token === ")";
40
44
  if (isSpace) {
@@ -51,7 +55,7 @@ export class Parser {
51
55
  if (currentPosition <= prevCurPos && currentPosition + token.length >= prevCurPos) {
52
56
  if (recommendation) {
53
57
  isNumber = true;
54
- if (mathematicalOperators.has(token)) {
58
+ if (this.allowedOperators.has(token)) {
55
59
  const updatedTokenString = `${token} ${recommendation}`;
56
60
  parseOutput.formattedString += updatedTokenString;
57
61
  currentPosition += updatedTokenString.length;
@@ -72,7 +76,7 @@ export class Parser {
72
76
  * skip error check if there is one already
73
77
  */
74
78
  if (expectation != Expectation.UNDEFINED) {
75
- if (mathematicalOperators.has(previousToken) && isOperator) {
79
+ if (this.allowedOperators.has(previousToken) && isOperator) {
76
80
  parseOutput.errorString = `Multiple operators at position ${currentPosition}`;
77
81
  expectation = Expectation.UNDEFINED;
78
82
  }
@@ -85,7 +89,7 @@ export class Parser {
85
89
  * No error for Unary `+` and `-` as they might represent a positive or negative number respectively
86
90
  */
87
91
  else if (expectation === Expectation.VARIABLE && !isNumber && !isSpace && token != "("
88
- && !((unaryOperators.includes(token)) && (!parsedString.trim() || previousToken === "(" || mathematicalOperators.has(previousToken)))) {
92
+ && !((unaryOperators.includes(token)) && (!parsedString.trim() || previousToken === "(" || this.allowedOperators.has(previousToken)))) {
89
93
  parseOutput.errorString = `Expected variable/number at position ${currentPosition}`;
90
94
  expectation = Expectation.UNDEFINED;
91
95
  }
@@ -143,10 +147,10 @@ export class Parser {
143
147
  parseOutput.formattedString += recommendation;
144
148
  previousToken = recommendation;
145
149
  }
146
- if (mathematicalOperators.has(previousToken) || !previousToken.trim().length) {
150
+ if (this.allowedOperators.has(previousToken) || !previousToken.trim().length) {
147
151
  parseOutput.recommendations = !parseOutput.errorString?.length ? Array.from(this.variables.keys()) : [];
148
152
  }
149
- if (mathematicalOperators.has(previousToken)) {
153
+ if (this.allowedOperators.has(previousToken)) {
150
154
  parseOutput.errorString = `Unexpected ending with mathematical operator at position: ${currentPosition}`;
151
155
  }
152
156
  if (!parentheses.isEmpty()) {
@@ -157,14 +161,14 @@ export class Parser {
157
161
  buildRPN(formula) {
158
162
  if (this.parseInput(formula).errorString)
159
163
  return null;
160
- const tokens = this.getFormulaTokens(formula)?.filter((el) => !/\s+/.test(el) && el !== "");
164
+ const tokens = getFormulaTokens(formula, this.formulaRegex)?.filter((el) => !/\s+/.test(el) && el !== "");
161
165
  let previousToken = "";
162
166
  let carriedToken = null;
163
167
  const parsedTokens = [];
164
168
  let currentTokens = "";
165
169
  // Check if variables include unary operators `-` and `+`.
166
170
  for (const token of tokens) {
167
- if ((unaryOperators.includes(token)) && (!currentTokens.trim() || previousToken === "(" || mathematicalOperators.has(previousToken))) {
171
+ if ((unaryOperators.includes(token)) && (!currentTokens.trim() || previousToken === "(" || this.allowedOperators.has(previousToken))) {
168
172
  carriedToken = token;
169
173
  }
170
174
  else if (carriedToken) {
@@ -192,8 +196,8 @@ export class Parser {
192
196
  }
193
197
  operatorStack.pop();
194
198
  }
195
- else if (mathematicalOperators.has(token)) {
196
- while (mathematicalOperators.has(operatorStack.top()) && operatorPrecedence[token] <= operatorPrecedence[operatorStack.top()]) {
199
+ else if (this.allowedOperators.has(token)) {
200
+ while (this.allowedOperators.has(operatorStack.top()) && operatorPrecedence[token] <= operatorPrecedence[operatorStack.top()]) {
197
201
  outputQueue.enqueue(operatorStack.pop());
198
202
  }
199
203
  operatorStack.push(token);
@@ -270,7 +274,7 @@ export class Parser {
270
274
  const calcStack = new Stack();
271
275
  while (!formulaRPN.isEmpty()) {
272
276
  const frontItem = formulaRPN.dequeue();
273
- if (!mathematicalOperators.has(frontItem)) {
277
+ if (!this.allowedOperators.has(frontItem)) {
274
278
  const [sign, variableKey] = /^[+-]/.test(frontItem) ? [frontItem[0], frontItem.slice(1)] : ["", frontItem];
275
279
  const operandValue = Number.parseFloat(this.variables.get(variableKey)?.toString() ?? variableKey);
276
280
  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.34",
3
+ "version": "2.0.7-formula-editor.36",
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": "bdf6225910be4141015192c528d44ea62ca2eecf"
34
+ "gitHead": "78fd3b78f8f72ac4ab0b4b7ec57848b2a78cccad"
35
35
  }