dev-api-ui 0.1.7 → 0.1.9
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 +2 -1
- package/ui/components/HubPage.tsx +8 -2
- package/ui/format.ts +59 -0
- package/ui/index.ts +3 -0
package/package.json
CHANGED
|
@@ -70,6 +70,12 @@ const GROUP_CONFIG: Record<string, GroupConfig> = {
|
|
|
70
70
|
|
|
71
71
|
type Row = { sym: string; name: string; last: string; delta: string; up: boolean; status: string; cat: string; slug: string };
|
|
72
72
|
|
|
73
|
+
// Route slug derived from the symbol so links always follow the house lowercase-hyphen convention
|
|
74
|
+
// (EUR/USD → eur-usd), matching every vertical's /<group>/<base>-<quote> routes. Used by BOTH the
|
|
75
|
+
// leaf "Popular" cards and the list/search RowTable — the hand-maintained `slug` field shipped
|
|
76
|
+
// un-hyphenated demo values (eurusd) that 404'd (BUG-3). Slash-less symbols (PJM-RT) just lowercase.
|
|
77
|
+
const routeSlug = (sym: string) => sym.toLowerCase().replace('/', '-');
|
|
78
|
+
|
|
73
79
|
const GROUP_ROWS: Record<string, Row[]> = {
|
|
74
80
|
fx: [
|
|
75
81
|
{ sym: 'EUR/USD', name: 'Euro / US Dollar', last: '1.08642', delta: '+0.31%', up: true, status: 'Live', cat: 'Majors', slug: 'eurusd' },
|
|
@@ -145,7 +151,7 @@ function deltaColor(up: boolean): string {
|
|
|
145
151
|
|
|
146
152
|
// The shared table / list layout used in both list and search modes
|
|
147
153
|
function RowTable({ rows, group }: { rows: Row[]; group: string }) {
|
|
148
|
-
const slugFor = (r: Row) => r.
|
|
154
|
+
const slugFor = (r: Row) => routeSlug(r.sym);
|
|
149
155
|
return (
|
|
150
156
|
<div
|
|
151
157
|
style={{
|
|
@@ -374,7 +380,7 @@ export default function HubPage({ group, description, pairs: pairsOverride, eyeb
|
|
|
374
380
|
{popular.map((p) => (
|
|
375
381
|
<Link
|
|
376
382
|
key={p.sym}
|
|
377
|
-
href={`/${group}/${p.
|
|
383
|
+
href={`/${group}/${routeSlug(p.sym)}`}
|
|
378
384
|
style={{
|
|
379
385
|
display: 'block',
|
|
380
386
|
background: 'var(--surface)',
|
package/ui/format.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Currency/number formatting helpers backed by Intl.NumberFormat + CLDR.
|
|
3
|
+
* Zero-decimal currencies (JPY, KRW, VND, …) render without fractional digits
|
|
4
|
+
* automatically — no lookup table required.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns the correct maximumFractionDigits for a currency code, or 2 on error.
|
|
9
|
+
* Uses a throw-away Intl.NumberFormat so we never duplicate CLDR data.
|
|
10
|
+
*/
|
|
11
|
+
function fractionDigits(currencyCode: string, locale: string): number {
|
|
12
|
+
try {
|
|
13
|
+
return new Intl.NumberFormat(locale, {
|
|
14
|
+
style: 'currency',
|
|
15
|
+
currency: currencyCode,
|
|
16
|
+
}).resolvedOptions().maximumFractionDigits ?? 2;
|
|
17
|
+
} catch {
|
|
18
|
+
return 2;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Formats `value` as a localised currency string including the symbol/code.
|
|
24
|
+
* Uses Intl.NumberFormat so minor-unit digit count is correct per ISO-4217
|
|
25
|
+
* (JPY → 0 decimals, USD → 2, BHD → 3, etc.).
|
|
26
|
+
* Falls back to a plain number string if `currencyCode` is invalid.
|
|
27
|
+
*/
|
|
28
|
+
export function formatCurrencyAmount(
|
|
29
|
+
value: number,
|
|
30
|
+
currencyCode: string,
|
|
31
|
+
locale = 'en-US',
|
|
32
|
+
): string {
|
|
33
|
+
try {
|
|
34
|
+
return new Intl.NumberFormat(locale, {
|
|
35
|
+
style: 'currency',
|
|
36
|
+
currency: currencyCode,
|
|
37
|
+
}).format(value);
|
|
38
|
+
} catch {
|
|
39
|
+
return new Intl.NumberFormat(locale).format(value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Formats `value` with the correct decimal precision for `currencyCode` but
|
|
45
|
+
* WITHOUT a currency symbol — for callers that render their own prefix/suffix.
|
|
46
|
+
* Falls back to 2 decimal places if `currencyCode` is invalid.
|
|
47
|
+
*/
|
|
48
|
+
export function formatCurrencyNumber(
|
|
49
|
+
value: number,
|
|
50
|
+
currencyCode: string,
|
|
51
|
+
locale = 'en-US',
|
|
52
|
+
): string {
|
|
53
|
+
const digits = fractionDigits(currencyCode, locale);
|
|
54
|
+
return new Intl.NumberFormat(locale, {
|
|
55
|
+
style: 'decimal',
|
|
56
|
+
minimumFractionDigits: digits,
|
|
57
|
+
maximumFractionDigits: digits,
|
|
58
|
+
}).format(value);
|
|
59
|
+
}
|
package/ui/index.ts
CHANGED
|
@@ -48,3 +48,6 @@ export { default as StatusMark } from './components/StatusMark';
|
|
|
48
48
|
// The illustrative SERIES_DATA blob stays internal (demo-only, not public API).
|
|
49
49
|
export { getSeriesData } from './data/series';
|
|
50
50
|
export type { RangeEntry, RelatedEntry, ReadingEntry, SeriesData } from './data/series';
|
|
51
|
+
|
|
52
|
+
// Formatting helpers
|
|
53
|
+
export { formatCurrencyAmount, formatCurrencyNumber } from './format';
|