@shopbb/helium 0.3.0 → 0.3.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Money.d.ts","sourceRoot":"","sources":["../../src/components/Money.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC;IACnE,cAAc;IACd,IAAI,EAAE,SAAS,CAAC;IAChB,oBAAoB;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iBAAiB;IACjB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mBAAmB;IACnB,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,iBAAiB,CAAC;IACjC,gCAAgC;IAChC,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC;
|
|
1
|
+
{"version":3,"file":"Money.d.ts","sourceRoot":"","sources":["../../src/components/Money.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC;IACnE,cAAc;IACd,IAAI,EAAE,SAAS,CAAC;IAChB,oBAAoB;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iBAAiB;IACjB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mBAAmB;IACnB,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,iBAAiB,CAAC;IACjC,gCAAgC;IAChC,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC;AAwCD,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,2CA6CtC"}
|
package/dist/components/Money.js
CHANGED
|
@@ -1,41 +1,59 @@
|
|
|
1
1
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
/**
|
|
3
|
+
* currency → symbol。手写一张表,不用 Intl.NumberFormat —— 因为它在 Cloudflare Workers V8
|
|
4
|
+
* 和浏览器 V8 之间输出可能不同(货币符号位置、本地化分隔符),导致 SSR hydration mismatch。
|
|
5
|
+
*
|
|
6
|
+
* Shopify Hydrogen 的 <Money> 用同款手写方案,原因相同。
|
|
7
|
+
*/
|
|
8
|
+
const CURRENCY_SYMBOL = {
|
|
9
|
+
CNY: '¥',
|
|
10
|
+
USD: '$',
|
|
11
|
+
EUR: '€',
|
|
12
|
+
GBP: '£',
|
|
13
|
+
JPY: '¥',
|
|
14
|
+
HKD: 'HK$',
|
|
15
|
+
KRW: '₩',
|
|
11
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* 按 1000 分组加 thousand-separator。
|
|
19
|
+
* 中文/英文 locale 都是逗号;其他 locale 暂用逗号(如需 . 分隔,加 locale 表)。
|
|
20
|
+
*/
|
|
21
|
+
function formatAmount(amount, minDecimals, maxDecimals) {
|
|
22
|
+
if (!Number.isFinite(amount))
|
|
23
|
+
return '0';
|
|
24
|
+
const negative = amount < 0;
|
|
25
|
+
const abs = Math.abs(amount);
|
|
26
|
+
// 强制小数位数
|
|
27
|
+
const fixed = abs.toFixed(maxDecimals);
|
|
28
|
+
const [intPart, decPart = ''] = fixed.split('.');
|
|
29
|
+
// 加千分位
|
|
30
|
+
const intWithSep = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
31
|
+
// 去掉多余的尾零(保留最低 minDecimals 位)
|
|
32
|
+
let trimmedDec = decPart;
|
|
33
|
+
while (trimmedDec.length > minDecimals && trimmedDec.endsWith('0')) {
|
|
34
|
+
trimmedDec = trimmedDec.slice(0, -1);
|
|
35
|
+
}
|
|
36
|
+
const out = trimmedDec ? `${intWithSep}.${trimmedDec}` : intWithSep;
|
|
37
|
+
return negative ? `-${out}` : out;
|
|
38
|
+
}
|
|
12
39
|
export function Money(props) {
|
|
13
|
-
const { data, withoutCurrency, withoutTrailingZeros, locale:
|
|
40
|
+
const { data, withoutCurrency, withoutTrailingZeros, locale: _localeProp, as, measurement, ...rest } = props;
|
|
14
41
|
const Tag = as || 'span';
|
|
15
|
-
|
|
16
|
-
const locale = localeProp ||
|
|
17
|
-
shop?.locale ||
|
|
18
|
-
DEFAULT_LOCALE_BY_CURRENCY[data.currencyCode] ||
|
|
19
|
-
'en-US';
|
|
42
|
+
// locale 现在不影响输出(避免 SSR 不一致)。保留 prop 给未来可能扩展。
|
|
20
43
|
const amount = Number(data.amount);
|
|
44
|
+
const minDec = withoutTrailingZeros && Number.isInteger(amount) ? 0 : 2;
|
|
45
|
+
const numStr = formatAmount(amount, minDec, 2);
|
|
46
|
+
const symbol = CURRENCY_SYMBOL[data.currencyCode];
|
|
21
47
|
let formatted;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
: {
|
|
29
|
-
style: 'currency',
|
|
30
|
-
currency: data.currencyCode,
|
|
31
|
-
minimumFractionDigits: withoutTrailingZeros && Number.isInteger(amount) ? 0 : 2,
|
|
32
|
-
maximumFractionDigits: 2,
|
|
33
|
-
};
|
|
34
|
-
formatted = new Intl.NumberFormat(locale, opts).format(amount);
|
|
48
|
+
if (withoutCurrency) {
|
|
49
|
+
formatted = numStr;
|
|
50
|
+
}
|
|
51
|
+
else if (symbol) {
|
|
52
|
+
formatted = `${symbol}${numStr}`;
|
|
35
53
|
}
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
formatted =
|
|
54
|
+
else {
|
|
55
|
+
// 未识别 currency code: 用 "USD 99.00" 形式
|
|
56
|
+
formatted = `${data.currencyCode} ${numStr}`;
|
|
39
57
|
}
|
|
40
58
|
// measurement suffix(unit price)
|
|
41
59
|
let suffix = '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Money.js","sourceRoot":"","sources":["../../src/components/Money.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"Money.js","sourceRoot":"","sources":["../../src/components/Money.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/components/Money.tsx
CHANGED
|
@@ -43,53 +43,70 @@ export interface MoneyProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
43
43
|
measurement?: MoneyMeasurement;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
/**
|
|
47
|
+
* currency → symbol。手写一张表,不用 Intl.NumberFormat —— 因为它在 Cloudflare Workers V8
|
|
48
|
+
* 和浏览器 V8 之间输出可能不同(货币符号位置、本地化分隔符),导致 SSR hydration mismatch。
|
|
49
|
+
*
|
|
50
|
+
* Shopify Hydrogen 的 <Money> 用同款手写方案,原因相同。
|
|
51
|
+
*/
|
|
52
|
+
const CURRENCY_SYMBOL: Record<string, string> = {
|
|
53
|
+
CNY: '¥',
|
|
54
|
+
USD: '$',
|
|
55
|
+
EUR: '€',
|
|
56
|
+
GBP: '£',
|
|
57
|
+
JPY: '¥',
|
|
58
|
+
HKD: 'HK$',
|
|
59
|
+
KRW: '₩',
|
|
54
60
|
};
|
|
55
61
|
|
|
62
|
+
/**
|
|
63
|
+
* 按 1000 分组加 thousand-separator。
|
|
64
|
+
* 中文/英文 locale 都是逗号;其他 locale 暂用逗号(如需 . 分隔,加 locale 表)。
|
|
65
|
+
*/
|
|
66
|
+
function formatAmount(amount: number, minDecimals: number, maxDecimals: number): string {
|
|
67
|
+
if (!Number.isFinite(amount)) return '0';
|
|
68
|
+
const negative = amount < 0;
|
|
69
|
+
const abs = Math.abs(amount);
|
|
70
|
+
// 强制小数位数
|
|
71
|
+
const fixed = abs.toFixed(maxDecimals);
|
|
72
|
+
const [intPart, decPart = ''] = fixed.split('.');
|
|
73
|
+
// 加千分位
|
|
74
|
+
const intWithSep = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
75
|
+
// 去掉多余的尾零(保留最低 minDecimals 位)
|
|
76
|
+
let trimmedDec = decPart;
|
|
77
|
+
while (trimmedDec.length > minDecimals && trimmedDec.endsWith('0')) {
|
|
78
|
+
trimmedDec = trimmedDec.slice(0, -1);
|
|
79
|
+
}
|
|
80
|
+
const out = trimmedDec ? `${intWithSep}.${trimmedDec}` : intWithSep;
|
|
81
|
+
return negative ? `-${out}` : out;
|
|
82
|
+
}
|
|
83
|
+
|
|
56
84
|
export function Money(props: MoneyProps) {
|
|
57
85
|
const {
|
|
58
86
|
data,
|
|
59
87
|
withoutCurrency,
|
|
60
88
|
withoutTrailingZeros,
|
|
61
|
-
locale:
|
|
89
|
+
locale: _localeProp,
|
|
62
90
|
as,
|
|
63
91
|
measurement,
|
|
64
92
|
...rest
|
|
65
93
|
} = props;
|
|
66
94
|
|
|
67
95
|
const Tag: any = as || 'span';
|
|
68
|
-
|
|
69
|
-
const locale =
|
|
70
|
-
localeProp ||
|
|
71
|
-
shop?.locale ||
|
|
72
|
-
DEFAULT_LOCALE_BY_CURRENCY[data.currencyCode] ||
|
|
73
|
-
'en-US';
|
|
96
|
+
// locale 现在不影响输出(避免 SSR 不一致)。保留 prop 给未来可能扩展。
|
|
74
97
|
const amount = Number(data.amount);
|
|
98
|
+
const minDec = withoutTrailingZeros && Number.isInteger(amount) ? 0 : 2;
|
|
99
|
+
const numStr = formatAmount(amount, minDec, 2);
|
|
100
|
+
const symbol = CURRENCY_SYMBOL[data.currencyCode];
|
|
75
101
|
|
|
76
102
|
let formatted: string;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
style: 'currency',
|
|
85
|
-
currency: data.currencyCode,
|
|
86
|
-
minimumFractionDigits: withoutTrailingZeros && Number.isInteger(amount) ? 0 : 2,
|
|
87
|
-
maximumFractionDigits: 2,
|
|
88
|
-
};
|
|
89
|
-
formatted = new Intl.NumberFormat(locale, opts).format(amount);
|
|
90
|
-
} catch {
|
|
91
|
-
// Fallback:Intl 不支持的 currencyCode
|
|
92
|
-
formatted = withoutCurrency ? amount.toFixed(2) : `${data.currencyCode} ${amount.toFixed(2)}`;
|
|
103
|
+
if (withoutCurrency) {
|
|
104
|
+
formatted = numStr;
|
|
105
|
+
} else if (symbol) {
|
|
106
|
+
formatted = `${symbol}${numStr}`;
|
|
107
|
+
} else {
|
|
108
|
+
// 未识别 currency code: 用 "USD 99.00" 形式
|
|
109
|
+
formatted = `${data.currencyCode} ${numStr}`;
|
|
93
110
|
}
|
|
94
111
|
|
|
95
112
|
// measurement suffix(unit price)
|