mirror of
https://github.com/pezkuwichain/pezkuwi-common.git
synced 2026-10-31 14:29:48 +00:00
Initial rebrand: @polkadot -> @pezkuwi (14 packages)
- Package namespace: @polkadot/* -> @pezkuwi/* - Repository: polkadot-js/common -> pezkuwichain/pezkuwi-common - Author: Pezkuwi Team <team@pezkuwichain.io> Core packages: - @pezkuwi/util (utilities) - @pezkuwi/util-crypto (crypto primitives) - @pezkuwi/keyring (account management) - @pezkuwi/networks (chain metadata) - @pezkuwi/hw-ledger (Ledger hardware wallet) - @pezkuwi/x-* (10 polyfill packages) Total: 14 packages Upstream: polkadot-js/common v14.0.1
This commit is contained in:
commit
ec06da0ebc
687 files changed
+48096
No files matched your search
@@ -0,0 +1,296 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { BN } from '../bn/index.js';
|
||||
import { formatBalance } from './index.js';
|
||||
|
||||
const FMT_DEFAULTS = {
|
||||
decimals: 0,
|
||||
unit: 'Unit'
|
||||
};
|
||||
|
||||
describe('formatBalance', (): void => {
|
||||
const TESTVAL = new BN('123456789000');
|
||||
|
||||
// We mess around with the global setDefaults inside some tests,
|
||||
// ensure we restore it to the defaults straight after each run
|
||||
afterEach((): void => {
|
||||
formatBalance.setDefaults(FMT_DEFAULTS);
|
||||
});
|
||||
|
||||
it('returns options for dropdown', (): void => {
|
||||
formatBalance.setDefaults({ decimals: 0, unit: 'TEST' });
|
||||
|
||||
expect(
|
||||
formatBalance.getOptions()
|
||||
).toEqual([
|
||||
{ power: 0, text: 'TEST', value: '-' },
|
||||
{ power: 3, text: 'Kilo', value: 'k' },
|
||||
{ power: 6, text: 'Mill', value: 'M' },
|
||||
{ power: 9, text: 'Bill', value: 'B' },
|
||||
{ power: 12, text: 'Tril', value: 'T' },
|
||||
{ power: 15, text: 'Peta', value: 'P' },
|
||||
{ power: 18, text: 'Exa', value: 'E' },
|
||||
{ power: 21, text: 'Zeta', value: 'Z' },
|
||||
{ power: 24, text: 'Yotta', value: 'Y' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('can set defaults from array values', (): void => {
|
||||
formatBalance.setDefaults({ decimals: [12, 24], unit: ['Multi', 'Unit'] });
|
||||
|
||||
expect(
|
||||
formatBalance(TESTVAL)
|
||||
).toEqual('123.4567 mMulti');
|
||||
});
|
||||
|
||||
describe('SI formatting', (): void => {
|
||||
it('formats empty to 0', (): void => {
|
||||
expect(formatBalance()).toEqual('0');
|
||||
expect(formatBalance('0')).toEqual('0');
|
||||
});
|
||||
|
||||
// this is after an issue/test from actual values with forceUnit
|
||||
it('formats 1000 (BN) (decimals = 12, withAll, withZero = false)', (): void => {
|
||||
expect(
|
||||
formatBalance(new BN(1000), { decimals: 12, forceUnit: '-', withAll: true, withSi: false, withZero: true })
|
||||
).toEqual('0.000000001000');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=15)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 15, withSi: true })
|
||||
).toEqual('123.4567 µUnit');
|
||||
});
|
||||
|
||||
it('formats 123,456 (decimals=0)', (): void => {
|
||||
expect(
|
||||
formatBalance(123456, { decimals: 0, withSi: true })
|
||||
).toEqual('123.4560 kUnit');
|
||||
});
|
||||
|
||||
it('formats BigInt numbers', (): void => {
|
||||
expect(
|
||||
formatBalance(123456789000n, { decimals: 15, withSi: true })
|
||||
).toEqual('123.4567 µUnit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=15, old style)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 15, withSi: true })
|
||||
).toEqual('123.4567 µUnit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=10, withAll=true)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 10, forceUnit: '-', withAll: true, withSi: true })
|
||||
).toEqual('12.3456789000 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=10, withAll=true, withZero=false)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 10, forceUnit: '-', withAll: true, withSi: true, withZero: false })
|
||||
).toEqual('12.3456789 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,000,000,000 (decimals=9, withAll=true, withZero=false)', (): void => {
|
||||
expect(
|
||||
formatBalance('123000000000', { decimals: 9, forceUnit: '-', withAll: true, withSi: true, withZero: false })
|
||||
).toEqual('123 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=15, Compact)', (): void => {
|
||||
const compact = {
|
||||
something: 'else',
|
||||
toBn: (): BN => TESTVAL,
|
||||
unwrap: (): BN => TESTVAL
|
||||
};
|
||||
|
||||
expect(
|
||||
formatBalance(compact, { decimals: 15, withSi: true })
|
||||
).toEqual('123.4567 µUnit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=12)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 12, withSi: true })
|
||||
).toEqual('123.4567 mUnit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=9)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 9, withSi: true })
|
||||
).toEqual('123.4567 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=9, no unit)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 9, withSi: true, withUnit: false })
|
||||
).toEqual('123.4567');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=9, unit given)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 9, withSi: true, withUnit: 'FOO' })
|
||||
).toEqual('123.4567 FOO');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=12, no SI)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 12, withSi: false })
|
||||
).toEqual('123.4567');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=12, full SI)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 12, withSiFull: true })
|
||||
).toEqual('123.4567 milli Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=12, full SI, no unit)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 12, withSiFull: true, withUnit: false })
|
||||
).toEqual('123.4567 milli');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=9, full SI)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 9, withSiFull: true })
|
||||
).toEqual('123.4567 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=6)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 6, withSi: true })
|
||||
).toEqual('123.4567 kUnit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=6, unit specified)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 6, withSi: true, withUnit: 'BAR' })
|
||||
).toEqual('123.4567 kBAR');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=0, unit specified)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 0, withSi: true, withUnit: 'BAR' })
|
||||
).toEqual('123.4567 BBAR');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 * 10 (decimals=12)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL.muln(10), { decimals: 12, withSi: true })
|
||||
).toEqual('1.2345 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 * 100 (decimals=12)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL.muln(100), { decimals: 12, withSi: true })
|
||||
).toEqual('12.3456 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 * 1000 (decimals=12)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL.muln(1000), { decimals: 12, withSi: true })
|
||||
).toEqual('123.4567 Unit');
|
||||
});
|
||||
|
||||
it('formats -123,456,789,000 (decimals=15)', (): void => {
|
||||
expect(
|
||||
formatBalance(new BN('-123456789000'), { decimals: 15, withSi: true })
|
||||
).toEqual('-123.4567 µUnit');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Forced formatting', (): void => {
|
||||
it('formats 123,456,789,000 (decimals=12, forceUnit=base)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 12, forceUnit: '-' })
|
||||
).toEqual('0.1234 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=9, forceUnit=base)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 9, forceUnit: '-' })
|
||||
).toEqual('123.4567 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=7, forceUnit=base)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 7, forceUnit: '-' })
|
||||
).toEqual('12,345.6789 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=15, forceUnit=micro)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 15, forceUnit: 'µ' })
|
||||
).toEqual('123.4567 µUnit');
|
||||
});
|
||||
|
||||
it('formats 123,456 (decimals=0, locale=sl)', (): void => {
|
||||
expect(
|
||||
formatBalance(123456, { decimals: 0, locale: 'sl', withSi: true })
|
||||
).toEqual('123,4560 kUnit');
|
||||
});
|
||||
|
||||
it('formats BigInt numbers (locale=sl)', (): void => {
|
||||
expect(
|
||||
formatBalance(123456789000n, { decimals: 15, locale: 'sl', withSi: true })
|
||||
).toEqual('123,4567 µUnit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=7, forceUnit=base locale=sl)', (): void => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, { decimals: 7, forceUnit: '-', locale: 'sl' })
|
||||
).toEqual('12.345,6789 Unit');
|
||||
});
|
||||
});
|
||||
|
||||
describe('calcSi', (): void => {
|
||||
it('exposes calcSi on formatBalance', (): void => {
|
||||
expect(
|
||||
formatBalance.calcSi('12345').value
|
||||
).toEqual('k');
|
||||
});
|
||||
});
|
||||
|
||||
describe('findSi', (): void => {
|
||||
it('finds the SI value', (): void => {
|
||||
expect(
|
||||
formatBalance.findSi('k')
|
||||
).toEqual({ power: 3, text: 'Kilo', value: 'k' });
|
||||
});
|
||||
|
||||
it('returns default on not found', (): void => {
|
||||
expect(
|
||||
formatBalance.findSi('blah')
|
||||
).toEqual({ power: 0, text: 'Unit', value: '-' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaults', (): void => {
|
||||
it('returns defaults', (): void => {
|
||||
expect(
|
||||
formatBalance.getDefaults()
|
||||
).toEqual(FMT_DEFAULTS);
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (defaultDecimals=12)', (): void => {
|
||||
formatBalance.setDefaults({ decimals: 12 });
|
||||
|
||||
expect(
|
||||
formatBalance(TESTVAL)
|
||||
).toEqual('123.4567 mUnit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (defaultUnit=TEST)', (): void => {
|
||||
formatBalance.setDefaults({ decimals: 12, unit: 'TEST' });
|
||||
|
||||
expect(
|
||||
formatBalance(TESTVAL)
|
||||
).toEqual('123.4567 mTEST');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '../bn/bn.js';
|
||||
import type { SiDef, ToBn } from '../types.js';
|
||||
|
||||
import { bnToBn } from '../bn/toBn.js';
|
||||
import { isBoolean } from '../is/boolean.js';
|
||||
import { formatDecimal } from './formatDecimal.js';
|
||||
import { getSeparator } from './getSeparator.js';
|
||||
import { calcSi, findSi, SI, SI_MID } from './si.js';
|
||||
|
||||
interface Defaults {
|
||||
decimals: number;
|
||||
unit: string;
|
||||
}
|
||||
|
||||
interface SetDefaults {
|
||||
decimals?: number[] | number;
|
||||
unit?: string[] | string;
|
||||
}
|
||||
|
||||
interface Options {
|
||||
/**
|
||||
* @description The number of decimals
|
||||
*/
|
||||
decimals?: number;
|
||||
/**
|
||||
* @description Format the number with this specific unit
|
||||
*/
|
||||
forceUnit?: string;
|
||||
/**
|
||||
* @description Returns value using all available decimals
|
||||
*/
|
||||
withAll?: boolean;
|
||||
/**
|
||||
* @description Format with SI, i.e. m/M/etc. (default = true)
|
||||
*/
|
||||
withSi?: boolean;
|
||||
/**
|
||||
* @description Format with full SI, i.e. mili/Mega/etc.
|
||||
*/
|
||||
withSiFull?: boolean;
|
||||
/**
|
||||
* @description Add the unit (useful in Balance formats)
|
||||
*/
|
||||
withUnit?: boolean | string;
|
||||
/**
|
||||
* @description Returns all trailing zeros, otherwise removes (default = true)
|
||||
*/
|
||||
withZero?: boolean;
|
||||
/**
|
||||
* @description The locale to use
|
||||
*/
|
||||
locale?: string;
|
||||
}
|
||||
|
||||
interface BalanceFormatter {
|
||||
<ExtToBn extends ToBn> (input?: number | string | BN | bigint | ExtToBn, options?: Options): string;
|
||||
calcSi (text: string, decimals?: number): SiDef;
|
||||
findSi (type: string): SiDef;
|
||||
getDefaults (): Defaults;
|
||||
getOptions (decimals?: number): SiDef[];
|
||||
setDefaults (defaults: SetDefaults): void;
|
||||
}
|
||||
|
||||
const DEFAULT_DECIMALS = 0;
|
||||
const DEFAULT_UNIT = SI[SI_MID].text;
|
||||
|
||||
let defaultDecimals = DEFAULT_DECIMALS;
|
||||
let defaultUnit = DEFAULT_UNIT;
|
||||
|
||||
// Formats a string/number with <prefix>.<postfix><type> notation
|
||||
function _formatBalance <ExtToBn extends ToBn> (input?: number | string | BN | bigint | ExtToBn, { decimals = defaultDecimals, forceUnit, locale = 'en', withAll = false, withSi = true, withSiFull = false, withUnit = true, withZero = true }: Options = {}): string {
|
||||
// we only work with string inputs here - convert anything
|
||||
// into the string-only value
|
||||
let text = bnToBn(input).toString();
|
||||
|
||||
if (text.length === 0 || text === '0') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
// strip the negative sign so we can work with clean groupings, re-add this in the
|
||||
// end when we return the result (from here on we work with positive numbers)
|
||||
let sign = '';
|
||||
|
||||
if (text[0].startsWith('-')) {
|
||||
sign = '-';
|
||||
text = text.substring(1);
|
||||
}
|
||||
|
||||
// We start at midpoint (8) minus 1 - this means that values display as
|
||||
// 123.4567 instead of 0.1234 k (so we always have the most relevant).
|
||||
const si = calcSi(text, decimals, forceUnit);
|
||||
const mid = text.length - (decimals + si.power);
|
||||
const pre = mid <= 0 ? '0' : text.substring(0, mid);
|
||||
|
||||
// get the post from the midpoint onward and then first add max decimals
|
||||
// before trimming to the correct (calculated) amount of decimals again
|
||||
let post = text
|
||||
.padStart(mid < 0 ? decimals : 1, '0')
|
||||
.substring(mid < 0 ? 0 : mid)
|
||||
.padEnd(withAll ? Math.max(decimals, 4) : 4, '0')
|
||||
.substring(0, withAll ? Math.max(4, decimals + si.power) : 4);
|
||||
|
||||
// remove all trailing 0's (if required via flag)
|
||||
if (!withZero) {
|
||||
let end = post.length - 1;
|
||||
|
||||
// This looks inefficient, however it is better to do the checks and
|
||||
// only make one final slice than it is to do it in multiples
|
||||
do {
|
||||
if (post[end] === '0') {
|
||||
end--;
|
||||
}
|
||||
} while (post[end] === '0');
|
||||
|
||||
post = post.substring(0, end + 1);
|
||||
}
|
||||
|
||||
// the display unit
|
||||
const unit = isBoolean(withUnit)
|
||||
? SI[SI_MID].text
|
||||
: withUnit;
|
||||
|
||||
// format the units for display based on the flags
|
||||
const units = withSi || withSiFull
|
||||
? si.value === '-'
|
||||
? withUnit
|
||||
? ` ${unit}`
|
||||
: ''
|
||||
: ` ${withSiFull ? `${si.text}${withUnit ? ' ' : ''}` : si.value}${withUnit ? unit : ''}`
|
||||
: '';
|
||||
|
||||
const { decimal, thousand } = getSeparator(locale);
|
||||
|
||||
return `${sign}${formatDecimal(pre, thousand)}${post && `${decimal}${post}`}${units}`;
|
||||
}
|
||||
|
||||
export const formatBalance = _formatBalance as BalanceFormatter;
|
||||
|
||||
formatBalance.calcSi = (text: string, decimals: number = defaultDecimals): SiDef =>
|
||||
calcSi(text, decimals);
|
||||
|
||||
formatBalance.findSi = findSi;
|
||||
|
||||
formatBalance.getDefaults = (): Defaults => {
|
||||
return {
|
||||
decimals: defaultDecimals,
|
||||
unit: defaultUnit
|
||||
};
|
||||
};
|
||||
|
||||
// get allowable options to display in a dropdown
|
||||
formatBalance.getOptions = (decimals: number = defaultDecimals): SiDef[] => {
|
||||
return SI.filter(({ power }): boolean =>
|
||||
power < 0
|
||||
? (decimals + power) >= 0
|
||||
: true
|
||||
);
|
||||
};
|
||||
|
||||
// Sets the default decimals to use for formatting (ui-wide)
|
||||
formatBalance.setDefaults = ({ decimals, unit }: SetDefaults): void => {
|
||||
defaultDecimals = (
|
||||
Array.isArray(decimals)
|
||||
? decimals[0]
|
||||
: decimals
|
||||
) ?? defaultDecimals;
|
||||
defaultUnit = (
|
||||
Array.isArray(unit)
|
||||
? unit[0]
|
||||
: unit
|
||||
) ?? defaultUnit;
|
||||
|
||||
SI[SI_MID].text = defaultUnit;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { formatDate } from './index.js';
|
||||
|
||||
describe('formatDate', (): void => {
|
||||
it('formats a known date into the correct format', (): void => {
|
||||
const date = new Date(Date.UTC(2020, 1, 15, 11, 14, 34));
|
||||
|
||||
// make this test-locale agnostic
|
||||
date.setTime(date.getTime() + date.getTimezoneOffset() * 60 * 1000);
|
||||
|
||||
expect(formatDate(date)).toEqual('2020-02-15 11:14:34');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/** @internal */
|
||||
function zeroPad (value: number): string {
|
||||
return value.toString().padStart(2, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* @name formatDate
|
||||
* @description Formats a date in CCYY-MM-DD HH:MM:SS format
|
||||
*/
|
||||
export function formatDate (date: Date): string {
|
||||
const year = date.getFullYear().toString();
|
||||
const month = zeroPad((date.getMonth() + 1));
|
||||
const day = zeroPad(date.getDate());
|
||||
const hour = zeroPad(date.getHours());
|
||||
const minute = zeroPad(date.getMinutes());
|
||||
const second = zeroPad(date.getSeconds());
|
||||
|
||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { formatDecimal } from './index.js';
|
||||
|
||||
describe('formatDecimal', (): void => {
|
||||
it('formats decimals in number groupings', (): void => {
|
||||
expect(formatDecimal('12345')).toEqual('12,345');
|
||||
});
|
||||
|
||||
it('formats decimal-only in number groupings', (): void => {
|
||||
expect(formatDecimal('test6789')).toEqual('6,789');
|
||||
});
|
||||
|
||||
it('returns input for non-decimal', (): void => {
|
||||
expect(formatDecimal('test')).toEqual('test');
|
||||
});
|
||||
|
||||
it('returns non-sensical negative text', (): void => {
|
||||
expect(formatDecimal('-test')).toEqual('-test');
|
||||
});
|
||||
|
||||
it('formats negative numbers', (): void => {
|
||||
expect(formatDecimal('-123456')).toEqual('-123,456');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// eslint-disable-next-line prefer-regex-literals
|
||||
const NUMBER_REGEX = new RegExp('(\\d+?)(?=(\\d{3})+(?!\\d)|$)', 'g');
|
||||
|
||||
/**
|
||||
* @name formatDecimal
|
||||
* @description Formats a number into string format with thousand separators
|
||||
*/
|
||||
export function formatDecimal (value: string, separator = ','): string {
|
||||
// We can do this by adjusting the regx, however for the sake of clarity
|
||||
// we rather strip and re-add the negative sign in the output
|
||||
const isNegative = value[0].startsWith('-');
|
||||
const matched = isNegative
|
||||
? value.substring(1).match(NUMBER_REGEX)
|
||||
: value.match(NUMBER_REGEX);
|
||||
|
||||
return matched
|
||||
? `${isNegative ? '-' : ''}${matched.join(separator)}`
|
||||
: value;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { BN } from '../bn/index.js';
|
||||
import { formatElapsed } from './index.js';
|
||||
|
||||
describe('formatElapsed', (): void => {
|
||||
const start = 12345678;
|
||||
const now = new Date(12345678);
|
||||
|
||||
it('formats a Date', (): void => {
|
||||
expect(
|
||||
formatElapsed(now, new Date(start + 9700))
|
||||
).toEqual('9.7s');
|
||||
});
|
||||
|
||||
it('formats a BN', (): void => {
|
||||
expect(
|
||||
formatElapsed(now, new BN(start + 42700))
|
||||
).toEqual('42s');
|
||||
});
|
||||
|
||||
it('formats a Compact', (): void => {
|
||||
expect(
|
||||
formatElapsed(now, {
|
||||
toBn: (): BN => new BN(start + (5.3 * 60000))
|
||||
})
|
||||
).toEqual('5m');
|
||||
});
|
||||
|
||||
it('formats a number', (): void => {
|
||||
expect(
|
||||
formatElapsed(now, start + (42 * 60 * 60000))
|
||||
).toEqual('42h');
|
||||
});
|
||||
|
||||
it('formats defaults', (): void => {
|
||||
expect(
|
||||
formatElapsed()
|
||||
).toEqual('0.0s');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '../bn/bn.js';
|
||||
import type { ToBn } from '../types.js';
|
||||
|
||||
import { bnToBn } from '../bn/toBn.js';
|
||||
|
||||
/** @internal */
|
||||
function formatValue (elapsed: number): string {
|
||||
if (elapsed < 15) {
|
||||
return `${elapsed.toFixed(1)}s`;
|
||||
} else if (elapsed < 60) {
|
||||
return `${elapsed | 0}s`;
|
||||
} else if (elapsed < 3600) {
|
||||
return `${elapsed / 60 | 0}m`;
|
||||
}
|
||||
|
||||
return `${elapsed / 3600 | 0}h`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name formatElapsed
|
||||
* @description Formats an elapsed value into s, m, h or day segments
|
||||
*/
|
||||
export function formatElapsed <ExtToBn extends ToBn> (now?: Date | null, value?: bigint | BN | ExtToBn | Date | number | null): string {
|
||||
const tsNow = now?.getTime() || 0;
|
||||
const tsValue = value instanceof Date
|
||||
? value.getTime()
|
||||
: bnToBn(value).toNumber();
|
||||
|
||||
return (tsNow && tsValue)
|
||||
? formatValue(Math.max(Math.abs(tsNow - tsValue), 0) / 1000)
|
||||
: '0.0s';
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { BN } from '../bn/index.js';
|
||||
import { formatNumber } from './index.js';
|
||||
|
||||
describe('formatNumber', (): void => {
|
||||
it('formats empty', (): void => {
|
||||
expect(
|
||||
formatNumber()
|
||||
).toEqual('0');
|
||||
});
|
||||
|
||||
it('formats negative numbers', (): void => {
|
||||
expect(
|
||||
formatNumber(-123456)
|
||||
).toEqual('-123,456');
|
||||
});
|
||||
|
||||
it('formats BN numbers', (): void => {
|
||||
expect(
|
||||
formatNumber(new BN(12345))
|
||||
).toEqual('12,345');
|
||||
});
|
||||
|
||||
it('formats BigInt numbers', (): void => {
|
||||
expect(
|
||||
formatNumber(123456789n)
|
||||
).toEqual('123,456,789');
|
||||
});
|
||||
|
||||
it('formats Compact<BN>', (): void => {
|
||||
expect(
|
||||
formatNumber({
|
||||
toBn: (): BN => new BN(12345),
|
||||
unwrap: (): BN => new BN(0)
|
||||
})
|
||||
).toEqual('12,345');
|
||||
});
|
||||
|
||||
it('formats negative numbers (locale=sl)', (): void => {
|
||||
expect(
|
||||
formatNumber(-123456, { locale: 'sl' })
|
||||
).toEqual('-123.456');
|
||||
});
|
||||
|
||||
it('formats BN numbers (locale=sl)', (): void => {
|
||||
expect(
|
||||
formatNumber(new BN(12345), { locale: 'sl' })
|
||||
).toEqual('12.345');
|
||||
});
|
||||
|
||||
it('formats BigInt numbers (locale=sl)', (): void => {
|
||||
expect(
|
||||
formatNumber(123456789n, { locale: 'sl' })
|
||||
).toEqual('123.456.789');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '../bn/bn.js';
|
||||
import type { ToBn } from '../types.js';
|
||||
|
||||
import { bnToBn } from '../bn/toBn.js';
|
||||
import { formatDecimal } from './formatDecimal.js';
|
||||
import { getSeparator } from './getSeparator.js';
|
||||
|
||||
interface Options {
|
||||
/**
|
||||
* @description The locale to use
|
||||
*/
|
||||
locale?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name formatNumber
|
||||
* @description Formats a number into string format with thousand separators
|
||||
*/
|
||||
export function formatNumber <ExtToBn extends ToBn> (
|
||||
value?: ExtToBn | BN | bigint | number | null,
|
||||
{ locale = 'en' }: Options = {}
|
||||
): string {
|
||||
const { thousand } = getSeparator(locale);
|
||||
|
||||
return formatDecimal(bnToBn(value).toString(), thousand);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import { getSeparator } from './getSeparator.js';
|
||||
|
||||
describe('getSeparator', (): void => {
|
||||
const normalizeSeparator = (separator: string) =>
|
||||
separator === '\u202F' ? ' ' : separator;
|
||||
|
||||
const testLocales = [
|
||||
{ decimal: '.', locale: 'en', thousand: ',' },
|
||||
{ decimal: '.', locale: 'en-gb', thousand: ',' },
|
||||
{ decimal: ',', locale: 'sl', thousand: '.' },
|
||||
{ decimal: ',', locale: 'sl-si', thousand: '.' },
|
||||
{ decimal: ',', locale: 'it-it', thousand: '.' },
|
||||
{ decimal: '.', locale: 'ja-jp', thousand: ',' },
|
||||
{ decimal: '.', locale: 'hi-IN', thousand: ',' },
|
||||
{ decimal: '.', locale: undefined, thousand: ',' } // Fallback test
|
||||
];
|
||||
|
||||
testLocales.forEach(({ decimal, locale, thousand }) => {
|
||||
it(`uses ${locale || 'system default'} locale`, (): void => {
|
||||
expect(
|
||||
getSeparator(locale)
|
||||
).toEqual({ decimal, thousand });
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back for invalid locale', (): void => {
|
||||
expect(
|
||||
getSeparator('invalid-locale')
|
||||
).toEqual({ decimal: '.', thousand: ',' }); // Default fallback
|
||||
});
|
||||
|
||||
it('matches system locale dynamically', (): void => {
|
||||
const n = 1000.1;
|
||||
const expected = {
|
||||
decimal: n.toLocaleString().substring(5, 6),
|
||||
thousand: n.toLocaleString().substring(1, 2)
|
||||
};
|
||||
|
||||
expect(getSeparator()).toEqual(expected);
|
||||
});
|
||||
|
||||
it('handles locales with unique separators', (): void => {
|
||||
const result = getSeparator('fr-FR');
|
||||
|
||||
expect(normalizeSeparator(result.thousand)).toBe(' ');
|
||||
expect(result.decimal).toBe(',');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* Get the decimal and thousand separator of a locale
|
||||
* @param locale
|
||||
* @returns {decimal: string, thousand: string}
|
||||
*/
|
||||
export function getSeparator (locale?: string):
|
||||
{ thousand: string, decimal: string } {
|
||||
return {
|
||||
decimal: (0.1).toLocaleString(locale, { useGrouping: false }).charAt(1),
|
||||
thousand: (1000).toLocaleString(locale, { useGrouping: true }).replace(/\d/g, '').charAt(0)
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export { formatBalance } from './formatBalance.js';
|
||||
export { formatDate } from './formatDate.js';
|
||||
export { formatDecimal } from './formatDecimal.js';
|
||||
export { formatElapsed } from './formatElapsed.js';
|
||||
export { formatNumber } from './formatNumber.js';
|
||||
export { calcSi, findSi } from './si.js';
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2017-2025 @polkadot/util authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { SiDef } from '../types.js';
|
||||
|
||||
/** @internal */
|
||||
export const SI_MID = 8;
|
||||
|
||||
/** @internal */
|
||||
export const SI: SiDef[] = [
|
||||
{ power: -24, text: 'yocto', value: 'y' },
|
||||
{ power: -21, text: 'zepto', value: 'z' },
|
||||
{ power: -18, text: 'atto', value: 'a' },
|
||||
{ power: -15, text: 'femto', value: 'f' },
|
||||
{ power: -12, text: 'pico', value: 'p' },
|
||||
{ power: -9, text: 'nano', value: 'n' },
|
||||
{ power: -6, text: 'micro', value: 'µ' },
|
||||
{ power: -3, text: 'milli', value: 'm' },
|
||||
{ power: 0, text: 'Unit', value: '-' }, // position 8
|
||||
{ power: 3, text: 'Kilo', value: 'k' },
|
||||
{ power: 6, text: 'Mill', value: 'M' }, // Mega, M
|
||||
{ power: 9, text: 'Bill', value: 'B' }, // Giga, G
|
||||
{ power: 12, text: 'Tril', value: 'T' }, // Tera, T
|
||||
{ power: 15, text: 'Peta', value: 'P' },
|
||||
{ power: 18, text: 'Exa', value: 'E' },
|
||||
{ power: 21, text: 'Zeta', value: 'Z' },
|
||||
{ power: 24, text: 'Yotta', value: 'Y' }
|
||||
];
|
||||
|
||||
// Given a SI type (e.g. k, m, Y) find the SI definition
|
||||
/** @internal */
|
||||
export function findSi (type: string): SiDef {
|
||||
// use a loop here, better RN support (which doesn't have [].find)
|
||||
for (let i = 0, count = SI.length; i < count; i++) {
|
||||
if (SI[i].value === type) {
|
||||
return SI[i];
|
||||
}
|
||||
}
|
||||
|
||||
return SI[SI_MID];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function calcSi (text: string, decimals: number, forceUnit?: string): SiDef {
|
||||
if (forceUnit) {
|
||||
return findSi(forceUnit);
|
||||
}
|
||||
|
||||
const siDefIndex = (SI_MID - 1) + Math.ceil((text.length - decimals) / 3);
|
||||
|
||||
return SI[siDefIndex] || SI[siDefIndex < 0 ? 0 : SI.length - 1];
|
||||
}
|
||||
Reference in new issue
Block a user