@teselagen/ui 0.3.76 → 0.3.77

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teselagen/ui",
3
- "version": "0.3.76",
3
+ "version": "0.3.77",
4
4
  "main": "./src/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -21,7 +21,6 @@
21
21
  "buffer": "5.7.1",
22
22
  "color": "^3.2.1",
23
23
  "copy-to-clipboard": "^3.3.1",
24
- "dayjs": "^1.10.4",
25
24
  "dom-scroll-into-view": "^2.0.1",
26
25
  "downloadjs": "^1.4.7",
27
26
  "fuse.js": "^6.6.2",
@@ -30,19 +29,15 @@
30
29
  "is-mobile": "^3.0.0",
31
30
  "jszip": "^3.10.1",
32
31
  "math-expression-evaluator": "^1.3.7",
33
- "mobx": "^6.10.2",
34
- "mobx-react": "^9.0.1",
35
32
  "mock-fs": "5.2.0",
36
33
  "nanoid": "^4.0.0",
37
34
  "papaparse": "^5.3.2",
38
35
  "prop-types": "^15.6.2",
39
- "qs": "^6.9.6",
40
36
  "react": "18.2.0",
41
37
  "react-color": "^2.19.3",
42
38
  "react-dom": "18.2.0",
43
39
  "react-dropzone": "^11.4.2",
44
40
  "react-markdown": "8.0.7",
45
- "react-redux": "^8.0.5",
46
41
  "react-rnd": "^10.2.4",
47
42
  "react-router-dom": "^4.3.1",
48
43
  "react-sortable-hoc": "^0.6.8",
@@ -54,8 +49,7 @@
54
49
  "remark-gfm": "^3.0.1",
55
50
  "tippy.js": "^6.3.7",
56
51
  "url-join": "^4.0.1",
57
- "use-deep-compare-effect": "^1.6.1",
58
- "write-excel-file": "^1.4.25"
52
+ "use-deep-compare-effect": "^1.6.1"
59
53
  },
60
54
  "license": "MIT"
61
55
  }
@@ -15,6 +15,7 @@ import { viewColumn, openColumn } from "../DataTable/viewColumn";
15
15
  import pureNoFunc from "../utils/pureNoFunc";
16
16
  import tgFormValues from "../utils/tgFormValues";
17
17
  import getTableConfigFromStorage from "./utils/getTableConfigFromStorage";
18
+ import computePresets from "./utils/computePresets";
18
19
 
19
20
  export default compose(
20
21
  // maybe we need this in some cases?
@@ -40,6 +41,7 @@ export default compose(
40
41
  ...ownProps.tableParams
41
42
  };
42
43
  }
44
+ propsToUse = computePresets(propsToUse);
43
45
 
44
46
  const {
45
47
  schema,
@@ -126,8 +128,8 @@ export default compose(
126
128
  return field.render
127
129
  ? !field.render(val, e)
128
130
  : cellRenderer[field.path]
129
- ? !cellRenderer[field.path](val, e)
130
- : !val;
131
+ ? !cellRenderer[field.path](val, e)
132
+ : !val;
131
133
  });
132
134
  }
