feat: Dynamic currency (#36416)

This commit is contained in:
Richard Fogaca Nienkotter
2026-01-17 02:58:41 -03:00
committed by GitHub
parent 896947c787
commit f4474b2e3e
72 changed files with 3068 additions and 173 deletions

View File

@@ -749,7 +749,7 @@ export class TableRenderer extends Component {
onContextMenu={e => this.props.onContextMenu(e, colKey, rowKey)}
style={style}
>
{displayCell(agg.format(aggValue), allowRenderHtml)}
{displayCell(agg.format(aggValue, agg), allowRenderHtml)}
</td>
);
});
@@ -766,7 +766,7 @@ export class TableRenderer extends Component {
onClick={rowTotalCallbacks[flatRowKey]}
onContextMenu={e => this.props.onContextMenu(e, undefined, rowKey)}
>
{displayCell(agg.format(aggValue), allowRenderHtml)}
{displayCell(agg.format(aggValue, agg), allowRenderHtml)}
</td>
);
}
@@ -830,7 +830,7 @@ export class TableRenderer extends Component {
onContextMenu={e => this.props.onContextMenu(e, colKey, undefined)}
style={{ padding: '5px' }}
>
{displayCell(agg.format(aggValue), this.props.allowRenderHtml)}
{displayCell(agg.format(aggValue, agg), this.props.allowRenderHtml)}
</td>
);
});
@@ -847,7 +847,7 @@ export class TableRenderer extends Component {
onClick={grandTotalCallback}
onContextMenu={e => this.props.onContextMenu(e, undefined, undefined)}
>
{displayCell(agg.format(aggValue), this.props.allowRenderHtml)}
{displayCell(agg.format(aggValue, agg), this.props.allowRenderHtml)}
</td>
);
}

View File

@@ -186,9 +186,13 @@ const usFmtPct = numberFormat({
suffix: '%',
});
const fmtNonString = formatter => x =>
typeof x === 'string' ? x : formatter(x);
const fmtNonString = formatter => (x, aggregator) =>
typeof x === 'string' ? x : formatter(x, aggregator);
/*
* Aggregators track currencies via push() and expose them via getCurrencies()
* for per-cell currency detection in AUTO mode.
*/
const baseAggregatorTemplates = {
count(formatter = usFmtInt) {
return () =>
@@ -211,14 +215,21 @@ const baseAggregatorTemplates = {
return function () {
return {
uniq: [],
currencySet: new Set(),
push(record) {
if (!Array.from(this.uniq).includes(record[attr])) {
this.uniq.push(record[attr]);
}
if (record.__currencyColumn && record[record.__currencyColumn]) {
this.currencySet.add(record[record.__currencyColumn]);
}
},
value() {
return fn(this.uniq);
},
getCurrencies() {
return Array.from(this.currencySet);
},
format: fmtNonString(formatter),
numInputs: typeof attr !== 'undefined' ? 0 : 1,
};
@@ -231,16 +242,23 @@ const baseAggregatorTemplates = {
return function () {
return {
sum: 0,
currencySet: new Set(),
push(record) {
if (Number.isNaN(Number(record[attr]))) {
this.sum = record[attr];
} else {
this.sum += parseFloat(record[attr]);
}
if (record.__currencyColumn && record[record.__currencyColumn]) {
this.currencySet.add(record[record.__currencyColumn]);
}
},
value() {
return this.sum;
},
getCurrencies() {
return Array.from(this.currencySet);
},
format: fmtNonString(formatter),
numInputs: typeof attr !== 'undefined' ? 0 : 1,
};
@@ -253,6 +271,7 @@ const baseAggregatorTemplates = {
return function (data) {
return {
val: null,
currencySet: new Set(),
sorter: getSort(
typeof data !== 'undefined' ? data.sorters : null,
attr,
@@ -285,10 +304,16 @@ const baseAggregatorTemplates = {
) {
this.val = x;
}
if (record.__currencyColumn && record[record.__currencyColumn]) {
this.currencySet.add(record[record.__currencyColumn]);
}
},
value() {
return this.val;
},
getCurrencies() {
return Array.from(this.currencySet);
},
format(x) {
if (typeof x === 'number') {
return formatter(x);
@@ -307,6 +332,7 @@ const baseAggregatorTemplates = {
return {
vals: [],
strMap: {},
currencySet: new Set(),
push(record) {
const val = record[attr];
const x = Number(val);
@@ -316,6 +342,9 @@ const baseAggregatorTemplates = {
} else {
this.vals.push(x);
}
if (record.__currencyColumn && record[record.__currencyColumn]) {
this.currencySet.add(record[record.__currencyColumn]);
}
},
value() {
if (
@@ -339,6 +368,9 @@ const baseAggregatorTemplates = {
const i = (this.vals.length - 1) * q;
return (this.vals[Math.floor(i)] + this.vals[Math.ceil(i)]) / 2.0;
},
getCurrencies() {
return Array.from(this.currencySet);
},
format: fmtNonString(formatter),
numInputs: typeof attr !== 'undefined' ? 0 : 1,
};
@@ -354,11 +386,15 @@ const baseAggregatorTemplates = {
m: 0.0,
s: 0.0,
strValue: null,
currencySet: new Set(),
push(record) {
const x = Number(record[attr]);
if (Number.isNaN(x)) {
this.strValue =
typeof record[attr] === 'string' ? record[attr] : this.strValue;
if (record.__currencyColumn && record[record.__currencyColumn]) {
this.currencySet.add(record[record.__currencyColumn]);
}
return;
}
this.n += 1.0;
@@ -368,6 +404,9 @@ const baseAggregatorTemplates = {
const mNew = this.m + (x - this.m) / this.n;
this.s += (x - this.m) * (x - mNew);
this.m = mNew;
if (record.__currencyColumn && record[record.__currencyColumn]) {
this.currencySet.add(record[record.__currencyColumn]);
}
},
value() {
if (this.strValue) {
@@ -392,6 +431,9 @@ const baseAggregatorTemplates = {
throw new Error('unknown mode for runningStat');
}
},
getCurrencies() {
return Array.from(this.currencySet);
},
format: fmtNonString(formatter),
numInputs: typeof attr !== 'undefined' ? 0 : 1,
};
@@ -405,6 +447,7 @@ const baseAggregatorTemplates = {
return {
sumNum: 0,
sumDenom: 0,
currencySet: new Set(),
push(record) {
if (!Number.isNaN(Number(record[num]))) {
this.sumNum += parseFloat(record[num]);
@@ -412,10 +455,16 @@ const baseAggregatorTemplates = {
if (!Number.isNaN(Number(record[denom]))) {
this.sumDenom += parseFloat(record[denom]);
}
if (record.__currencyColumn && record[record.__currencyColumn]) {
this.currencySet.add(record[record.__currencyColumn]);
}
},
value() {
return this.sumNum / this.sumDenom;
},
getCurrencies() {
return Array.from(this.currencySet);
},
format: formatter,
numInputs:
typeof num !== 'undefined' && typeof denom !== 'undefined' ? 0 : 2,
@@ -447,6 +496,9 @@ const baseAggregatorTemplates = {
return this.inner.value() / acc;
},
getCurrencies() {
return this.inner.getCurrencies ? this.inner.getCurrencies() : [];
},
numInputs: wrapped(...Array.from(x || []))().numInputs,
};
};