@hanzo/ui 4.3.8 → 4.4.0
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/package.json +1 -1
- package/primitives/tooltip.tsx +19 -1
- package/util/format-and-abbreviate-as-currency.ts +119 -0
- package/util/format-to-max-char.ts +5 -20
- package/util/index.ts +7 -1
- package/util/number-abbreviate.ts +17 -68
package/package.json
CHANGED
package/primitives/tooltip.tsx
CHANGED
|
@@ -27,4 +27,22 @@ const TooltipContent = React.forwardRef<
|
|
|
27
27
|
))
|
|
28
28
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
const TooltipWrapper: React.FC<{
|
|
31
|
+
text: string
|
|
32
|
+
tooltipClx?: string
|
|
33
|
+
} & React.PropsWithChildren> = ({
|
|
34
|
+
children,
|
|
35
|
+
text,
|
|
36
|
+
tooltipClx=''
|
|
37
|
+
}) => (
|
|
38
|
+
<Tooltip>
|
|
39
|
+
<TooltipTrigger asChild>
|
|
40
|
+
{children}
|
|
41
|
+
</TooltipTrigger>
|
|
42
|
+
<TooltipContent className={tooltipClx}>
|
|
43
|
+
{text}
|
|
44
|
+
</TooltipContent>
|
|
45
|
+
</Tooltip>
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, TooltipWrapper }
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import Abbr, { type QuantityAbbrSymbol, ABBR_SYMBOLS_ARRAY } from './number-abbreviate'
|
|
2
|
+
|
|
3
|
+
interface FormatThreshold {
|
|
4
|
+
from: number
|
|
5
|
+
use: QuantityAbbrSymbol
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const formatAndAbbreviateAsCurrency = (
|
|
9
|
+
n: number | null,
|
|
10
|
+
thresholds: FormatThreshold[] = [{
|
|
11
|
+
from: 1000000000,
|
|
12
|
+
use: 'M'
|
|
13
|
+
}],
|
|
14
|
+
/**
|
|
15
|
+
* Chars that will be added by ui if the number is rounded.
|
|
16
|
+
* For example, if the desired output for 10.15 is "~10.1",
|
|
17
|
+
* the tilda counts as 1 char.
|
|
18
|
+
*/
|
|
19
|
+
roundingAdds: number = 1,
|
|
20
|
+
maxDecimal: number = 2
|
|
21
|
+
): {
|
|
22
|
+
full: string
|
|
23
|
+
result: string
|
|
24
|
+
change: 'rounded' | 'none' | 'abbr' | 'empty'
|
|
25
|
+
} => {
|
|
26
|
+
if (n === null) {
|
|
27
|
+
return {
|
|
28
|
+
full: '',
|
|
29
|
+
result: '',
|
|
30
|
+
change: 'empty'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const usdFormatter = Intl.NumberFormat('en-US', {
|
|
35
|
+
style: 'currency',
|
|
36
|
+
currency: 'USD',
|
|
37
|
+
minimumFractionDigits: 0
|
|
38
|
+
})
|
|
39
|
+
const formatted = usdFormatter.format(n)
|
|
40
|
+
|
|
41
|
+
if (n < thresholds[0].from) {
|
|
42
|
+
return {
|
|
43
|
+
full: formatted,
|
|
44
|
+
result: formatted,
|
|
45
|
+
change: 'none'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Get operative FormatThreshold pair...
|
|
50
|
+
let threshold: FormatThreshold
|
|
51
|
+
for (
|
|
52
|
+
let i = 0, threshold = thresholds[0];
|
|
53
|
+
i < thresholds.length, n >= thresholds[i].from;
|
|
54
|
+
i++
|
|
55
|
+
) {}
|
|
56
|
+
|
|
57
|
+
// Build up units array to all units
|
|
58
|
+
// up to threshold.use
|
|
59
|
+
const units: QuantityAbbrSymbol[] = []
|
|
60
|
+
for (let i = 0; i < ABBR_SYMBOLS_ARRAY.length; i++) {
|
|
61
|
+
const current = ABBR_SYMBOLS_ARRAY[i]
|
|
62
|
+
units.push(current)
|
|
63
|
+
if (current === threshold!.use) {
|
|
64
|
+
break
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const abbreviator = new Abbr(units)
|
|
69
|
+
|
|
70
|
+
// Use thresholdFrom as a guide to how many chars are available
|
|
71
|
+
// first digit + comma = 2
|
|
72
|
+
// Possible trailing cents: '.xx'.length = 3
|
|
73
|
+
// 3 - 2 = 1
|
|
74
|
+
const charsAvail = usdFormatter.format(threshold!.from).length + 1
|
|
75
|
+
const abbr = abbreviator.abbreviate(n, charsAvail) // arbitrary, but good approx
|
|
76
|
+
const numStr = abbr.slice(0, -1)
|
|
77
|
+
const abbreviation = abbr.slice(-1)
|
|
78
|
+
const numerical = parseFloat(numStr)
|
|
79
|
+
|
|
80
|
+
const integral = Math.floor(numerical)
|
|
81
|
+
const integralString = usdFormatter.format(integral)
|
|
82
|
+
const commas = integralString.split(',').length - 1
|
|
83
|
+
|
|
84
|
+
// minus abbr, dec point, dollar sign, and roundingAdds / tilda,
|
|
85
|
+
// (1 + 1 + 1 + roundingAdds)
|
|
86
|
+
// ("precision" does NOT include the decimal point itself,
|
|
87
|
+
// so we have to explicitly factor it in.)
|
|
88
|
+
const roundedString = numerical.toPrecision(charsAvail - commas - (3 + roundingAdds))
|
|
89
|
+
// remove trailing zeros, if any
|
|
90
|
+
const roundedNumerical = parseFloat(roundedString)
|
|
91
|
+
const roundedIntegral = Math.trunc(roundedNumerical)
|
|
92
|
+
const roundedIntegralString = usdFormatter.format(roundedIntegral)
|
|
93
|
+
|
|
94
|
+
let decimalPortion = roundedNumerical - roundedIntegral
|
|
95
|
+
let result
|
|
96
|
+
if (decimalPortion !== 0) {
|
|
97
|
+
// remove trailing zeros if any
|
|
98
|
+
decimalPortion = parseFloat(decimalPortion.toFixed(maxDecimal))
|
|
99
|
+
const decimalPortionString = decimalPortion.toString()
|
|
100
|
+
const afterDecimalString = decimalPortionString.slice(decimalPortionString.indexOf('.') + 1)
|
|
101
|
+
result = roundedIntegralString + '.' + afterDecimalString + abbreviation
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
result = roundedIntegralString + abbreviation
|
|
105
|
+
}
|
|
106
|
+
// Did we lose any precision?
|
|
107
|
+
const rounded = (roundedIntegral + decimalPortion !== n)
|
|
108
|
+
return {
|
|
109
|
+
full: formatted,
|
|
110
|
+
result,
|
|
111
|
+
change: rounded ? 'rounded' : 'abbr'
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export {
|
|
116
|
+
formatAndAbbreviateAsCurrency as default,
|
|
117
|
+
type FormatThreshold,
|
|
118
|
+
type QuantityAbbrSymbol
|
|
119
|
+
}
|
|
@@ -2,29 +2,14 @@ import Abbr from './number-abbreviate'
|
|
|
2
2
|
|
|
3
3
|
const abbr = new Abbr(['K', 'M', 'B', 'T'])
|
|
4
4
|
|
|
5
|
-
const isPowerOf10 = (n: number) => {
|
|
6
|
-
if (n <= 0) {
|
|
7
|
-
return false
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
while (n > 1) {
|
|
11
|
-
if (n % 10 !== 0) {
|
|
12
|
-
return false
|
|
13
|
-
}
|
|
14
|
-
n /= 10
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return true
|
|
18
|
-
}
|
|
19
|
-
|
|
20
5
|
const formatToMaxChar = (
|
|
21
6
|
n: number | null,
|
|
22
7
|
maxChars: number,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Chars that will be added by ui if the number is rounded.
|
|
10
|
+
* For example, if the desired output for 10.15 is "~10.1",
|
|
11
|
+
* the tilda counts as 1 char.
|
|
12
|
+
*/
|
|
28
13
|
roundingAdds: number = 1
|
|
29
14
|
): {
|
|
30
15
|
result: string
|
package/util/index.ts
CHANGED
|
@@ -67,6 +67,12 @@ export const capitalize = (str: string): string => (
|
|
|
67
67
|
|
|
68
68
|
export { default as spreadToTransform } from './spread-to-transform'
|
|
69
69
|
export { default as formatToMaxChar } from './format-to-max-char'
|
|
70
|
-
|
|
70
|
+
export {
|
|
71
|
+
default as formatAndAbbreviateAsCurrency,
|
|
72
|
+
type FormatThreshold,
|
|
73
|
+
type QuantityAbbrSymbol
|
|
74
|
+
} from './format-and-abbreviate-as-currency'
|
|
75
|
+
|
|
76
|
+
// Must be imported from 'use client', so can't include this...
|
|
71
77
|
// export * from './step-animation'
|
|
72
78
|
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
// cf: https://github.com/domharrington/js-number-abbreviate/blob/master/index.js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
type QuantityAbbrSymbol = 'K' | 'M' | 'B' | 'T'
|
|
4
|
+
const ABBR_SYMBOLS_ARRAY = ['K', 'M', 'B', 'T'] satisfies QuantityAbbrSymbol[]
|
|
4
5
|
|
|
5
6
|
class NumberAbbreviator {
|
|
6
7
|
|
|
7
|
-
private _units:
|
|
8
|
+
private _units: QuantityAbbrSymbol[]
|
|
8
9
|
|
|
9
|
-
constructor(units?:
|
|
10
|
-
this._units = units ??
|
|
10
|
+
constructor(units?: QuantityAbbrSymbol[]) {
|
|
11
|
+
this._units = units ?? ABBR_SYMBOLS_ARRAY
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
private _abbreviate = (
|
|
14
|
+
private _abbreviate = (
|
|
15
|
+
n: number,
|
|
16
|
+
decPlaces: number,
|
|
17
|
+
log: boolean = false
|
|
18
|
+
): string => {
|
|
14
19
|
|
|
15
20
|
const _decPlaces = Math.pow(10, decPlaces)
|
|
16
21
|
let _n = n
|
|
@@ -31,70 +36,14 @@ class NumberAbbreviator {
|
|
|
31
36
|
return _n.toString() + (_unit ?? '')
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
abbreviate = (n: number, decPlaces: number) => {
|
|
35
|
-
const abbreviatedNumber = this._abbreviate(Math.abs(n), decPlaces
|
|
39
|
+
abbreviate = (n: number, decPlaces: number, log: boolean = false) => {
|
|
40
|
+
const abbreviatedNumber = this._abbreviate(Math.abs(n), decPlaces, log)
|
|
36
41
|
return n < 0 ? '-' + abbreviatedNumber : abbreviatedNumber
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
'use strict';
|
|
46
|
-
|
|
47
|
-
function NumberAbbreviate() {
|
|
48
|
-
var units
|
|
49
|
-
if (!(this instanceof NumberAbbreviate)) {
|
|
50
|
-
// function usage: abbrev(n, decPlaces, units)
|
|
51
|
-
var n = arguments[0]
|
|
52
|
-
var decPlaces = arguments[1]
|
|
53
|
-
units = arguments[2]
|
|
54
|
-
var ab = new NumberAbbreviate(units)
|
|
55
|
-
return ab.abbreviate(n, decPlaces)
|
|
56
|
-
}
|
|
57
|
-
// class usage: new NumberAbbreviate(units)
|
|
58
|
-
units = arguments[0]
|
|
59
|
-
this.units = units == null ? ['k', 'm', 'b', 't'] : units
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
NumberAbbreviate.prototype._abbreviate = function(number, decPlaces) {
|
|
63
|
-
decPlaces = Math.pow(10, decPlaces)
|
|
64
|
-
|
|
65
|
-
for (var i = this.units.length - 1; i >= 0; i--) {
|
|
66
|
-
|
|
67
|
-
var size = Math.pow(10, (i + 1) * 3)
|
|
68
|
-
|
|
69
|
-
if (size <= number) {
|
|
70
|
-
number = Math.round(number * decPlaces / size) / decPlaces
|
|
71
|
-
|
|
72
|
-
if ((number === 1000) && (i < this.units.length - 1)) {
|
|
73
|
-
number = 1
|
|
74
|
-
i++
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
number += this.units[i]
|
|
78
|
-
|
|
79
|
-
break
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return number
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
NumberAbbreviate.prototype.abbreviate = function(number, decPlaces) {
|
|
87
|
-
var isNegative = number < 0
|
|
88
|
-
var abbreviatedNumber = this._abbreviate(Math.abs(number), decPlaces || 0)
|
|
89
|
-
|
|
90
|
-
return isNegative ? '-' + abbreviatedNumber : abbreviatedNumber;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (typeof module !== 'undefined' && module.exports) {
|
|
94
|
-
module.exports = NumberAbbreviate
|
|
95
|
-
} else {
|
|
96
|
-
root.NumberAbbreviate = NumberAbbreviate
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
})(this);
|
|
100
|
-
*/
|
|
45
|
+
export {
|
|
46
|
+
type QuantityAbbrSymbol,
|
|
47
|
+
ABBR_SYMBOLS_ARRAY,
|
|
48
|
+
NumberAbbreviator as default
|
|
49
|
+
}
|