133
135
  if (noValsForField) {
@@ -1,25 +1,166 @@
1
- import { set } from "lodash";
1
+ import { isString, set, toNumber } from "lodash";
2
2
  import { defaultValidators } from "./defaultValidators";
3
3
  import { defaultFormatters } from "./defaultFormatters";
4
+ import { evaluate } from "mathjs";
5
+ import getIdOrCodeOrIndex from "./utils/getIdOrCodeOrIndex";
4
6
 
5
7
  //(mutative) responsible for formatting and then validating the
8
+ // const depGraph = {
9
+ // a1: ["a3"],
10
+ // a2: ["a1"],
11
+ // a3: [],
12
+ // b1: ["a1", "a2"],
13
+ // b2: ["a2"],
14
+ // b3: ["a3"]
15
+ // };
6
16
 
7
17
  export const editCellHelper = ({
18
+ _cellAlphaNum,
19
+ updateGroup,
20
+ depGraph,
8
21
  entity,
9
22
  path,
10
23
  schema,
11
24
  columnSchema,
12
- newVal
25
+ newVal,
26
+ entities,
27
+ nestLevel = 0,
28
+ depLevel = 0,
29
+ allowFormulas
13
30
  }) => {
14
31
  let nv = newVal;
32
+ if (nv?.formula) {
33
+ nv = nv.formula;
34
+ }
15
35
 
16
36
  const colSchema =
17
37
  columnSchema || schema?.fields?.find(({ path: p }) => p === path) || {};
18
38
  path = path || colSchema.path;
39
+ const cellAlphaNum =
40
+ _cellAlphaNum ||
41
+ getCellAlphaNum({
42
+ entities,
43
+ entity,
44
+ colSchema,
45
+ schema
46
+ });
47
+ if (
48
+ updateGroup[cellAlphaNum] !== undefined &&
49
+ updateGroup[cellAlphaNum] !== "__Currently&&Updating__"
50
+ ) {
51
+ // if the cell is already being updated, return the value
52
+ return { value: updateGroup[cellAlphaNum], errors: {} };
53
+ } else {
54
+ updateGroup[cellAlphaNum] = "__Currently&&Updating__";
55
+ }
56
+ const cellId = `${getIdOrCodeOrIndex(entity)}:${colSchema.path}`;
57
+ const oldVal = entity[path];
19
58
  const { format, validate, type } = colSchema;
20
- let error;
21
- if (nv === undefined && colSchema.defaultValue !== undefined)
59
+ let errors = {
60
+ __hasErrors: false
61
+ };
62
+ if (nv === undefined && colSchema.defaultValue !== undefined) {
22
63
  nv = colSchema.defaultValue;
64
+ }
65
+
66
+ let hasFormula = false;
67
+ if (colSchema.allowFormulas && typeof nv === "string" && nv[0] === "=") {
68
+ const ogFormula = nv;
69
+ // if the nv is missing a closing paren, add it
70
+ // count the number of open parens
71
+ // count the number of close parens
72
+ // if the number of open parens is greater than the number of close parens, add a close paren
73
+ const openParens = (nv.match(/\(/g) || []).length;
74
+ const closeParens = (nv.match(/\)/g) || []).length;
75
+ if (openParens > closeParens) {
76
+ nv = nv + ")";
77
+ }
78
+ // if the nv is a valid formula, evaluate it and evaluate any nested formulas
79
+ // if the nv is not a valid formula, return the error
80
+ // fill in any variables with their values
81
+ let error;
82
+ // if nv contains : then it is a range
83
+ // if (nv.includes(":")) {
84
+ // replace the range with the the values of the range
85
+ // get the start and end of the range
86
+ const { rangeErr, replacedFormula } = replaceFormulaRanges({
87
+ formula: nv,
88
+ schema,
89
+ entities
90
+ });
91
+ error = rangeErr;
92
+ nv = replacedFormula;
93
+ nv = nv.replace(/([A-Z]+[0-9]+)/gi, _match => {
94
+ const match = _match.toUpperCase();
95
+ if (updateGroup[match] === "__Currently&&Updating__") {
96
+ error = `Circular Loop Detected between ${cellAlphaNum} and ${match}`;
97
+ return "circular_loop_detected";
98
+ }
99
+ // match will equal E12 or B4 for example
100
+ const [letter, rowIndex] = match.split(/(\d+)/);
101
+ const entity = entities.find((e, i) => {
102
+ return i === rowIndex - 1;
103
+ });
104
+ const columns = schema.fields;
105
+ const letterIndex = lettersToNumber(letter);
106
+ const col = columns[letterIndex];
107
+ if (!col) {
108
+ return match;
109
+ }
110
+ const { path } = col;
111
+ if (!entity) return match;
112
+ let val = entity[path];
113
+ if (val === undefined || val === "") return 0;
114
+ if (val?.formula) {
115
+ val = val.formula;
116
+ }
117
+ if (isString(val) && val[0] === "=") {
118
+ // if the value is a formula, evaluate it
119
+ const { value, errors: _errors } = editCellHelper({
120
+ _cellAlphaNum: match,
121
+ updateGroup,
122
+ depGraph,
123
+ entity,
124
+ path,
125
+ schema,
126
+ columnSchema: col,
127
+ newVal: val,
128
+ entities,
129
+ nestLevel: nestLevel + 1
130
+ });
131
+ errors = mergeErrors(errors, _errors);
132
+ val = value?.formula ? value.value : value;
133
+ } else if (!isNaN(toNumber(val))) {
134
+ return val;
135
+ } else if (isString(val)) {
136
+ return 0;
137
+ }
138
+ return val;
139
+ });
140
+
141
+ const toEval = nv.slice(1);
142
+ try {
143
+ if (!error) {
144
+ nv = evaluate(toEval);
145
+ nv = {
146
+ formula: ogFormula,
147
+ value: `${nv}`
148
+ };
149
+ } else {
150
+ throw new Error(error);
151
+ }
152
+ } catch (e) {
153
+ nv = {
154
+ formula: ogFormula,
155
+ value: `#ERROR`,
156
+ error: e.message
157
+ };
158
+ errors[cellId] = e.message;
159
+ errors.__hasErrors = true;
160
+ }
161
+ hasFormula = nv;
162
+ nv = nv.value;
163
+ }
23
164
 
24
165
  if (format) {
25
166
  nv = format(nv, colSchema);
@@ -27,18 +168,173 @@ export const editCellHelper = ({
27
168
  if (defaultFormatters[type]) {
28
169
  nv = defaultFormatters[type](nv, colSchema);
29
170
  }
30
- if (validate) {
31
- error = validate(nv, colSchema, entity);
171
+ if (validate && !hasErrors(errors)) {
172
+ const error = validate(nv, colSchema, entity);
173
+ if (error) {
174
+ errors.__hasErrors = true;
175
+ }
176
+ errors[cellId] = error;
32
177
  }
33
- if (!error) {
178
+ if (!hasErrors(errors)) {
34
179
  const validator =
35
180
  defaultValidators[type] ||
36
181
  type === "string" ||
37
182
  (type === undefined && defaultValidators.string);
38
183
  if (validator) {
39
- error = validator(nv, colSchema);
184
+ const error = validator(nv, colSchema);
185
+ if (error) {
186
+ errors.__hasErrors = true;
187
+ }
188
+ errors[cellId] = error;
40
189
  }
41
190
  }
42
- set(entity, path, nv);
43
- return { entity, error };
191
+ const value = hasFormula || nv;
192
+ if (
193
+ // if the formula and its value is the same as it was, don't set it again otherwise it will be added to the undo/redo stack
194
+ !(
195
+ hasFormula &&
196
+ value.value === oldVal?.value &&
197
+ value.formula === oldVal?.formula
198
+ )
199
+ ) {
200
+ set(entity, path, value);
201
+ }
202
+ updateGroup[cellAlphaNum] = value;
203
+ if (allowFormulas && entities && entities.length) {
204
+ // go through the depGraph and update any cells that depend on this cell
205
+ const cellDepGraph = depGraph[cellAlphaNum];
206
+ if (cellDepGraph && cellDepGraph.length) {
207
+ cellDepGraph.forEach(depCellAlphaNum => {
208
+ if (depCellAlphaNum === cellAlphaNum) return;
209
+ if (updateGroup[depCellAlphaNum] === "__Currently&&Updating__") {
210
+ // if the cell is already being updated, return the value
211
+ return updateGroup[depCellAlphaNum];
212
+ }
213
+ const [depColLetter, depRowIndex] = depCellAlphaNum.split(/(\d+)/);
214
+ const depEntity = entities[depRowIndex - 1];
215
+ // if (!depEntity) debugger
216
+ // if (!depEntity) return
217
+ const depColIndex = depColLetter.charCodeAt(0) - 65;
218
+ const depColSchema = schema.fields[depColIndex];
219
+ const depPath = depColSchema.path;
220
+ const depNewVal = depEntity[depPath];
221
+
222
+ const { errors: _errors } = editCellHelper({
223
+ _cellAlphaNum: depCellAlphaNum,
224
+ allowFormulas,
225
+ updateGroup,
226
+ depGraph,
227
+ entity: depEntity,
228
+ path: depPath,
229
+ schema,
230
+ columnSchema: depColSchema,
231
+ newVal: depNewVal,
232
+ entities,
233
+ depLevel: depLevel + 1,
234
+ nestLevel: nestLevel
235
+ });
236
+ errors = mergeErrors(errors, _errors);
237
+ });
238
+ }
239
+ }
240
+ updateGroup[cellAlphaNum] = value?.formula ? value.value : value;
241
+ if (!hasErrors(errors)) {
242
+ errors[cellId] = undefined;
243
+ }
244
+ return { entity, errors, value };
44
245
  };
246
+
247
+ function getCellAlphaNum({ entities, entity, colSchema, schema }) {
248
+ const rowIndex = entities.indexOf(entity);
249
+ const colIndex = schema.fields.indexOf(colSchema);
250
+ return getCellAlphaNumHelper(colIndex, rowIndex);
251
+ }
252
+ export function getCellAlphaNumHelper(colIndex, rowIndex) {
253
+ const colLetter = getColLetFromIndex(colIndex);
254
+ const cellAlphaNum = `${colLetter}${rowIndex + 1}`;
255
+ return cellAlphaNum;
256
+ }
257
+
258
+ const hasErrors = errors => {
259
+ return errors.__hasErrors;
260
+ };
261
+
262
+ export const getColLetFromIndex = index => {
263
+ if (index > 25)
264
+ return (
265
+ getColLetFromIndexHelper(index / 26 - 1) +
266
+ getColLetFromIndexHelper(index % 26)
267
+ );
268
+ return getColLetFromIndexHelper(index);
269
+ };
270
+ const getColLetFromIndexHelper = index => {
271
+ return String.fromCharCode(65 + index);
272
+ };
273
+
274
+ const lettersToNumber = letters => {
275
+ let n = 0;
276
+ for (let p = 0; p < letters.length; p++) {
277
+ n = letters[p].charCodeAt() - 64 + n * 26;
278
+ }
279
+ return n - 1;
280
+ };
281
+
282
+ export const replaceFormulaRanges = ({ formula, schema, entities }) => {
283
+ let error;
284
+ const replaced = formula
285
+ .toLowerCase()
286
+ .replaceAll("$", "")
287
+ .replace(/([A-Z]*[0-9]*:[A-Z]*[0-9]*)/gi, _match => {
288
+ // if (_match.includes(":")) {
289
+ // }
290
+ const match = _match.toUpperCase();
291
+ const [start, end] = match.split(":");
292
+ const [startLetter, _startRowIndex] = start.split(/(\d+)/);
293
+ const [endLetter, _endRowIndex] = end.split(/(\d+)/);
294
+ let startRowIndex = parseInt(_startRowIndex);
295
+ let endRowIndex = parseInt(_endRowIndex);
296
+ let toRet = "";
297
+
298
+ if (startLetter !== endLetter) {
299
+ error = `Ranges must be in the same column`;
300
+ return "range_in_different_columns";
301
+ }
302
+ if (!startLetter && !endLetter && _startRowIndex === _endRowIndex) {
303
+ // we have a range like 1:1
304
+ const rowIndex = startRowIndex;
305
+ const startColIndex = 1;
306
+ const endColIndex = schema.fields.length;
307
+ for (let i = startColIndex; i <= endColIndex; i++) {
308
+ const colLet = getColLetFromIndex(i - 1);
309
+ toRet += `${colLet}${rowIndex}${i === endColIndex ? "" : ","}`;
310
+ }
311
+ return toRet;
312
+ }
313
+ if (_startRowIndex === undefined && _endRowIndex === undefined) {
314
+ // we have a range like A:A
315
+ startRowIndex = 1;
316
+ endRowIndex = entities.length;
317
+ }
318
+ for (let j = startRowIndex; j <= endRowIndex; j++) {
319
+ toRet += `${startLetter}${j}${j === endRowIndex ? "" : ","}`;
320
+ }
321
+ return toRet;
322
+ });
323
+ return {
324
+ replacedFormula: replaced,
325
+ error
326
+ };
327
+ };
328
+
329
+ function mergeErrors(errors, _errors) {
330
+ let hasErrors = false;
331
+ if (_errors.__hasErrors || errors.__hasErrors) {
332
+ hasErrors = true;
333
+ }
334
+ errors = {
335
+ ...errors,
336
+ ..._errors,
337
+ __hasErrors: hasErrors
338
+ };
339
+ return errors;
340
+ }
@@ -1,4 +1,4 @@
1
- import { get } from "lodash";
1
+ import { get, has } from "lodash";
2
2
  import { isString } from "lodash";
3
3
  import { isTruthy } from "./isTruthy";
4
4
 
@@ -16,5 +16,8 @@ export const getCellVal = (ent, path, col) => {
16
16
  selectedCellVal === "yes";
17
17
  selectedCellVal = isTruthy(selectedCellVal);
18
18
  }
19
+ if (has(selectedCellVal, "value")) {
20
+ return selectedCellVal.value;
21
+ }
19
22
  return selectedCellVal;
20
23
  };