Skip to content

Commit e2de807

Browse files
committed
remove numbro
1 parent b209617 commit e2de807

8 files changed

Lines changed: 109 additions & 65 deletions

File tree

Signum.React/Scripts/Finder.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from "react";
22
import { DateTime } from 'luxon'
3-
import numbro from "numbro"
43
import * as AppContext from "./AppContext"
54
import * as Navigator from "./Navigator"
65
import { Dic, classes } from './Globals'
@@ -21,7 +20,7 @@ import { TypeEntity, QueryEntity } from './Signum.Entities.Basics';
2120

2221
import {
2322
Type, IType, EntityKind, QueryKey, getQueryNiceName, getQueryKey, isQueryDefined, TypeReference,
24-
getTypeInfo, tryGetTypeInfos, getEnumInfo, toLuxonFormat, toNumbroFormat, PseudoType, EntityData,
23+
getTypeInfo, tryGetTypeInfos, getEnumInfo, toLuxonFormat, toNumberFormat, PseudoType, EntityData,
2524
TypeInfo, PropertyRoute, QueryTokenString, getTypeInfos, tryGetTypeInfo, onReloadTypesActions
2625
} from './Reflection';
2726

@@ -1642,16 +1641,16 @@ export const formatRules: FormatRule[] = [
16421641
name: "Number",
16431642
isApplicable: col => col.token!.filterType == "Integer" || col.token!.filterType == "Decimal",
16441643
formatter: col => {
1645-
const numbroFormat = toNumbroFormat(col.token!.format);
1646-
return new CellFormatter((cell: number | undefined) => cell == undefined ? "" : <span>{numbro(cell).format(numbroFormat)}</span>, "numeric-cell");
1644+
const numberFormat = toNumberFormat(col.token!.format);
1645+
return new CellFormatter((cell: number | undefined) => cell == undefined ? "" : <span>{numberFormat.format(cell)}</span>, "numeric-cell");
16471646
}
16481647
},
16491648
{
16501649
name: "Number with Unit",
16511650
isApplicable: col => (col.token!.filterType == "Integer" || col.token!.filterType == "Decimal") && !!col.token!.unit,
16521651
formatter: col => {
1653-
const numbroFormat = toNumbroFormat(col.token!.format);
1654-
return new CellFormatter((cell: number | undefined) => cell == undefined ? "" : <span>{numbro(cell).format(numbroFormat) + "\u00a0" + col.token!.unit}</span>, "numeric-cell");
1652+
const numberFormat = toNumberFormat(col.token!.format);
1653+
return new CellFormatter((cell: number | undefined) => cell == undefined ? "" : <span>{numberFormat.format(cell) + "\u00a0" + col.token!.unit}</span>, "numeric-cell");
16551654
}
16561655
},
16571656
{

Signum.React/Scripts/Lines/ValueLine.tsx

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import * as React from 'react'
22
import { DateTime } from 'luxon'
3-
import numbro from 'numbro'
43
import * as DateTimePicker from 'react-widgets/lib/DateTimePicker'
54
import { Dic, addClass, classes } from '../Globals'
6-
import { MemberInfo, getTypeInfo, TypeReference, toLuxonFormat, toDurationFormat, toNumbroFormat, isTypeEnum, durationToString, TypeInfo } from '../Reflection'
5+
import { MemberInfo, getTypeInfo, TypeReference, toLuxonFormat, toDurationFormat, toNumberFormat, isTypeEnum, durationToString, TypeInfo } from '../Reflection'
76
import { LineBaseController, LineBaseProps, useController } from '../Lines/LineBase'
87
import { FormGroup } from '../Lines/FormGroup'
98
import { FormControlReadonly } from '../Lines/FormControlReadonly'
@@ -12,6 +11,7 @@ import TextArea from '../Components/TextArea';
1211
import 'react-widgets/dist/css/react-widgets.css';
1312
import { KeyCodes } from '../Components/Basic';
1413
import { format } from 'd3';
14+
import { isPrefix } from '../FindOptions'
1515

1616
export interface ValueLineProps extends LineBaseProps {
1717
valueLineType?: ValueLineType;
@@ -455,14 +455,14 @@ ValueLineRenderers.renderers["Decimal" as ValueLineType] = (vl) => {
455455
function numericTextBox(vl: ValueLineController, validateKey: (e: React.KeyboardEvent<any>) => boolean) {
456456
const s = vl.props
457457

458-
const numbroFormat = toNumbroFormat(s.formatText);
458+
const numberFormat = toNumberFormat(s.formatText);
459459

460460
if (s.ctx.readOnly)
461461
return (
462462
<FormGroup ctx={s.ctx} labelText={s.labelText} helpText={s.helpText} htmlAttributes={{ ...vl.baseHtmlAttributes(), ...s.formGroupHtmlAttributes }} labelHtmlAttributes={s.labelHtmlAttributes}>
463463
{vl.withItemGroup(
464464
<FormControlReadonly htmlAttributes={vl.props.valueHtmlAttributes} ctx={s.ctx} className="numeric" innerRef={vl.inputElement}>
465-
{s.ctx.value == null ? "" : numbro(s.ctx.value).format(numbroFormat)}
465+
{s.ctx.value == null ? "" : numberFormat.format(s.ctx.value)}
466466
</FormControlReadonly>)}
467467
</FormGroup>
468468
);
@@ -498,7 +498,7 @@ function numericTextBox(vl: ValueLineController, validateKey: (e: React.Keyboard
498498
onChange={handleOnChange}
499499
formControlClass={classes(s.ctx.formControlClass, vl.mandatoryClass)}
500500
validateKey={validateKey}
501-
format={numbroFormat}
501+
format={numberFormat}
502502
innerRef={vl.inputElement as React.RefObject<HTMLInputElement>}
503503
/>
504504
)}
@@ -510,7 +510,7 @@ export interface NumericTextBoxProps {
510510
value: number | null;
511511
onChange: (newValue: number | null) => void;
512512
validateKey: (e: React.KeyboardEvent<any>) => boolean;
513-
format?: string;
513+
format: Intl.NumberFormat;
514514
formControlClass?: string;
515515
htmlAttributes?: React.HTMLAttributes<HTMLInputElement>;
516516
innerRef?: ((ta: HTMLInputElement | null) => void) | React.RefObject<HTMLInputElement>;
@@ -522,7 +522,7 @@ export function NumericTextBox(p: NumericTextBoxProps) {
522522

523523

524524
const value = text != undefined ? text :
525-
p.value != undefined ? numbro(p.value).format(p.format) :
525+
p.value != undefined ? p.format?.format(p.value) :
526526
"";
527527

528528
return <input ref={p.innerRef} {...p.htmlAttributes}
@@ -552,15 +552,10 @@ export function NumericTextBox(p: NumericTextBoxProps) {
552552

553553
let value = ValueLineController.autoFixString(input.value, false);
554554

555-
if (numbro.languageData().delimiters.decimal == ',' && !value.contains(",") && value.trim().length > 0) //Numbro transforms 1.000 to 1,0 in spanish or german
556-
value = value + ",00";
557-
558-
if (p.format && p.format.endsWith("%")) {
559-
if (value && !value.endsWith("%"))
560-
value += "%";
561-
}
555+
//if (numbro.languageData().delimiters.decimal == ',' && !value.contains(",") && value.trim().length > 0) //Numbro transforms 1.000 to 1,0 in spanish or german
556+
// value = value + ",00";
562557

563-
const result = value == undefined || value.length == 0 ? null : numbro.unformat(value, p.format);
558+
const result = value == undefined || value.length == 0 ? null : unformat(p.format, value);
564559
setText(undefined);
565560
if (result != p.value)
566561
p.onChange(result);
@@ -569,6 +564,29 @@ export function NumericTextBox(p: NumericTextBoxProps) {
569564
p.htmlAttributes.onBlur(e);
570565
}
571566

567+
function unformat(format: Intl.NumberFormat, str: string): number {
568+
var isPercentage = format.resolvedOptions().style == "percent";
569+
if (isPercentage) {
570+
format = new Intl.NumberFormat(format.resolvedOptions().locale);
571+
}
572+
573+
const thousandSeparator = format.format(1111).replace(/1/g, '');
574+
const decimalSeparator = format.format(1.1).replace(/1/g, '');
575+
576+
if (thousandSeparator)
577+
str = str.replace(new RegExp('\\' + thousandSeparator, 'g'), '');
578+
579+
if (decimalSeparator)
580+
str = str.replace(new RegExp('\\' + decimalSeparator), '.');
581+
582+
var result = parseFloat(str);
583+
584+
if (isPercentage)
585+
return result / 100;
586+
587+
return result;
588+
}
589+
572590
function handleOnChange(e: React.SyntheticEvent<any>) {
573591
const input = e.currentTarget as HTMLInputElement;
574592
setText(input.value);

Signum.React/Scripts/Reflection.ts

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { DateTime} from 'luxon';
2-
import numbro from 'numbro';
32
import { Dic } from './Globals';
43
import { ModifiableEntity, Entity, Lite, MListElement, ModelState, MixinEntity } from './Signum.Entities'; //ONLY TYPES or Cyclic problems in Webpack!
54
import { ajaxGet } from './Services';
@@ -120,38 +119,80 @@ export function toDurationFormat(format: string | undefined): string | undefined
120119
return format.replace("\\:", ":");
121120
}
122121

123-
export function toNumbroFormat(format: string | undefined) {
122+
export namespace NumberFormatSettings {
123+
export let defaultNumberFormatLocale: string = null!;
124+
}
125+
126+
//https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings
127+
export function toNumberFormat(format: string | undefined, locale?: string): Intl.NumberFormat {
128+
return new Intl.NumberFormat(locale ?? NumberFormatSettings.defaultNumberFormatLocale, toNumberFormatOptions(format));
129+
}
130+
131+
export function toNumberFormatOptions(format: string | undefined): Intl.NumberFormatOptions | undefined {
124132

125133
if (format == undefined)
126134
return undefined;
127135

128136
const f = format.toUpperCase();
129137

130-
if (f.startsWith("C"))
131-
return "0." + "0".repeat(parseInt(f.after("C") || "2"));
138+
if (f.startsWith("C")) //unit comes separated
139+
return {
140+
style: "decimal",
141+
minimumFractionDigits: parseInt(f.after("C")) || 2,
142+
maximumFractionDigits: parseInt(f.after("C")) || 2,
143+
useGrouping: true,
144+
}
132145

133146
if (f.startsWith("N"))
134-
return "0,0." + "0".repeat(parseInt(f.after("N") || "2"));
147+
return {
148+
style: "decimal",
149+
minimumFractionDigits: parseInt(f.after("N")) || 2,
150+
maximumFractionDigits: parseInt(f.after("N")) || 2,
151+
useGrouping: true,
152+
}
135153

136154
if (f.startsWith("D"))
137-
return "0".repeat(parseInt(f.after("D") || "1"));
155+
return {
156+
style: "decimal",
157+
maximumFractionDigits: 0,
158+
minimumIntegerDigits: parseInt(f.after("D")) || 1,
159+
useGrouping: false,
160+
}
138161

139162
if (f.startsWith("F"))
140-
return "0." + "0".repeat(parseInt(f.after("F") || "2"));
163+
return {
164+
style: "decimal",
165+
minimumFractionDigits: parseInt(f.after("F")) || 2,
166+
maximumFractionDigits: parseInt(f.after("F")) || 2,
167+
useGrouping: false,
168+
}
141169

142170
if (f.startsWith("E"))
143-
return "0." + "0".repeat(parseInt(f.after("E") || "2"));
171+
return {
172+
style: "decimal",
173+
notation: "scientific",
174+
minimumFractionDigits: parseInt(f.after("E")) || 6,
175+
maximumFractionDigits: parseInt(f.after("E")) || 6,
176+
useGrouping: false,
177+
} as any;
144178

145179
if (f.startsWith("P"))
146-
return "0." + "0".repeat(parseInt(f.after("P") || "2")) + "%";
180+
return {
181+
style: "percent",
182+
minimumFractionDigits: parseInt(f.after("P")) || 2,
183+
maximumFractionDigits: parseInt(f.after("P")) || 2,
184+
useGrouping: false,
185+
}
147186

148-
if (f.contains("#"))
149-
format = format
150-
.replaceAll(".#", "[.]0")
151-
.replaceAll(",#", "[,]0")
152-
.replaceAll("#", "0");
153187

154-
return format;
188+
//simple euristic
189+
var afterDot = f.tryAfter(".") ?? "";
190+
return {
191+
style: "decimal",
192+
minimumFractionDigits: afterDot.trimStart("#").length,
193+
maximumFractionDigits: afterDot.length,
194+
useGrouping: f.contains(","),
195+
}
155196
}
156197

157198
export function valToString(val: any) {
@@ -165,7 +206,7 @@ export function numberToString(val: any, format?: string) {
165206
if (val == null)
166207
return "";
167208

168-
return numbro(val).format(toNumbroFormat(format));
209+
return toNumberFormat(format).format(val);
169210
}
170211

171212
export function dateToString(val: any, format?: string) {

Signum.React/Scripts/SearchControl/FilterBuilder.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { FilterOptionParsed, QueryDescription, QueryToken, SubTokensOptions, fil
55
import { SearchMessage } from '../Signum.Entities'
66
import { isNumber } from '../Lines/ValueLine'
77
import { ValueLine, EntityLine, EntityCombo, StyleContext, FormControlReadonly } from '../Lines'
8-
import { Binding, IsByAll, tryGetTypeInfos, toLuxonFormat, getTypeInfos } from '../Reflection'
8+
import { Binding, IsByAll, tryGetTypeInfos, toLuxonFormat, getTypeInfos, toNumberFormat } from '../Reflection'
99
import { TypeContext } from '../TypeContext'
1010
import QueryTokenBuilder from './QueryTokenBuilder'
1111
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -582,8 +582,10 @@ export function PinnedFilterEditor(p: PinnedFilterEditorProps) {
582582
if (p.readonly)
583583
return <span className="numeric form-control form-control-xs" style={{ width: "60px" }}>{val}</span>;
584584

585+
var numberFormat = toNumberFormat("0");
586+
585587
return (
586-
<NumericTextBox value={val == undefined ? null : val} onChange={n => { binding.setValue(n == null ? undefined : n); p.onChange(); }}
588+
<NumericTextBox value={val == undefined ? null : val} format={numberFormat} onChange={n => { binding.setValue(n == null ? undefined : n); p.onChange(); }}
587589
validateKey={isNumber} formControlClass="form-control form-control-xs" htmlAttributes={{ placeholder: title, style: { width: "60px" } }} />
588590
);
589591
}

Signum.React/Scripts/SearchControl/PaginationSelector.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import * as React from 'react'
2-
import numbro from 'numbro'
32
import * as Finder from '../Finder'
43
import { classes } from '../Globals'
54
import { ResultTable, Pagination, PaginationMode, PaginateMath } from '../FindOptions'
65
import { SearchMessage } from '../Signum.Entities'
76
import "./PaginationSelector.css"
7+
import { toNumberFormat } from '../Reflection'
88

99
interface PaginationSelectorProps {
1010
resultTable?: ResultTable;
@@ -32,32 +32,30 @@ export default function PaginationSelector(p: PaginationSelectorProps) {
3232

3333
const pagination = p.pagination;
3434

35-
function format(num: number): string {
36-
return numbro(num).format("0,0");
37-
}
35+
var numberFormat = toNumberFormat("0")
3836

3937
switch (pagination.mode) {
4038

4139
case "All":
4240
return (
4341
<span>{SearchMessage._0Results_N.niceToString().forGenderAndNumber(resultTable.totalElements).formatHtml(
44-
<span className="sf-pagination-strong" key={1}>{resultTable.totalElements && format(resultTable.totalElements)}</span>)
42+
<span className="sf-pagination-strong" key={1}>{resultTable.totalElements && numberFormat.format(resultTable.totalElements)}</span>)
4543
}</span>
4644
);
4745

4846
case "Firsts":
4947
return (
5048
<span>{SearchMessage.First0Results_N.niceToString().forGenderAndNumber(resultTable.rows.length).formatHtml(
51-
<span className={"sf-pagination-strong" + (resultTable.rows.length == resultTable.pagination.elementsPerPage ? " sf-pagination-overflow" : "")} key={1}>{format(resultTable.rows.length)}</span>)
49+
<span className={"sf-pagination-strong" + (resultTable.rows.length == resultTable.pagination.elementsPerPage ? " sf-pagination-overflow" : "")} key={1}>{numberFormat.format(resultTable.rows.length)}</span>)
5250
}</span>
5351
);
5452

5553
case "Paginate":
5654
return (
5755
<span>{SearchMessage._01of2Results_N.niceToString().forGenderAndNumber(resultTable.totalElements).formatHtml(
58-
<span className={"sf-pagination-strong"} key={1}>{format(PaginateMath.startElementIndex(pagination))}</span>,
59-
<span className={"sf-pagination-strong"} key={2}>{format(PaginateMath.endElementIndex(pagination, resultTable.rows.length))}</span>,
60-
<span className={"sf-pagination-strong"} key={3}>{resultTable.totalElements && format(resultTable.totalElements)}</span>)
56+
<span className={"sf-pagination-strong"} key={1}>{numberFormat.format(PaginateMath.startElementIndex(pagination))}</span>,
57+
<span className={"sf-pagination-strong"} key={2}>{numberFormat.format(PaginateMath.endElementIndex(pagination, resultTable.rows.length))}</span>,
58+
<span className={"sf-pagination-strong"} key={3}>{resultTable.totalElements && numberFormat.format(resultTable.totalElements)}</span>)
6159
}</span>
6260
);
6361
default:

Signum.React/Scripts/SearchControl/ValueSearchControl.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import * as React from 'react'
2-
import numbro from 'numbro'
32
import { DateTime } from 'luxon'
43
import { classes } from '../Globals'
54
import * as Navigator from '../Navigator'
65
import * as Finder from '../Finder'
76
import { FindOptions, FindOptionsParsed, SubTokensOptions, QueryToken, QueryValueRequest } from '../FindOptions'
87
import { Lite, Entity, getToString, EmbeddedEntity } from '../Signum.Entities'
9-
import { getQueryKey, toNumbroFormat, toLuxonFormat, getEnumInfo, QueryTokenString, getTypeInfo, getTypeName } from '../Reflection'
8+
import { getQueryKey, toNumberFormat, toLuxonFormat, getEnumInfo, QueryTokenString, getTypeInfo, getTypeName } from '../Reflection'
109
import { AbortableRequest } from "../Services";
1110
import { SearchControlProps } from "./SearchControl";
1211
import { BsColor } from '../Components';
@@ -282,8 +281,8 @@ export default class ValueSearchControl extends React.Component<ValueSearchContr
282281
switch (token.filterType) {
283282
case "Integer":
284283
case "Decimal":
285-
const numbroFormat = toNumbroFormat(this.props.format ?? token.format);
286-
return numbro(value).format(numbroFormat);
284+
const numbroFormat = toNumberFormat(this.props.format ?? token.format);
285+
return numbroFormat.format(value);
287286
case "DateTime":
288287
const momentFormat = toLuxonFormat(this.props.format ?? token.format);
289288
return DateTime.fromISO(value).toFormat(momentFormat);

Signum.React/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
"@types/react-router-dom": "5.1.5",
3737
"@types/react-transition-group": "4.4.0",
3838
"@types/react-widgets": "4.4.2",
39-
"numbro": "2.3.0",
4039
"@types/luxon": "1.24.4",
4140
"luxon": "1.25.0",
4241
"popper.js": "1.16.1",

Signum.React/yarn.lock

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -364,11 +364,6 @@
364364
resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
365365
integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=
366366

367-
bignumber.js@^8.1.1:
368-
version "8.1.1"
369-
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.1.1.tgz#4b072ae5aea9c20f6730e4e5d529df1271c4d885"
370-
integrity sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ==
371-
372367
classnames@^2.2.6:
373368
version "2.2.6"
374369
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
@@ -421,13 +416,6 @@ luxon@1.25.0:
421416
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.25.0.tgz#d86219e90bc0102c0eb299d65b2f5e95efe1fe72"
422417
integrity sha512-hEgLurSH8kQRjY6i4YLey+mcKVAWXbDNlZRmM6AgWDJ1cY3atl8Ztf5wEY7VBReFbmGnwQPz7KYJblL8B2k0jQ==
423418

424-
numbro@2.3.0:
425-
version "2.3.0"
426-
resolved "https://registry.yarnpkg.com/numbro/-/numbro-2.3.0.tgz#a2cdcf164346833fded1128dfc5056a882b3097a"
427-
integrity sha512-KGa4qVveFGC0HgKaJnmKYqyC9CX7jQONxEfREVwc/8UwTJtcEt60F8j/NCKgZH/IFW/Z9uibhCDqpJiRxuXdsA==
428-
dependencies:
429-
bignumber.js "^8.1.1"
430-
431419
object-assign@^4.1.1:
432420
version "4.1.1"
433421
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"

0 commit comments

Comments
 (0)