@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 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
- /** True to pad with trailing zeros out to precision, default false. */
60
- zeroPad?: boolean;
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
- * True to add placeholder after positive ledgers to align vertically with negative ledgers
17
- * in columns.
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
- forceLedgerAlign?: boolean;
20
- /** True to prepend positive numbers with a '+'. */
21
- withPlusSign?: boolean;
12
+ colorSpec?: boolean | ColorSpec;
22
13
  /**
23
- * If set to false, small numbers that would show only digits of zero due to precision will be
24
- * formatted as exactly zero. In particular, if a zeroDisplay is specified it will be used and
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
- strictZero?: boolean;
28
- /** True to prepend an up / down arrow. */
29
- withSignGlyph?: boolean;
30
- /** True to include comma delimiters. */
31
- withCommas?: boolean;
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
- * Color output based on the sign of the value. True to use red/green/grey defaults, or provide
44
- * an object with alternate CSS classes or properties.
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
- colorSpec?: boolean | ColorSpec;
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, StyleProps, LayoutProps, HSide } from '@xh/hoist/core';
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
- /** True to pad with trailing zeros out to precision, default false. */
47
- zeroPad?: boolean;
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.
@@ -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: Error = new Error()) {
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
- attributes
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
- /** True to pad with trailing zeros out to precision, default false. */
94
- zeroPad?: boolean;
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,
@@ -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 {defaults, isBoolean, isFinite, isFunction, isNil, isString} from 'lodash';
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
- /** A valid numbro format object or string. */
33
- formatConfig?: string | Numbro.Format;
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
- /** Desired number of decimal places. */
36
- precision?: number | 'auto';
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
- /** True to pad with trailing zeros out to given precision. */
39
- zeroPad?: boolean;
57
+ /** A valid numbro format object or string. */
58
+ formatConfig?: Numbro.Format | string;
40
59
 
41
- /** Optional display value for the input value 0. */
42
- zeroDisplay?: ReactNode;
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
- * True to add placeholder after positive ledgers to align vertically with negative ledgers
49
- * in columns.
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
- forceLedgerAlign?: boolean;
76
+ omitFourDigitComma?: boolean;
52
77
 
53
- /** True to prepend positive numbers with a '+'. */
54
- withPlusSign?: boolean;
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. Default true.
90
+ * sign-based glyphs, '+/-' characters, and colors will not be shown. Default true.
60
91
  */
61
92
  strictZero?: boolean;
62
93
 
63
- /** True to prepend an up / down arrow. */
64
- withSignGlyph?: boolean;
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
- /** Set to true to omit comma if value has exactly 4 digits (i.e. 1500 instead of 1,500). */
70
- omitFourDigitComma?: boolean;
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
- /** CSS class of label span. */
81
- labelCls?: string;
106
+ /** True to prepend an up / down arrow. */
107
+ withSignGlyph?: boolean;
82
108
 
83
- /**
84
- * Color output based on the sign of the value. True to use red/green/grey defaults, or provide
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 enable default tooltip with minimally formatted original value, or a function to
91
- * generate a custom tooltip string.
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
- tooltip?: boolean | ((v: number) => string);
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 = 'auto',
130
- zeroPad = precision != 'auto',
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(v, precision, zeroPad, withCommas, omitFourDigitComma): Numbro.Format {
433
- const num = Math.abs(v);
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
- const config: Numbro.Format = {};
436
- let mantissa = undefined;
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
- if (precision % 1 === 0) {
439
- precision = precision < MAX_NUMERIC_PRECISION ? precision : MAX_NUMERIC_PRECISION;
440
- mantissa = precision === 0 ? 0 : precision;
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
- if (num === 0) {
443
- mantissa = 2;
444
- } else if (num < 0.01) {
445
- mantissa = 6;
446
- } else if (num < 100) {
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
- num < 10000 &&
460
- (mantissa == 0 || (!zeroPad && Number.isInteger(num)))
527
+ absVal < 10000 &&
528
+ (config.mantissa == 0 || (!zeroPad && Number.isInteger(absVal)))
461
529
  );
462
- config.mantissa = mantissa;
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, StyleProps, LayoutProps, HSide} from '@xh/hoist/core';
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
- /** True to pad with trailing zeros out to precision, default false. */
72
- zeroPad?: boolean;
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 scaleFactor() {
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "66.1.0",
3
+ "version": "66.1.1",
4
4
  "description": "Hoist add-on for building and deploying React Applications.",
5
5
  "repository": "github:xh/hoist-react",
6
6
  "homepage": "https://xh.io",