@fw-components/formula-editor 2.3.3 → 2.3.4-linting.0

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.
@@ -5,28 +5,39 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { html, LitElement } from "lit";
8
- import { customElement, property, state, query } from "lit/decorators.js";
9
- import "./suggestion-menu.js";
8
+ import { customElement, property, query, state } from "lit/decorators.js";
9
+ import { getFormulaTokens } from "./utils/get-formula-tokens.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";
13
12
  let FormulaEditor = class FormulaEditor extends LitElement {
14
- constructor() {
15
- super(...arguments);
16
- this.recommendations = [];
17
- this.currentCursorPosition = 0;
18
- this.isFocused = false;
19
- /**
20
- * Text area input value
21
- */
22
- this.formulaString = "";
23
- this.placeholder = "Type your formula...";
24
- this.recommendationLabels = new Map();
25
- this.variables = new Map();
26
- this.minSuggestionLen = 2;
27
- this.errorString = "";
28
- this.allowedNumbers = true;
29
- }
13
+ /**
14
+ * Formula Parser
15
+ */
16
+ _parser;
17
+ recommendations = [];
18
+ currentCursorPosition = 0;
19
+ /**
20
+ * user input event type
21
+ */
22
+ lastInputType = "";
23
+ _selectedRecommendation = "";
24
+ isFocused = false;
25
+ /**
26
+ * Text area input value
27
+ */
28
+ formulaString = "";
29
+ placeholder = "Type your formula...";
30
+ recommendationLabels = new Map();
31
+ label = "";
32
+ variables = new Map();
33
+ variableType = "";
34
+ minSuggestionLen = 2;
35
+ errorString = "";
36
+ formulaRegex = /.*/;
37
+ allowedNumbers = true;
38
+ allowedOperators = new Set();
39
+ editor;
40
+ suggestionMenu;
30
41
  updated(_changedProperties) {
31
42
  if (_changedProperties.has("formulaString")) {
32
43
  if (!this.formulaString?.trim()) {
@@ -66,7 +77,7 @@ let FormulaEditor = class FormulaEditor extends LitElement {
66
77
  this.currentCursorPosition = this.editor.selectionStart;
67
78
  const { recommendations, errorString, formattedString, newCursorPosition } = this._parser.parseInput(this.formulaString, this.currentCursorPosition, recommendation);
68
79
  this.recommendations = recommendations;
69
- this.errorString = errorString;
80
+ this.errorString = errorString || "";
70
81
  /**
71
82
  * Don't modify the text stream manually if the text is being composed,
72
83
  * unless the user manually chooses to do so by selecting a recommendation.
@@ -92,7 +103,7 @@ let FormulaEditor = class FormulaEditor extends LitElement {
92
103
  recommendations: this.recommendations,
93
104
  formulaTokens: getFormulaTokens(this.formulaString || "", this.formulaRegex)
94
105
  },
95
- bubbles: true,
106
+ bubbles: true
96
107
  }));
97
108
  }
98
109
  formatFormula() {
@@ -131,7 +142,9 @@ let FormulaEditor = class FormulaEditor extends LitElement {
131
142
  }
132
143
  render() {
133
144
  return html `
134
- <style>${FormulaEditorStyles}</style>
145
+ <style>
146
+ ${FormulaEditorStyles}
147
+ </style>
135
148
 
136
149
  ${this.label ? html `<label for="fw-formula-editor" class="editor-label">${this.label}</label>` : ""}
137
150
 
@@ -155,7 +168,7 @@ let FormulaEditor = class FormulaEditor extends LitElement {
155
168
  .onRecommendationClick=${this.onRecommendationClick.bind(this)}
156
169
  .recommendationLabels=${this.recommendationLabels}
157
170
  ></suggestion-menu>`
158
- : ''}
171
+ : ""}
159
172
  `;
160
173
  }
161
174
  };
@@ -181,31 +194,31 @@ __decorate([
181
194
  property()
182
195
  ], FormulaEditor.prototype, "placeholder", void 0);
183
196
  __decorate([
184
- property()
197
+ property({ type: Object })
185
198
  ], FormulaEditor.prototype, "recommendationLabels", void 0);
186
199
  __decorate([
187
- property()
200
+ property({ type: String })
188
201
  ], FormulaEditor.prototype, "label", void 0);
189
202
  __decorate([
190
- property()
203
+ property({ type: Object })
191
204
  ], FormulaEditor.prototype, "variables", void 0);
192
205
  __decorate([
193
- property()
206
+ property({ type: String })
194
207
  ], FormulaEditor.prototype, "variableType", void 0);
195
208
  __decorate([
196
- property()
209
+ property({ type: Number })
197
210
  ], FormulaEditor.prototype, "minSuggestionLen", void 0);
198
211
  __decorate([
199
- property()
212
+ property({ type: String })
200
213
  ], FormulaEditor.prototype, "errorString", void 0);
201
214
  __decorate([
202
- property()
215
+ property({ type: Object })
203
216
  ], FormulaEditor.prototype, "formulaRegex", void 0);
204
217
  __decorate([
205
- property()
218
+ property({ type: Boolean })
206
219
  ], FormulaEditor.prototype, "allowedNumbers", void 0);
207
220
  __decorate([
208
- property()
221
+ property({ type: Object })
209
222
  ], FormulaEditor.prototype, "allowedOperators", void 0);
210
223
  __decorate([
211
224
  query("#fw-formula-editor")
@@ -8,13 +8,11 @@ import { html, LitElement } from "lit";
8
8
  import { customElement, property, query, state } from "lit/decorators.js";
9
9
  import { SuggestionMenuStyles } from "./styles/suggestion-menu";
10
10
  let SuggestionMenu = class SuggestionMenu extends LitElement {
11
- constructor() {
12
- super(...arguments);
13
- this.recommendations = [];
14
- this.recommendationLabels = new Map();
15
- this.onRecommendationClick = () => { };
16
- this._currentFocusedIndex = -1;
17
- }
11
+ recommendations = [];
12
+ recommendationLabels = new Map();
13
+ onRecommendationClick = () => { };
14
+ _currentFocusedIndex = -1;
15
+ suggestionList;
18
16
  scrollToSelectedRecommendation(index) {
19
17
  const listItem = this.suggestionList?.querySelectorAll("li")[index];
20
18
  if (!listItem)
@@ -45,12 +43,13 @@ let SuggestionMenu = class SuggestionMenu extends LitElement {
45
43
  }
46
44
  render() {
47
45
  return html `
48
- <style>${SuggestionMenuStyles}</style>
46
+ <style>
47
+ ${SuggestionMenuStyles}
48
+ </style>
49
49
  <ul class="fw-formula-suggestion-menu" @mousedown=${(e) => e.preventDefault()}>
50
- ${this.recommendations.map((recommendation, index) => html `<li
51
- class="${this._currentFocusedIndex === index ? "selected" : ""}"
52
- @click=${(e) => this.handleRecommendationSelect(index)}
53
- >${this.recommendationLabels.get(recommendation) ?? recommendation}</li>`)}
50
+ ${this.recommendations.map((recommendation, index) => html `<li class="${this._currentFocusedIndex === index ? "selected" : ""}" @click=${(e) => this.handleRecommendationSelect(index)}>
51
+ ${this.recommendationLabels.get(recommendation) ?? recommendation}
52
+ </li>`)}
54
53
  </ul>
55
54
  `;
56
55
  }
@@ -6,9 +6,12 @@ export var Expectation;
6
6
  })(Expectation || (Expectation = {}));
7
7
  export class Formula {
8
8
  constructor(name, formulaString, precision = -1) {
9
- this.error = null;
10
9
  this.name = name || "";
11
10
  this.formulaString = formulaString || "";
12
11
  this.precision = precision;
13
12
  }
13
+ name;
14
+ formulaString;
15
+ precision;
16
+ error = null;
14
17
  }
@@ -1,11 +1,17 @@
1
- import Big from "big.js";
1
+ import { mathematicalOperators, operatorPrecedence, unaryOperators } from "./constants.js";
2
+ import { getFormulaTokens } from "./get-formula-tokens.js";
3
+ import { Queue } from "./queue.js";
2
4
  import { Recommender } from "./recommendor.js";
3
5
  import { Stack } from "./stack.js";
4
- import { Queue } from "./queue.js";
6
+ import Big from "big.js";
5
7
  import { Expectation } from "../types";
6
- import { operatorPrecedence, unaryOperators, mathematicalOperators } from "./constants.js";
7
- import { getFormulaTokens } from "./get-formula-tokens.js";
8
8
  export class Parser {
9
+ _recommender;
10
+ variables;
11
+ formulaRegex;
12
+ allowedNumbers;
13
+ allowedOperators;
14
+ variableType;
9
15
  constructor(variables, minSuggestionLen, formulaRegex = /[A-Za-z0-9_#@.]+|[-+(),*^/\s]/g, allowedNumbers = true, allowedOperators = mathematicalOperators, variableType = "") {
10
16
  this.variables = variables;
11
17
  this.formulaRegex = formulaRegex;
@@ -20,7 +26,7 @@ export class Parser {
20
26
  return !Number.isNaN(Number(value));
21
27
  }
22
28
  formatFormulaToken(token) {
23
- for (let existingKey of this.variables.keys()) {
29
+ for (const existingKey of this.variables.keys()) {
24
30
  if (existingKey.toLowerCase() === token.toLowerCase()) {
25
31
  return existingKey;
26
32
  }
@@ -38,7 +44,7 @@ export class Parser {
38
44
  recommendations: [],
39
45
  formattedString: "",
40
46
  newCursorPosition: prevCurPos ?? -1,
41
- errorString: null,
47
+ errorString: null
42
48
  };
43
49
  if (!formula.trim() && recommendation) {
44
50
  parseOutput.formattedString = recommendation;
@@ -73,7 +79,6 @@ export class Parser {
73
79
  recommendation = null;
74
80
  return;
75
81
  }
76
- ;
77
82
  const updatedTokenLength = recommendation.length - token.length;
78
83
  parseOutput.newCursorPosition = Math.min(parseOutput.newCursorPosition, formula.length) + updatedTokenLength;
79
84
  token = recommendation;
@@ -84,7 +89,7 @@ export class Parser {
84
89
  /**
85
90
  * Error checks
86
91
  * skip error check if there is one already
87
- */
92
+ */
88
93
  if (expectation != Expectation.UNDEFINED) {
89
94
  /**
90
95
  * Unknown symbol/variable/word
@@ -101,33 +106,37 @@ export class Parser {
101
106
  parseOutput.errorString = "Unexpected closing bracket. Make sure all opening brackets '(' have matching closing brackets ')'.";
102
107
  expectation = Expectation.UNDEFINED;
103
108
  }
109
+ else if (
104
110
  /**
105
111
  * Operator or ')' after an operator (Eg: '23 / *' or '23 / )')
106
112
  * No error for Unary `+` and `-` as they might represent a positive or negative number respectively
107
113
  */
108
- else if (expectation === Expectation.VARIABLE && !isNumber && !isSpace && token != "("
109
- && !((unaryOperators.includes(token)) && (!parsedString.trim() || previousToken === "(" || this.allowedOperators.has(previousToken)))) {
110
- parseOutput.errorString = `Please use ${this.variableType} ${this.variableType && this.allowedNumbers ? " or " : ''} ${this.allowedNumbers ? "numbers" : ""} after '${previousToken}'.`;
114
+ expectation === Expectation.VARIABLE &&
115
+ !isNumber &&
116
+ !isSpace &&
117
+ token != "(" &&
118
+ !(unaryOperators.includes(token) && (!parsedString.trim() || previousToken === "(" || this.allowedOperators.has(previousToken)))) {
119
+ parseOutput.errorString = `Please use ${this.variableType} ${this.variableType && this.allowedNumbers ? " or " : ""} ${this.allowedNumbers ? "numbers" : ""} after '${previousToken}'.`;
111
120
  expectation = Expectation.UNDEFINED;
112
121
  }
113
- /**
114
- * Multiple number/variable together without operator
115
- */
116
122
  else if (expectation === Expectation.OPERATOR && !isOperator && !isSpace && token != ")") {
123
+ /**
124
+ * Multiple number/variable together without operator
125
+ */
117
126
  parseOutput.errorString = `Please use mathematical operators (${Array.from(this.allowedOperators).join(" ")}) after '${previousToken}'.`;
118
127
  expectation = Expectation.UNDEFINED;
119
128
  }
120
- /**
121
- * division by zero
122
- */
123
129
  else if (isNumber && previousToken === "/" && (this.variables.get(token) === 0 || Number(token) === 0)) {
130
+ /**
131
+ * division by zero
132
+ */
124
133
  parseOutput.errorString = `Division by zero is not possible.`;
125
134
  expectation = Expectation.UNDEFINED;
126
135
  }
127
- /**
128
- * Empty brackets
129
- */
130
136
  else if (previousToken === "(" && token === ")") {
137
+ /**
138
+ * Empty brackets
139
+ */
131
140
  parseOutput.errorString = `Please don't use empty brackets ().`;
132
141
  expectation = Expectation.UNDEFINED;
133
142
  }
@@ -178,7 +187,7 @@ export class Parser {
178
187
  let currentTokens = "";
179
188
  // Check if variables include unary operators `-` and `+`.
180
189
  for (const token of tokens) {
181
- if ((unaryOperators.includes(token)) && (!currentTokens.trim() || previousToken === "(" || this.allowedOperators.has(previousToken))) {
190
+ if (unaryOperators.includes(token) && (!currentTokens.trim() || previousToken === "(" || this.allowedOperators.has(previousToken))) {
182
191
  carriedToken = token;
183
192
  }
184
193
  else if (carriedToken) {
@@ -235,7 +244,7 @@ export class Parser {
235
244
  let parsedLeftExpression;
236
245
  let parsedRightExpression;
237
246
  // check if the symbol is a number or variable or unaryOperatorPreceded Variable
238
- if (((unaryOperators.includes(symbol[0])) && this.variables.has(symbol.substring(1))) ||
247
+ if ((unaryOperators.includes(symbol[0]) && this.variables.has(symbol.substring(1))) ||
239
248
  this.variables.has(symbol) ||
240
249
  (!Number.isNaN(parseFloat(symbol)) && Number.isFinite(parseFloat(symbol)))) {
241
250
  resultStack.push(symbol);
@@ -243,21 +252,14 @@ export class Parser {
243
252
  }
244
253
  // If symbol is an operator, check operatorStack, adds brackets accordingly to the result and add it to operatorStack
245
254
  else if (Object.keys(operatorPrecedence).includes(symbol)) {
246
- const [rightExpression, leftExpression, operatorA, operatorB] = [
247
- resultStack.pop(),
248
- resultStack.pop(),
249
- operatorStack.pop(),
250
- operatorStack.pop(),
251
- ];
252
- if ((operatorPrecedence[operatorB] <= operatorPrecedence[symbol]) ||
253
- (operatorPrecedence[operatorB] === operatorPrecedence[symbol] && ["/", "-"].includes(symbol))) {
255
+ const [rightExpression, leftExpression, operatorA, operatorB] = [resultStack.pop(), resultStack.pop(), operatorStack.pop(), operatorStack.pop()];
256
+ if (operatorPrecedence[operatorB] <= operatorPrecedence[symbol] || (operatorPrecedence[operatorB] === operatorPrecedence[symbol] && ["/", "-"].includes(symbol))) {
254
257
  parsedLeftExpression = `(${leftExpression})`;
255
258
  }
256
259
  else {
257
260
  parsedLeftExpression = leftExpression;
258
261
  }
259
- if (operatorPrecedence[operatorA] <= operatorPrecedence[symbol] ||
260
- (operatorPrecedence[operatorA] === operatorPrecedence[symbol] && ["/", "-"].includes(symbol))) {
262
+ if (operatorPrecedence[operatorA] <= operatorPrecedence[symbol] || (operatorPrecedence[operatorA] === operatorPrecedence[symbol] && ["/", "-"].includes(symbol))) {
261
263
  parsedRightExpression = `(${rightExpression})`;
262
264
  }
263
265
  else {
@@ -277,7 +279,7 @@ export class Parser {
277
279
  const formulaRPN = this.buildRPN(formula);
278
280
  const calculationResult = {
279
281
  result: undefined,
280
- errorString: null,
282
+ errorString: null
281
283
  };
282
284
  if (!formulaRPN)
283
285
  return calculationResult;
@@ -287,7 +289,7 @@ export class Parser {
287
289
  if (!this.allowedOperators.has(frontItem)) {
288
290
  const [sign, variableKey] = /^[+-]/.test(frontItem) ? [frontItem[0], frontItem.slice(1)] : ["", frontItem];
289
291
  const operandValue = Number.parseFloat(this.variables.get(variableKey)?.toString() ?? variableKey);
290
- const number = Number.parseFloat(sign + "1") * operandValue;
292
+ const number = Number.parseFloat(`${sign}1`) * operandValue;
291
293
  calcStack.push(Big(number));
292
294
  }
293
295
  else {
@@ -321,7 +323,7 @@ export class Parser {
321
323
  }
322
324
  }
323
325
  catch (error) {
324
- calculationResult.errorString = error;
326
+ calculationResult.errorString = error instanceof Error ? error.message : "Unknown error during calculation";
325
327
  return calculationResult;
326
328
  }
327
329
  }
@@ -330,7 +332,8 @@ export class Parser {
330
332
  calculationResult.errorString = "Calculation error: Empty result stack";
331
333
  return calculationResult;
332
334
  }
333
- calculationResult.result = parseFloat(calcStack.top().toString());
335
+ const topValue = calcStack.top();
336
+ calculationResult.result = topValue !== undefined ? parseFloat(topValue.toString()) : undefined;
334
337
  return calculationResult;
335
338
  }
336
339
  }
@@ -1,9 +1,7 @@
1
1
  export class Queue {
2
- constructor() {
3
- this._elements = {};
4
- this._head = 0;
5
- this._tail = 0;
6
- }
2
+ _elements = {};
3
+ _head = 0;
4
+ _tail = 0;
7
5
  enqueue(item) {
8
6
  this._elements[this._tail] = item;
9
7
  this._tail++;
@@ -1,5 +1,7 @@
1
1
  import { matchSorter } from "match-sorter";
2
2
  export class Recommender {
3
+ _wordLimitForSuggestions;
4
+ variableList;
3
5
  constructor(variables, minSuggestionLen) {
4
6
  this._wordLimitForSuggestions = minSuggestionLen > 0 ? minSuggestionLen : 1;
5
7
  this.variableList = variables;
@@ -1,7 +1,5 @@
1
1
  export class Stack {
2
- constructor() {
3
- this._elements = [];
4
- }
2
+ _elements = [];
5
3
  push(item) {
6
4
  this._elements.push(item);
7
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fw-components/formula-editor",
3
- "version": "2.3.3",
3
+ "version": "2.3.4-linting.0",
4
4
  "description": "A WYSIWYG type formula editor",
5
5
  "main": "dist/formula-editor/src/formula-editor.js",
6
6
  "exports": {
@@ -13,23 +13,21 @@
13
13
  "access": "public"
14
14
  },
15
15
  "scripts": {
16
- "start": "es-dev-server --app-index ../../index.html --node-resolve --watch --open",
17
16
  "build": "tsc --composite false",
18
17
  "dev": "tsc -w --composite false",
19
18
  "prepublishOnly": "npm run build"
20
19
  },
21
20
  "dependencies": {
22
- "@fw-components/styles": "^2.3.3",
23
- "big.js": "^6.2.1",
24
- "lit": "^2.1.2",
25
- "match-sorter": "^8.0.0",
26
- "typescript": "^5.0.4"
21
+ "@fw-components/styles": "^2.3.4-linting.0",
22
+ "big.js": "^7.0.1",
23
+ "lit": "^3.3.1",
24
+ "match-sorter": "^8.1.0",
25
+ "typescript": "^5.9.3"
27
26
  },
28
27
  "author": "The Fundwave Authors",
29
28
  "license": "ISC",
30
29
  "devDependencies": {
31
- "@types/big.js": "^6.1.6",
32
- "es-dev-server": "^2.1.0"
30
+ "@types/big.js": "^6.2.2"
33
31
  },
34
- "gitHead": "bbfeadceb3d36d13e2bce55af4a4fa69e664c553"
32
+ "gitHead": "d3ffaee5f92e119b0a93114e1753021065271a67"
35
33
  }
package/tsconfig.json CHANGED
@@ -1,20 +1,9 @@
1
1
  {
2
- "compilerOptions": {
3
- "experimentalDecorators": true,
4
- "useDefineForClassFields":false,
5
- "outDir": "./dist",
6
- "rootDir": "../",
7
- "target": "esnext",
8
- "module": "esnext",
9
- "moduleResolution": "node",
10
- "declaration": true,
11
- "declarationDir": "./dist/types",
12
- "composite": false,
13
- "lib": ["esnext", "dom"],
14
- "esModuleInterop": true,
15
- "skipLibCheck": true,
16
- "noEmitOnError": false
17
- },
18
- "include": ["src/**/*.ts"],
19
- "exclude": ["node_modules", "dist"]
20
- }
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "./dist",
6
+ "allowJs": true
7
+ },
8
+ "include": ["src"]
9
+ }