Skip to content

Commit

Permalink
more work on formulas
Browse files Browse the repository at this point in the history
  • Loading branch information
tnrich committed Jan 25, 2024
1 parent d09768f commit f8d7a5d
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 59 deletions.
29 changes: 27 additions & 2 deletions packages/ui/demo/src/examples/EditableCellTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,33 @@ export default function SimpleTable(p) {
// const [tagValuesAsObjects, tagValuesAsObjectsComp] = useToggle({
// type: "tagValuesAsObjects"
// });
let entsToUse;
const [entities, setEnts] = useState([]);
entsToUse = entities;

const schema = useMemo(() => {
if (allowFormulas) {
entsToUse = [
{
col1: "=sum(b1,b2,a2)",
col2: 44
},
{
col1: "=sum(b1,b2)",
col2: 44
},
{
col1: "=sum(a1,b3)",
col2: 44
},
];
return {
fields: [
{ path: "col1", allowFormulas: true },
{ path: "col2", allowFormulas: true }
]
};
}
return {
fields: [
{
Expand All @@ -69,7 +93,7 @@ export default function SimpleTable(p) {
return "Must include the letter 'a'";
},
format: newVal => {
return newVal?.toLowerCase();
return newVal?.toLowerCase?.();
},
allowFormulas
},
Expand Down Expand Up @@ -115,6 +139,7 @@ export default function SimpleTable(p) {
]
};
}, [defaultValAsFunc, allowFormulas]);
console.log(`entsToUse:`,entsToUse)
return (
<div>
<ExcelCell></ExcelCell>
Expand All @@ -131,7 +156,7 @@ export default function SimpleTable(p) {
formName="editableCellTable"
isSimple
isCellEditable
entities={entities}
entities={entsToUse}
schema={schema}
// isEntityDisabled={
// isEntityDisabled
Expand Down
58 changes: 51 additions & 7 deletions packages/ui/src/DataTable/editCellHelper.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { set } from "lodash";
import { isPlainObject, set } from "lodash";
import { defaultValidators } from "./defaultValidators";
import { defaultFormatters } from "./defaultFormatters";
import { evaluate } from "mathjs";

//(mutative) responsible for formatting and then validating the

Expand All @@ -9,7 +10,8 @@ export const editCellHelper = ({
path,
schema,
columnSchema,
newVal
newVal,
entities
}) => {
let nv = newVal;

Expand All @@ -20,7 +22,9 @@ export const editCellHelper = ({
let error;
if (nv === undefined && colSchema.defaultValue !== undefined)
nv = colSchema.defaultValue;
let hasFormula = false;
if (colSchema.allowFormulas && typeof nv === "string" && nv[0] === "=") {
const ogFormula = nv;
// if the nv is missing a closing paren, add it
// count the number of open parens
// count the number of close parens
Expand All @@ -30,19 +34,58 @@ export const editCellHelper = ({
if (openParens > closeParens) {
nv = nv + ")";
}
console.log(`nv:`,nv)
// if the nv is a valid formula, evaluate it
// if the nv is not a valid formula, return the error
// fill in any variables with their values
nv = nv.toLowerCase().replace(/([A-Z]+[0-9]+)/gi, match => {
// match = E12 or B4
const [letter, rowIndex] = match.split(/(\d+)/);
const entity = entities.find((e, i) => {
return i === rowIndex - 1;
});
const columns = schema.fields;
const letterIndex = letter.toUpperCase().charCodeAt(0) - 65;
const col = columns[letterIndex];
if (!col) {
return match;
}
const { path } = col;
if (!entity) return match;
const val = entity[path];
if (val === undefined) return match;
if (isPlainObject(val)) {
if (val.formula) {
const {value} = editCellHelper({})
}
}
return val;
});
const toEval = nv.slice(1);
try {
nv = evaluate(toEval);


nv = {
formula: ogFormula,
value: `${nv}`
};
} catch (e) {
nv = {
formula: ogFormula,
value: `#ERROR`
};
error = e.message;
}
hasFormula = nv;
nv = nv.value;
}

if (format) {
nv = format(nv, colSchema);
}
if (defaultFormatters[type]) {
nv = defaultFormatters[type](nv, colSchema);
}
if (validate) {
if (validate && !error) {
error = validate(nv, colSchema, entity);
}
if (!error) {
Expand All @@ -54,6 +97,7 @@ export const editCellHelper = ({
error = validator(nv, colSchema);
}
}
set(entity, path, nv);
return { entity, error };
const value = hasFormula || nv;
set(entity, path, value);
return { entity, error, value };
};
108 changes: 58 additions & 50 deletions packages/ui/src/DataTable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ import ReactMarkdown from "react-markdown";
import immer, { produceWithPatches, enablePatches, applyPatches } from "immer";
import papaparse from "papaparse";
import remarkGfm from "remark-gfm";
import { evaluate } from "mathjs";

import TgSelect from "../TgSelect";
import { withHotkeys } from "../utils/hotkeyUtils";
import InfoHelper from "../InfoHelper";
Expand Down Expand Up @@ -421,7 +419,9 @@ class DataTable extends React.Component {
}
//mutative
const { error } = editCellHelper({
entities,
entity: e,
schema,
columnSchema,
newVal: e[columnSchema.path]
});
Expand Down Expand Up @@ -632,6 +632,8 @@ class DataTable extends React.Component {
toPaste = e.clipboardData.getData("text/plain");
}
const jsonToPaste = e.clipboardData.getData("application/json");
let hasReplace = false;

try {
const pastedJson = [];
JSON.parse(jsonToPaste).forEach(row => {
Expand All @@ -652,12 +654,12 @@ class DataTable extends React.Component {
pasteData = pasteData.slice(1);
}
} catch (e) {
let hasReplace = false;
if (toPaste.inclues("=") && allowFormulas) {
if (toPaste.includes("=") && allowFormulas) {
// replace any commas inside brackets with %%comma%% so that we can parse it out later
toPaste.replace(/\(([^)]+)\)/g, (match) => {
toPaste = toPaste.replace(/\(([^)]+)\)/g, match => {
hasReplace = true;
return match.replace(/,/g, "%%comma%%");
const toRet = match.replace(/,/g, "%%comma%%");
return toRet;
});
}
if (toPaste.includes(",")) {
Expand All @@ -673,17 +675,18 @@ class DataTable extends React.Component {
console.error(`error p982qhgpf9qh`, error);
}
}
if (hasReplace) {
// put the commas back in
pasteData = pasteData.map(row => {
return row.map(cell => {
return cell.replace(/%%comma%%/g, ",");
});
});
}

}
pasteData = pasteData.length ? pasteData : defaultParsePaste(toPaste);
if (hasReplace) {
// put the commas back in

pasteData = pasteData.map(row => {
return row.map(cell => {
return cell.replace(/%%comma%%/g, ",");
});
});
}
if (!pasteData || !pasteData.length) return;

if (pasteData.length === 1 && pasteData[0].length === 1) {
Expand All @@ -700,6 +703,7 @@ class DataTable extends React.Component {
const entity = entityIdToEntity[rowId].e;
delete entity._isClean;
const { error } = editCellHelper({
entities,
entity,
path,
schema,
Expand Down Expand Up @@ -747,6 +751,7 @@ class DataTable extends React.Component {
const path = indexToPath[cellIndexToChange];
if (path) {
const { error } = editCellHelper({
entities,
entity,
path,
schema,
Expand Down Expand Up @@ -854,6 +859,7 @@ class DataTable extends React.Component {
const entity = entityIdToEntity[rowId].e;
delete entity._isClean;
const { error } = editCellHelper({
entities,
entity,
path,
schema,
Expand Down Expand Up @@ -1486,10 +1492,7 @@ class DataTable extends React.Component {
// if (isArrowKey && e.target?.tagName !== "INPUT") {
const isTabKey = e.keyCode === 9;
// const isEnter = e.keyCode === 13;
// console.log(`onKeydown datatable inner`);
// console.log(`isEnter:`, isEnter)
const isArrowKey = e.keyCode >= 37 && e.keyCode <= 40;
// console.log(`e.target?.tagName:`,e.target?.tagName)
if (
(isArrowKey && e.target?.tagName !== "INPUT") ||
isTabKey
Expand Down Expand Up @@ -2279,6 +2282,7 @@ class DataTable extends React.Component {
});
delete entity._isClean;
const { error } = editCellHelper({
entities,
entity,
path,
schema,
Expand Down Expand Up @@ -2594,37 +2598,40 @@ class DataTable extends React.Component {
};
} else if (column.allowFormulas) {
tableColumn.Cell = row => {
let val = cloneDeep(row.value);
const val = row.value;

if (!val) return "";
if (typeof val === "number") return val;
if (val.startsWith("=")) {
// fill in any variables with their values
val = val.toLowerCase().replace(/([A-Z]+[0-9]+)/gi, match => {
// match = E12 or B4
const [letter, rowIndex] = match.split(/(\d+)/);
const entity = entities.find((e, i) => {
return i === rowIndex - 1;
});
const letterIndex = letter.toUpperCase().charCodeAt(0) - 65;
const col = columns[letterIndex];
if (!col) {
return match;
}
const { path } = col;
if (!entity) return match;
const val = entity[path];
if (val === undefined) return match;
return val;
});
const toEval = val.slice(1);
try {
val = evaluate(toEval);
return val;
} catch (e) {
return "#ERROR";
}
if (val.formula) {
return val.value;
}
// if (val.startsWith("=")) {
// // fill in any variables with their values
// val = val.toLowerCase().replace(/([A-Z]+[0-9]+)/gi, match => {
// // match = E12 or B4
// const [letter, rowIndex] = match.split(/(\d+)/);
// const entity = entities.find((e, i) => {
// return i === rowIndex - 1;
// });
// const letterIndex = letter.toUpperCase().charCodeAt(0) - 65;
// const col = columns[letterIndex];
// if (!col) {
// return match;
// }
// const { path } = col;
// if (!entity) return match;
// const val = entity[path];
// if (val === undefined) return match;
// return val;
// });
// const toEval = val.slice(1);
// try {
// val = evaluate(toEval);
// return val;
// } catch (e) {
// return "#ERROR";
// }
// }
return val;
};
} else if (column.render) {
Expand Down Expand Up @@ -2683,6 +2690,10 @@ class DataTable extends React.Component {
let val = oldFunc(...args);
const oldVal = val;
const formulaVal = row.original?.[row.column.path];
if (row.index === 0) {
console.log(`row:`,row)
console.log(`formulaVal:`, formulaVal)
}
const text = this.getCopyTextForCell(val, row, column);
const isBool = column.type === "boolean";
const dataTest = {
Expand Down Expand Up @@ -2749,7 +2760,7 @@ class DataTable extends React.Component {
shouldSelectAll={reduxFormEditingCellSelectAll}
cancelEdit={this.cancelCellEdit}
isNumeric={column.type === "number"}
initialValue={column.allowFormulas ? formulaVal : text}
initialValue={formulaVal?.formula || text}
finishEdit={newVal => {
this.finishCellEdit(cellId, newVal);
}}
Expand Down Expand Up @@ -3059,6 +3070,7 @@ class DataTable extends React.Component {
}
}
const { error } = editCellHelper({
entities,
entity: entityToUpdate,
path: cellPath,
schema,
Expand Down Expand Up @@ -3588,11 +3600,7 @@ class DataTable extends React.Component {
{columnTitleTextified && !noTitle && (
<React.Fragment>
{maybeCheckbox}
{allowFormulas && (
<div>
{String.fromCharCode(65 + index)}:
</div>
)}
{allowFormulas && <div>{String.fromCharCode(65 + index)}:</div>}
<span
title={columnTitleTextified}
className={classNames({
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/FormComponents/tryToMatchSchemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ async function matchSchemas({ userSchema, officialSchema }) {
return editableFields.some(columnSchema => {
//mutative
const { error } = editCellHelper({
schema: officialSchema,
entities: userSchema.userData,
entity: e,
columnSchema,
newVal: columnSchema.hasMatch
Expand Down

0 comments on commit f8d7a5d

Please sign in to comment.