@xh/hoist 66.1.0 → 66.1.1
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/CHANGELOG.md +13 -0
- package/build/types/desktop/cmp/input/NumberInput.d.ts +3 -2
- package/build/types/format/FormatNumber.d.ts +49 -31
- package/build/types/mobile/cmp/input/NumberInput.d.ts +4 -3
- package/cmp/ag-grid/AgGrid.scss +6 -2
- package/core/exception/Exception.ts +4 -3
- package/desktop/cmp/input/NumberInput.ts +8 -8
- package/format/FormatNumber.ts +136 -63
- package/mobile/cmp/input/NumberInput.ts +10 -10
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/utils/js/LangUtils.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 66.1.1 - 2024-08-01
|
|
4
|
+
|
|
5
|
+
### 🐞 Bug Fixes
|
|
6
|
+
|
|
7
|
+
* `HoistException` now correctly passes an exception message to its underlying `Error` instance.
|
|
8
|
+
* Fixed `GridModel.cellBorders` prop to apply previously missing top and bottom borders to cells in the grid.
|
|
9
|
+
* Fix to new `mergeDeep` method.
|
|
10
|
+
|
|
3
11
|
## 66.1.0 - 2024-07-31
|
|
4
12
|
|
|
5
13
|
### 🎁 New Features
|
|
@@ -9,11 +17,16 @@
|
|
|
9
17
|
* New `mergeDeep` method provided in `@xh/hoist/utils/js` as an alternative to `lodash.merge`,
|
|
10
18
|
without lodash's surprising deep-merging of array-based properties.
|
|
11
19
|
* Enhanced Roles Admin UI to support bulk category reassignment.
|
|
20
|
+
* fmtNumber `zeroPad` now supports numbers to specify the decimal places out to which a
|
|
21
|
+
formatted number should be zero-padded.
|
|
12
22
|
|
|
13
23
|
### 🐞 Bug Fixes
|
|
14
24
|
|
|
15
25
|
* Fixed `Record.descendants` and `Record.allDescendants` getters that were incorrectly returning the
|
|
16
26
|
parent record itself. Now only the descendants are returned, as expected.
|
|
27
|
+
* ⚠️ Potentially Breaking Change: apps relying on the previous behavior may need to adjust their code
|
|
28
|
+
to account for the parent record no longer being included in the results. Tree mode checkbox
|
|
29
|
+
grids are one example of a component that may be affected by this change.
|
|
17
30
|
* Fixed `Grid` regression where pinned columns were automatically un-pinned when the viewport became
|
|
18
31
|
too small to accommodate them.
|
|
19
32
|
* Fixed bug where `Grid` context-menus would lose focus when rendered inside `Overlay` components.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { HoistInputProps } from '@xh/hoist/cmp/input';
|
|
2
2
|
import { HoistProps, HSide, LayoutProps, StyleProps } from '@xh/hoist/core';
|
|
3
3
|
import '@xh/hoist/desktop/register';
|
|
4
|
+
import { ZeroPad } from '@xh/hoist/format';
|
|
4
5
|
import { KeyboardEventHandler, ReactElement, ReactNode, Ref } from 'react';
|
|
5
6
|
export interface NumberInputProps extends HoistProps, LayoutProps, StyleProps, HoistInputProps {
|
|
6
7
|
value?: number;
|
|
@@ -56,8 +57,8 @@ export interface NumberInputProps extends HoistProps, LayoutProps, StyleProps, H
|
|
|
56
57
|
* Can be used to append e.g. "%" or a unit without need for an external right label.
|
|
57
58
|
*/
|
|
58
59
|
valueLabel?: string;
|
|
59
|
-
/**
|
|
60
|
-
zeroPad?:
|
|
60
|
+
/** @see NumberFormatOptions.zeroPad */
|
|
61
|
+
zeroPad?: ZeroPad;
|
|
61
62
|
}
|
|
62
63
|
/**
|
|
63
64
|
* Number input, with optional support for formatting of display value, shorthand units, and more.
|
|
@@ -1,54 +1,72 @@
|
|
|
1
1
|
import Numbro from 'numbro';
|
|
2
2
|
import { CSSProperties, ReactNode } from 'react';
|
|
3
|
+
import { IntRange } from 'type-fest';
|
|
3
4
|
import { FormatOptions } from './FormatMisc';
|
|
5
|
+
export type Precision = 'auto' | IntRange<0, 13>;
|
|
6
|
+
export type ZeroPad = boolean | IntRange<1, 13>;
|
|
4
7
|
export interface NumberFormatOptions extends Omit<FormatOptions<number>, 'tooltip'> {
|
|
5
|
-
/** A valid numbro format object or string. */
|
|
6
|
-
formatConfig?: string | Numbro.Format;
|
|
7
|
-
/** Desired number of decimal places. */
|
|
8
|
-
precision?: number | 'auto';
|
|
9
|
-
/** True to pad with trailing zeros out to given precision. */
|
|
10
|
-
zeroPad?: boolean;
|
|
11
|
-
/** Optional display value for the input value 0. */
|
|
12
|
-
zeroDisplay?: ReactNode;
|
|
13
|
-
/** True to use ledger format. */
|
|
14
|
-
ledger?: boolean;
|
|
15
8
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
9
|
+
* Color output based on the sign of the value. True to use red/green/grey defaults, or provide
|
|
10
|
+
* an object with alternate CSS classes or properties.
|
|
18
11
|
*/
|
|
19
|
-
|
|
20
|
-
/** True to prepend positive numbers with a '+'. */
|
|
21
|
-
withPlusSign?: boolean;
|
|
12
|
+
colorSpec?: boolean | ColorSpec;
|
|
22
13
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* sign-based glyphs, '+/-' characters, and colors will not be shown. Default true.
|
|
14
|
+
* True to add placeholder after positive ledgers to ensure columns of mixed positive and
|
|
15
|
+
* negative numbers vertically align their digits, avoiding shift due to ")" on negative values.
|
|
26
16
|
*/
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
/** Set to true to omit comma if value has exactly 4 digits (i.e. 1500 instead of 1,500). */
|
|
33
|
-
omitFourDigitComma?: boolean;
|
|
34
|
-
/** Prefix to prepend to value (between the number and its sign). */
|
|
35
|
-
prefix?: string;
|
|
36
|
-
/** Label to append to value, or true to append a default label for the formattter
|
|
17
|
+
forceLedgerAlign?: boolean;
|
|
18
|
+
/** A valid numbro format object or string. */
|
|
19
|
+
formatConfig?: Numbro.Format | string;
|
|
20
|
+
/**
|
|
21
|
+
* Label to append to value, or true to append a default label for the formatter -
|
|
37
22
|
* e.g. 'm' for fmtMillions.
|
|
38
23
|
*/
|
|
39
24
|
label?: string | boolean;
|
|
40
25
|
/** CSS class of label span. */
|
|
41
26
|
labelCls?: string;
|
|
27
|
+
/** True to use ledger format. */
|
|
28
|
+
ledger?: boolean;
|
|
42
29
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
30
|
+
* Set to true to omit thousands-separator comma if value is to be formatted as a whole number
|
|
31
|
+
* with exactly 4 digits (e.g. 1,500).
|
|
45
32
|
*/
|
|
46
|
-
|
|
33
|
+
omitFourDigitComma?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Desired number of decimal places, or 'auto' (default) to adjust the displayed precision
|
|
36
|
+
* automatically based on the scale of the value.
|
|
37
|
+
*/
|
|
38
|
+
precision?: Precision;
|
|
39
|
+
/** Prefix to prepend to value (between the number and its sign). */
|
|
40
|
+
prefix?: string;
|
|
41
|
+
/**
|
|
42
|
+
* If set to false, small numbers that would show only digits of zero due to precision will be
|
|
43
|
+
* formatted as exactly zero. In particular, if a zeroDisplay is specified it will be used and
|
|
44
|
+
* sign-based glyphs, '+/-' characters, and colors will not be shown. Default true.
|
|
45
|
+
*/
|
|
46
|
+
strictZero?: boolean;
|
|
47
47
|
/**
|
|
48
48
|
* True to enable default tooltip with minimally formatted original value, or a function to
|
|
49
49
|
* generate a custom tooltip string.
|
|
50
50
|
*/
|
|
51
51
|
tooltip?: boolean | ((v: number) => string);
|
|
52
|
+
/** True to include comma delimiters. */
|
|
53
|
+
withCommas?: boolean;
|
|
54
|
+
/** True to prepend positive numbers with a '+'. */
|
|
55
|
+
withPlusSign?: boolean;
|
|
56
|
+
/** True to prepend an up / down arrow. */
|
|
57
|
+
withSignGlyph?: boolean;
|
|
58
|
+
/** Optional display value for the input value 0. */
|
|
59
|
+
zeroDisplay?: ReactNode;
|
|
60
|
+
/**
|
|
61
|
+
* True to pad with trailing zeros out to precision, false to skip adding any trailing zeroes.
|
|
62
|
+
* Can also be a number (lte precision) to specify a minimum number of trailing zeroes to add,
|
|
63
|
+
* without extending zero padding all the way out to full precision.
|
|
64
|
+
*
|
|
65
|
+
* e.g. `{precision:4, zeroPad:2}` will format `1.2` → "1.20" and `1.234` → "1.234"
|
|
66
|
+
*
|
|
67
|
+
* Default is true if a fixed precision is set, false if precision is 'auto'.
|
|
68
|
+
*/
|
|
69
|
+
zeroPad?: ZeroPad;
|
|
52
70
|
}
|
|
53
71
|
export interface QuantityFormatOptions extends NumberFormatOptions {
|
|
54
72
|
useMillions?: boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { HoistInputProps } from '@xh/hoist/cmp/input';
|
|
3
|
-
import { HoistProps,
|
|
3
|
+
import { HoistProps, HSide, LayoutProps, StyleProps } from '@xh/hoist/core';
|
|
4
|
+
import { ZeroPad } from '@xh/hoist/format';
|
|
4
5
|
import '@xh/hoist/mobile/register';
|
|
5
6
|
import './NumberInput.scss';
|
|
6
7
|
export interface NumberInputProps extends HoistProps, HoistInputProps, StyleProps, LayoutProps {
|
|
@@ -43,8 +44,8 @@ export interface NumberInputProps extends HoistProps, HoistInputProps, StyleProp
|
|
|
43
44
|
* Can be used to append e.g. "%" or a unit without need for an external right label.
|
|
44
45
|
*/
|
|
45
46
|
valueLabel?: string;
|
|
46
|
-
/**
|
|
47
|
-
zeroPad?:
|
|
47
|
+
/** @see NumberFormatOptions.zeroPad */
|
|
48
|
+
zeroPad?: ZeroPad;
|
|
48
49
|
}
|
|
49
50
|
/**
|
|
50
51
|
* Number input, with optional support for formatting of display value, shorthand units, and more.
|
package/cmp/ag-grid/AgGrid.scss
CHANGED
|
@@ -140,11 +140,14 @@
|
|
|
140
140
|
border-color: var(--xh-grid-group-border-color);
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
+
.ag-cell {
|
|
144
|
+
border-bottom: none;
|
|
145
|
+
border-top: none;
|
|
146
|
+
}
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
&--no-row-borders {
|
|
146
|
-
.ag-row
|
|
147
|
-
.ag-cell {
|
|
150
|
+
.ag-row {
|
|
148
151
|
border-bottom: none;
|
|
149
152
|
border-top: none;
|
|
150
153
|
}
|
|
@@ -249,6 +252,7 @@
|
|
|
249
252
|
&--cell-borders {
|
|
250
253
|
.ag-cell {
|
|
251
254
|
border-right-color: var(--xh-grid-border-color);
|
|
255
|
+
border-bottom-color: var(--xh-grid-border-color);
|
|
252
256
|
}
|
|
253
257
|
|
|
254
258
|
.ag-row {
|
|
@@ -193,14 +193,15 @@ export class Exception {
|
|
|
193
193
|
}) as FetchException;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
private static createInternal(attributes: PlainObject, baseError
|
|
196
|
+
private static createInternal(attributes: PlainObject, baseError?: Error) {
|
|
197
|
+
const {message, ...rest} = attributes;
|
|
197
198
|
return Object.assign(
|
|
198
|
-
baseError,
|
|
199
|
+
baseError ?? new Error(message),
|
|
199
200
|
{
|
|
200
201
|
isRoutine: false,
|
|
201
202
|
isHoistException: true
|
|
202
203
|
},
|
|
203
|
-
|
|
204
|
+
rest
|
|
204
205
|
) as HoistException;
|
|
205
206
|
}
|
|
206
207
|
}
|
|
@@ -8,7 +8,7 @@ import composeRefs from '@seznam/compose-react-refs';
|
|
|
8
8
|
import {HoistInputModel, HoistInputProps, useHoistInputModel} from '@xh/hoist/cmp/input';
|
|
9
9
|
import {hoistCmp, HoistProps, HSide, LayoutProps, StyleProps} from '@xh/hoist/core';
|
|
10
10
|
import '@xh/hoist/desktop/register';
|
|
11
|
-
import {fmtNumber, parseNumber} from '@xh/hoist/format';
|
|
11
|
+
import {fmtNumber, parseNumber, Precision, ZeroPad} from '@xh/hoist/format';
|
|
12
12
|
import {numericInput} from '@xh/hoist/kit/blueprint';
|
|
13
13
|
import {wait} from '@xh/hoist/promise';
|
|
14
14
|
import {TEST_ID, throwIf, withDefault} from '@xh/hoist/utils/js';
|
|
@@ -90,8 +90,8 @@ export interface NumberInputProps extends HoistProps, LayoutProps, StyleProps, H
|
|
|
90
90
|
*/
|
|
91
91
|
valueLabel?: string;
|
|
92
92
|
|
|
93
|
-
/**
|
|
94
|
-
zeroPad?:
|
|
93
|
+
/** @see NumberFormatOptions.zeroPad */
|
|
94
|
+
zeroPad?: ZeroPad;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/**
|
|
@@ -129,14 +129,14 @@ class NumberInputModel extends HoistInputModel {
|
|
|
129
129
|
throwIf(Math.log10(this.scaleFactor) % 1 !== 0, 'scaleFactor must be a factor of 10');
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
get precision(): number {
|
|
133
|
-
return withDefault(this.componentProps.precision, 4);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
132
|
override get commitOnChange(): boolean {
|
|
137
133
|
return withDefault(this.componentProps.commitOnChange, false);
|
|
138
134
|
}
|
|
139
135
|
|
|
136
|
+
get precision(): number {
|
|
137
|
+
return withDefault(this.componentProps.precision, 4);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
140
|
get scaleFactor(): number {
|
|
141
141
|
return withDefault(this.componentProps.scaleFactor, 1);
|
|
142
142
|
}
|
|
@@ -206,7 +206,7 @@ class NumberInputModel extends HoistInputModel {
|
|
|
206
206
|
const {valueLabel, displayWithCommas} = componentProps,
|
|
207
207
|
zeroPad = withDefault(componentProps.zeroPad, false),
|
|
208
208
|
formattedVal = fmtNumber(value, {
|
|
209
|
-
precision,
|
|
209
|
+
precision: precision as Precision,
|
|
210
210
|
zeroPad,
|
|
211
211
|
label: valueLabel,
|
|
212
212
|
labelCls: null,
|
package/format/FormatNumber.ts
CHANGED
|
@@ -5,10 +5,20 @@
|
|
|
5
5
|
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {span} from '@xh/hoist/cmp/layout';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
defaults,
|
|
10
|
+
isBoolean,
|
|
11
|
+
isFinite,
|
|
12
|
+
isFunction,
|
|
13
|
+
isInteger,
|
|
14
|
+
isNil,
|
|
15
|
+
isNumber,
|
|
16
|
+
isString
|
|
17
|
+
} from 'lodash';
|
|
9
18
|
import Numbro from 'numbro';
|
|
10
19
|
import numbro from 'numbro';
|
|
11
20
|
import {CSSProperties, ReactNode} from 'react';
|
|
21
|
+
import {IntRange} from 'type-fest';
|
|
12
22
|
import {fmtSpan, FormatOptions} from './FormatMisc';
|
|
13
23
|
import {createRenderer} from './FormatUtils';
|
|
14
24
|
import {saveOriginal} from './impl/Utils';
|
|
@@ -28,69 +38,87 @@ const UP_TICK = '▴',
|
|
|
28
38
|
neutral: 'xh-neutral-val'
|
|
29
39
|
};
|
|
30
40
|
|
|
41
|
+
export type Precision = 'auto' | IntRange<0, 13>;
|
|
42
|
+
export type ZeroPad = boolean | IntRange<1, 13>;
|
|
43
|
+
|
|
31
44
|
export interface NumberFormatOptions extends Omit<FormatOptions<number>, 'tooltip'> {
|
|
32
|
-
/**
|
|
33
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Color output based on the sign of the value. True to use red/green/grey defaults, or provide
|
|
47
|
+
* an object with alternate CSS classes or properties.
|
|
48
|
+
*/
|
|
49
|
+
colorSpec?: boolean | ColorSpec;
|
|
34
50
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
51
|
+
/**
|
|
52
|
+
* True to add placeholder after positive ledgers to ensure columns of mixed positive and
|
|
53
|
+
* negative numbers vertically align their digits, avoiding shift due to ")" on negative values.
|
|
54
|
+
*/
|
|
55
|
+
forceLedgerAlign?: boolean;
|
|
37
56
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
57
|
+
/** A valid numbro format object or string. */
|
|
58
|
+
formatConfig?: Numbro.Format | string;
|
|
40
59
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Label to append to value, or true to append a default label for the formatter -
|
|
62
|
+
* e.g. 'm' for fmtMillions.
|
|
63
|
+
*/
|
|
64
|
+
label?: string | boolean;
|
|
65
|
+
|
|
66
|
+
/** CSS class of label span. */
|
|
67
|
+
labelCls?: string;
|
|
43
68
|
|
|
44
69
|
/** True to use ledger format. */
|
|
45
70
|
ledger?: boolean;
|
|
46
71
|
|
|
47
72
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
73
|
+
* Set to true to omit thousands-separator comma if value is to be formatted as a whole number
|
|
74
|
+
* with exactly 4 digits (e.g. 1,500).
|
|
50
75
|
*/
|
|
51
|
-
|
|
76
|
+
omitFourDigitComma?: boolean;
|
|
52
77
|
|
|
53
|
-
/**
|
|
54
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Desired number of decimal places, or 'auto' (default) to adjust the displayed precision
|
|
80
|
+
* automatically based on the scale of the value.
|
|
81
|
+
*/
|
|
82
|
+
precision?: Precision;
|
|
83
|
+
|
|
84
|
+
/** Prefix to prepend to value (between the number and its sign). */
|
|
85
|
+
prefix?: string;
|
|
55
86
|
|
|
56
87
|
/**
|
|
57
88
|
* If set to false, small numbers that would show only digits of zero due to precision will be
|
|
58
89
|
* formatted as exactly zero. In particular, if a zeroDisplay is specified it will be used and
|
|
59
|
-
* sign-based glyphs, '+/-' characters, and colors will not be shown.
|
|
90
|
+
* sign-based glyphs, '+/-' characters, and colors will not be shown. Default true.
|
|
60
91
|
*/
|
|
61
92
|
strictZero?: boolean;
|
|
62
93
|
|
|
63
|
-
/**
|
|
64
|
-
|
|
94
|
+
/**
|
|
95
|
+
* True to enable default tooltip with minimally formatted original value, or a function to
|
|
96
|
+
* generate a custom tooltip string.
|
|
97
|
+
*/
|
|
98
|
+
tooltip?: boolean | ((v: number) => string);
|
|
65
99
|
|
|
66
100
|
/** True to include comma delimiters. */
|
|
67
101
|
withCommas?: boolean;
|
|
68
102
|
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/** Prefix to prepend to value (between the number and its sign). */
|
|
73
|
-
prefix?: string;
|
|
74
|
-
|
|
75
|
-
/** Label to append to value, or true to append a default label for the formattter
|
|
76
|
-
* e.g. 'm' for fmtMillions.
|
|
77
|
-
*/
|
|
78
|
-
label?: string | boolean;
|
|
103
|
+
/** True to prepend positive numbers with a '+'. */
|
|
104
|
+
withPlusSign?: boolean;
|
|
79
105
|
|
|
80
|
-
/**
|
|
81
|
-
|
|
106
|
+
/** True to prepend an up / down arrow. */
|
|
107
|
+
withSignGlyph?: boolean;
|
|
82
108
|
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
* an object with alternate CSS classes or properties.
|
|
86
|
-
*/
|
|
87
|
-
colorSpec?: boolean | ColorSpec;
|
|
109
|
+
/** Optional display value for the input value 0. */
|
|
110
|
+
zeroDisplay?: ReactNode;
|
|
88
111
|
|
|
89
112
|
/**
|
|
90
|
-
* True to
|
|
91
|
-
*
|
|
113
|
+
* True to pad with trailing zeros out to precision, false to skip adding any trailing zeroes.
|
|
114
|
+
* Can also be a number (lte precision) to specify a minimum number of trailing zeroes to add,
|
|
115
|
+
* without extending zero padding all the way out to full precision.
|
|
116
|
+
*
|
|
117
|
+
* e.g. `{precision:4, zeroPad:2}` will format `1.2` → "1.20" and `1.234` → "1.234"
|
|
118
|
+
*
|
|
119
|
+
* Default is true if a fixed precision is set, false if precision is 'auto'.
|
|
92
120
|
*/
|
|
93
|
-
|
|
121
|
+
zeroPad?: ZeroPad;
|
|
94
122
|
}
|
|
95
123
|
|
|
96
124
|
export interface QuantityFormatOptions extends NumberFormatOptions {
|
|
@@ -126,8 +154,8 @@ export function fmtNumber(v: number, opts?: NumberFormatOptions): ReactNode {
|
|
|
126
154
|
nullDisplay = '',
|
|
127
155
|
zeroDisplay = null,
|
|
128
156
|
formatConfig = null,
|
|
129
|
-
precision
|
|
130
|
-
zeroPad
|
|
157
|
+
precision,
|
|
158
|
+
zeroPad,
|
|
131
159
|
ledger = false,
|
|
132
160
|
forceLedgerAlign = true,
|
|
133
161
|
withPlusSign = false,
|
|
@@ -142,10 +170,13 @@ export function fmtNumber(v: number, opts?: NumberFormatOptions): ReactNode {
|
|
|
142
170
|
tooltip = null,
|
|
143
171
|
asHtml = false,
|
|
144
172
|
originalValue = v
|
|
145
|
-
} = opts ?? {};
|
|
146
|
-
|
|
173
|
+
} = opts ?? ({} as NumberFormatOptions);
|
|
147
174
|
if (isInvalidInput(v)) return nullDisplay;
|
|
148
175
|
|
|
176
|
+
// Ensure any non-int precision is treated as 'auto', use to default zeroPad.
|
|
177
|
+
if (!isInteger(precision)) precision = 'auto';
|
|
178
|
+
if (isNil(zeroPad)) zeroPad = precision != 'auto';
|
|
179
|
+
|
|
149
180
|
formatConfig =
|
|
150
181
|
formatConfig || buildFormatConfig(v, precision, zeroPad, withCommas, omitFourDigitComma);
|
|
151
182
|
const str = numbro(v).format(formatConfig).replace('-', '');
|
|
@@ -429,41 +460,83 @@ function calcStyleFromColorSpec(v: number, colorSpec: ColorSpec | boolean): CSSP
|
|
|
429
460
|
return !isString(possibleStyles) ? possibleStyles : {};
|
|
430
461
|
}
|
|
431
462
|
|
|
432
|
-
function buildFormatConfig(
|
|
433
|
-
|
|
463
|
+
function buildFormatConfig(
|
|
464
|
+
v: number,
|
|
465
|
+
precisionSpec: Precision,
|
|
466
|
+
zeroPad: ZeroPad,
|
|
467
|
+
withCommas: boolean,
|
|
468
|
+
omitFourDigitComma: boolean
|
|
469
|
+
): Numbro.Format {
|
|
470
|
+
const absVal = Math.abs(v),
|
|
471
|
+
config: Numbro.Format = {};
|
|
472
|
+
|
|
473
|
+
let precision: number;
|
|
474
|
+
if (precisionSpec === 'auto') {
|
|
475
|
+
// Auto-precision - base on scale of number
|
|
476
|
+
if (absVal === 0) {
|
|
477
|
+
precision = 2;
|
|
478
|
+
} else if (absVal < 0.01) {
|
|
479
|
+
precision = 6;
|
|
480
|
+
} else if (absVal < 100) {
|
|
481
|
+
precision = 4;
|
|
482
|
+
} else if (absVal < 10000) {
|
|
483
|
+
precision = 2;
|
|
484
|
+
} else {
|
|
485
|
+
precision = 0;
|
|
486
|
+
}
|
|
487
|
+
} else {
|
|
488
|
+
// Fixed precision - use requested, capped at max.
|
|
489
|
+
precision = precisionSpec < MAX_NUMERIC_PRECISION ? precisionSpec : MAX_NUMERIC_PRECISION;
|
|
490
|
+
}
|
|
434
491
|
|
|
435
|
-
|
|
436
|
-
|
|
492
|
+
// If zeroPad gte precision, treat as `true` to pad out to (but not beyond) full precision.
|
|
493
|
+
// We don't support applying some precision (rounding) then padding out zeroes after that.
|
|
494
|
+
if (isNumber(zeroPad) && zeroPad >= precision) {
|
|
495
|
+
zeroPad = true;
|
|
496
|
+
}
|
|
437
497
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
498
|
+
// Calculate numbro mantissa and trimMantissa options based on precision and zeroPad settings.
|
|
499
|
+
if (isNumber(zeroPad)) {
|
|
500
|
+
// Specific zeroPad set - need to read actual precision of number to determine exact
|
|
501
|
+
// mantissa, as we cannot use trimMantissa (since we do want to allow some trailing zeroes).
|
|
502
|
+
const actualPrecision = countDecimalPlaces(absVal);
|
|
503
|
+
|
|
504
|
+
// How much precision do we actually want/need? Lower of actual vs. precision set above.
|
|
505
|
+
// Ensures we respect a deliberately low precision spec, but otherwise include only as
|
|
506
|
+
// much precision as we need to show the value.
|
|
507
|
+
const requiredPrecision = Math.min(actualPrecision, precision);
|
|
508
|
+
|
|
509
|
+
// Then set mantissa to higher of required precision vs. requested zeroPad.
|
|
510
|
+
// Ensures we display all of the requested/available precision + extra zeros if needed.
|
|
511
|
+
config.mantissa = Math.max(requiredPrecision, zeroPad);
|
|
512
|
+
config.trimMantissa = false;
|
|
441
513
|
} else {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
mantissa = 4;
|
|
448
|
-
} else if (num < 10000) {
|
|
449
|
-
mantissa = 2;
|
|
450
|
-
} else {
|
|
451
|
-
mantissa = 0;
|
|
452
|
-
}
|
|
514
|
+
// Without a specific (numeric) zeroPad set, we can set mantissa to precision then
|
|
515
|
+
// optionally enable trimMantissa option to remove trailing zeroes if requested.
|
|
516
|
+
// No need to measure actual precision of number.
|
|
517
|
+
config.mantissa = precision;
|
|
518
|
+
config.trimMantissa = !zeroPad && precision != 0;
|
|
453
519
|
}
|
|
454
520
|
|
|
521
|
+
// Apply comma-separation unless contradicted by omitFourDigitComma, which should apply only to
|
|
522
|
+
// whole number values between 1000 and 9999, where we are not displaying any decimal places.
|
|
455
523
|
config.thousandSeparated =
|
|
456
524
|
withCommas &&
|
|
457
525
|
!(
|
|
458
526
|
omitFourDigitComma &&
|
|
459
|
-
|
|
460
|
-
(mantissa == 0 || (!zeroPad && Number.isInteger(
|
|
527
|
+
absVal < 10000 &&
|
|
528
|
+
(config.mantissa == 0 || (!zeroPad && Number.isInteger(absVal)))
|
|
461
529
|
);
|
|
462
|
-
|
|
463
|
-
config.trimMantissa = !zeroPad && mantissa != 0;
|
|
530
|
+
|
|
464
531
|
return config;
|
|
465
532
|
}
|
|
466
533
|
|
|
534
|
+
function countDecimalPlaces(number: number): number {
|
|
535
|
+
const numStr = number.toString(),
|
|
536
|
+
dpIdx = numStr.indexOf('.');
|
|
537
|
+
return dpIdx === -1 ? 0 : numStr.length - dpIdx - 1;
|
|
538
|
+
}
|
|
539
|
+
|
|
467
540
|
function isInvalidInput(v) {
|
|
468
541
|
return v == null || v === '';
|
|
469
542
|
}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {HoistInputModel, HoistInputProps, useHoistInputModel} from '@xh/hoist/cmp/input';
|
|
8
|
-
import {hoistCmp, HoistProps,
|
|
9
|
-
import {fmtNumber} from '@xh/hoist/format';
|
|
8
|
+
import {hoistCmp, HoistProps, HSide, LayoutProps, StyleProps} from '@xh/hoist/core';
|
|
9
|
+
import {fmtNumber, Precision, ZeroPad} from '@xh/hoist/format';
|
|
10
10
|
import {input} from '@xh/hoist/kit/onsen';
|
|
11
11
|
import '@xh/hoist/mobile/register';
|
|
12
12
|
import {wait} from '@xh/hoist/promise';
|
|
@@ -68,8 +68,8 @@ export interface NumberInputProps extends HoistProps, HoistInputProps, StyleProp
|
|
|
68
68
|
*/
|
|
69
69
|
valueLabel?: string;
|
|
70
70
|
|
|
71
|
-
/**
|
|
72
|
-
zeroPad?:
|
|
71
|
+
/** @see NumberFormatOptions.zeroPad */
|
|
72
|
+
zeroPad?: ZeroPad;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
@@ -97,15 +97,15 @@ class NumberInputModel extends HoistInputModel {
|
|
|
97
97
|
throwIf(Math.log10(this.scaleFactor) % 1 !== 0, 'scaleFactor must be a factor of 10');
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
get precision() {
|
|
101
|
-
return withDefault(this.componentProps.precision, 4);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
100
|
override get commitOnChange() {
|
|
105
101
|
return withDefault(this.componentProps.commitOnChange, false);
|
|
106
102
|
}
|
|
107
103
|
|
|
108
|
-
get
|
|
104
|
+
get precision(): number {
|
|
105
|
+
return withDefault(this.componentProps.precision, 4);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
get scaleFactor(): number {
|
|
109
109
|
return withDefault(this.componentProps.scaleFactor, 1);
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -195,7 +195,7 @@ class NumberInputModel extends HoistInputModel {
|
|
|
195
195
|
const {valueLabel, displayWithCommas} = componentProps,
|
|
196
196
|
zeroPad = withDefault(componentProps.zeroPad, false),
|
|
197
197
|
formattedVal = fmtNumber(value, {
|
|
198
|
-
precision,
|
|
198
|
+
precision: precision as Precision,
|
|
199
199
|
zeroPad,
|
|
200
200
|
label: valueLabel,
|
|
201
201
|
labelCls: null,
|