@uxf/localize 11.125.0 → 11.126.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/README.md CHANGED
@@ -169,6 +169,7 @@ const formatNumber = useFormatNumber();
169
169
 
170
170
  formatNumber(2000.78); // 2 001 (default precision 0)
171
171
  formatNumber(2000.78, { precision: 2 }); // 2 000,78
172
+ formatNumber(2000, { showPlusSign: true }); // +2 000 (+ only on positive values)
172
173
 
173
174
  <FormatNumber value={2000.78} />;
174
175
  ```
@@ -219,6 +220,7 @@ formatMoney({ amount: "2000.78", currency: "USD" }); // 2 001 $
219
220
  formatMoney({ amount: "2000.78", currency: "CZK" }, { precision: 1 }); // 2 000,8 Kč
220
221
  formatMoney({ amount: "2000.78", currency: "CZK" }, { precision: 1, preferIsoCode: true }); // 2 000,8 CZK
221
222
  formatMoney({ amount: "2000.78", currency: "CZK" }, { precision: 1, hideSymbol: true }); // 2 000,8
223
+ formatMoney({ amount: "500", currency: "CZK" }, { showPlusSign: true }); // +500 Kč (+ only on positive values)
222
224
 
223
225
  <FormatMoney money={{ amount: "2000.78", currency: "CZK" }} />;
224
226
  ```
@@ -236,6 +238,7 @@ formatPercentage(0.782); // 78 % (default precision 0)
236
238
  formatPercentage(0.782, null, { precision: 2 }); // 78,20 %
237
239
  formatPercentage(0.782, "up"); // 79 %
238
240
  formatPercentage(0.788, "down"); // 78 %
241
+ formatPercentage(0.5, null, { showPlusSign: true }); // +50 % (+ only on positive values)
239
242
 
240
243
  <FormatPercentage roundingType="up" value={0.782} />;
241
244
  ```
@@ -268,25 +271,25 @@ createLocalize<DT extends string = DateTimes, T extends string = Times, Locales
268
271
 
269
272
  Returns an object with the following members:
270
273
 
271
- | Member | Signature | Notes |
272
- | --- | --- | --- |
273
- | `LocalizeProvider` | `Provider<string>` | React context provider; `value` is the active locale key. |
274
- | `useLocaleConfig` | `() => LocalizeConfig<DT, T>` | Returns the config for the current locale. |
275
- | `formatNumber` | `(locale, value, options?) => string` | `options: { precision? }`. |
276
- | `useFormatNumber` | `() => (value, options?) => string` | Locale from context. |
277
- | `FormatNumber` | `FC<{ value; options? }>` | |
278
- | `formatMoney` | `(locale, money, options?) => string` | `options: { hideSymbol?, precision?, preferIsoCode? }`. |
279
- | `useFormatMoney` | `() => (money, options?) => string` | Locale from context. |
280
- | `FormatMoney` | `FC<{ money; options? }>` | |
281
- | `formatPercentage` | `(locale, value, roundingType?, options?) => string` | `roundingType: "nearest" \| "up" \| "down" \| null`. |
282
- | `useFormatPercentage` | `() => (value, roundingType?, options?) => string` | Locale from context. |
283
- | `FormatPercentage` | `FC<{ value; roundingType?; options? }>` | |
284
- | `formatDateTime` | `(locale, value, format, timeZone?) => string` | `value: DateValue`; default `timeZone` is `Europe/Prague`. |
285
- | `useFormatDateTime` | `() => (value, format) => string` | No `timeZone` param (fixed to default). |
286
- | `FormatDateTime` | `FC<{ value; format; timeZone? }>` | |
287
- | `formatTime` | `(locale, value, format) => string` | `value: TimeString`. |
288
- | `useFormatTime` | `() => (value, format) => string` | Locale from context. |
289
- | `FormatTime` | `FC<{ value; format }>` | |
274
+ | Member | Signature | Notes |
275
+ | --------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
276
+ | `LocalizeProvider` | `Provider<string>` | React context provider; `value` is the active locale key. |
277
+ | `useLocaleConfig` | `() => LocalizeConfig<DT, T>` | Returns the config for the current locale. |
278
+ | `formatNumber` | `(locale, value, options?) => string` | `options: { precision?, showPlusSign? }`. |
279
+ | `useFormatNumber` | `() => (value, options?) => string` | Locale from context. |
280
+ | `FormatNumber` | `FC<{ value; options? }>` | |
281
+ | `formatMoney` | `(locale, money, options?) => string` | `options: { hideSymbol?, precision?, preferIsoCode?, showPlusSign? }`. |
282
+ | `useFormatMoney` | `() => (money, options?) => string` | Locale from context. |
283
+ | `FormatMoney` | `FC<{ money; options? }>` | |
284
+ | `formatPercentage` | `(locale, value, roundingType?, options?) => string` | `roundingType: "nearest" \| "up" \| "down" \| null`; `options: { precision?, showPlusSign? }`. |
285
+ | `useFormatPercentage` | `() => (value, roundingType?, options?) => string` | Locale from context. |
286
+ | `FormatPercentage` | `FC<{ value; roundingType?; options? }>` | |
287
+ | `formatDateTime` | `(locale, value, format, timeZone?) => string` | `value: DateValue`; default `timeZone` is `Europe/Prague`. |
288
+ | `useFormatDateTime` | `() => (value, format) => string` | No `timeZone` param (fixed to default). |
289
+ | `FormatDateTime` | `FC<{ value; format; timeZone? }>` | |
290
+ | `formatTime` | `(locale, value, format) => string` | `value: TimeString`. |
291
+ | `useFormatTime` | `() => (value, format) => string` | Locale from context. |
292
+ | `FormatTime` | `FC<{ value; format }>` | |
290
293
 
291
294
  ### Exported types
292
295
 
@@ -305,6 +308,7 @@ Returns an object with the following members:
305
308
  - **Time zone defaults to `Europe/Prague`** for date/time formatting. The `useFormatDateTime` hook is fixed to that default; to override, use the standalone `formatDateTime(locale, value, format, timeZone)` or the `FormatDateTime` component's `timeZone` prop.
306
309
  - **Date-only strings** (`"YYYY-MM-DD"`) are parsed as midnight in the target time zone, while `Date` instances represent a concrete instant and are shifted into that zone — the same wall-clock string can render differently depending on the input form.
307
310
  - **Currency patterns** use currency.js placeholders: `#` = amount, `!` = symbol. Built-in defaults exist for `EUR` and `USD`; `specialCases` in the config override per currency; otherwise a plain `# <symbol>` pattern is used, and `preferIsoCode` forces the ISO code instead of the symbol.
311
+ - **`showPlusSign` only adds `+`** to a positive, non-zero **final** value (e.g. `+500 Kč`, `+50 %`). It never touches the negative sign — `-` is always shown regardless (handled by currency.js `negativePattern`) — and a value that rounds to `0` stays unsigned. Matches `Intl.NumberFormat`'s `signDisplay: "exceptZero"`.
308
312
  - **Separators are non-breaking spaces** in some locales (e.g. `cs`), so `"2 001"` contains ` `, not a regular space.
309
313
 
310
314
  ## Links
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxf/localize",
3
- "version": "11.125.0",
3
+ "version": "11.126.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -19,8 +19,8 @@
19
19
  "currency.js": "2.0.4"
20
20
  },
21
21
  "peerDependencies": {
22
- "@uxf/core": "11.124.0",
23
- "@uxf/core-react": "11.125.0",
22
+ "@uxf/core": "11.126.0",
23
+ "@uxf/core-react": "11.126.0",
24
24
  "dayjs": "^1.11.21",
25
25
  "react": ">=18.2.0",
26
26
  "react-dom": ">=18.2.0"
@@ -28,8 +28,8 @@
28
28
  "devDependencies": {
29
29
  "@types/react": "18.3.31",
30
30
  "@types/react-dom": "18.3.7",
31
- "@uxf/core": "11.124.0",
32
- "@uxf/core-react": "11.125.0",
31
+ "@uxf/core": "11.126.0",
32
+ "@uxf/core-react": "11.126.0",
33
33
  "dayjs": "^1.11.21",
34
34
  "react": "18.3.1",
35
35
  "react-dom": "18.3.1"
@@ -9,6 +9,7 @@ const empty_object_1 = require("@uxf/core/constants/empty-object");
9
9
  const currency_js_1 = __importDefault(require("currency.js"));
10
10
  const react_1 = require("react");
11
11
  const context_1 = require("../context/context");
12
+ const apply_sign_to_pattern_1 = require("../utils/apply-sign-to-pattern");
12
13
  const curry_1 = require("../utils/curry");
13
14
  const money_defaults_1 = require("../utils/money-defaults");
14
15
  const defaultPatterns = {
@@ -32,14 +33,17 @@ function createFormatMoney(localizeConfigs) {
32
33
  : !(options === null || options === void 0 ? void 0 : options.preferIsoCode)
33
34
  ? currentPatterns
34
35
  : defaultPatterns;
36
+ const amount = (0, currency_js_1.default)(money.amount, { precision });
37
+ const pattern = (0, apply_sign_to_pattern_1._applySignToPattern)(patterns.pattern, amount.value, options === null || options === void 0 ? void 0 : options.showPlusSign);
35
38
  const formatOptions = {
36
39
  ...patterns,
40
+ pattern,
37
41
  decimal: config.decimalSeparator,
38
42
  separator: config.thousandsSeparator,
39
43
  precision,
40
44
  symbol,
41
45
  };
42
- return (0, currency_js_1.default)(money.amount, { precision }).format(formatOptions);
46
+ return amount.format(formatOptions);
43
47
  };
44
48
  }
45
49
  function createUseFormatMoney(localizeConfigs) {
@@ -28,4 +28,11 @@ describe("money formatter", function () {
28
28
  expect(formatMoneyWithLocales("en", { amount: "2000.78", currency: "USD" }, { precision: 1, preferIsoCode: true })).toBe("2,000.8\xa0USD");
29
29
  expect(formatMoneyWithLocales("en", { amount: "2000.78", currency: "USD" }, { precision: 1, hideSymbol: true })).toBe("2,000.8");
30
30
  });
31
+ it("format money with sign", function () {
32
+ expect(formatMoneyWithLocales("cs", { amount: "500", currency: "CZK" }, { showPlusSign: true })).toBe("+500\xa0Kč");
33
+ expect(formatMoneyWithLocales("cs", { amount: "-500", currency: "CZK" }, { showPlusSign: true })).toBe("-500\xa0Kč");
34
+ expect(formatMoneyWithLocales("cs", { amount: "0", currency: "CZK" }, { showPlusSign: true })).toBe("0\xa0Kč");
35
+ expect(formatMoneyWithLocales("cs", { amount: "500", currency: "CZK" }, { showPlusSign: true, hideSymbol: true })).toBe("+500");
36
+ expect(formatMoneyWithLocales("cs", { amount: "500", currency: "CZK" }, { showPlusSign: true, preferIsoCode: true })).toBe("+500\xa0CZK");
37
+ });
31
38
  });
@@ -8,14 +8,17 @@ exports.createUseFormatNumber = createUseFormatNumber;
8
8
  const currency_js_1 = __importDefault(require("currency.js"));
9
9
  const react_1 = require("react");
10
10
  const context_1 = require("../context/context");
11
+ const apply_sign_to_pattern_1 = require("../utils/apply-sign-to-pattern");
11
12
  const curry_1 = require("../utils/curry");
12
13
  const map_options_1 = require("../utils/map-options");
13
14
  function createFormatNumber(localizeConfigs) {
14
15
  return (locale, value, options) => {
15
16
  var _a;
16
17
  const precision = (_a = options === null || options === void 0 ? void 0 : options.precision) !== null && _a !== void 0 ? _a : 0;
17
- const formatOptions = { ...(0, map_options_1._mapOptions)(localizeConfigs[locale].number), precision };
18
- return (0, currency_js_1.default)(value, { precision }).format(formatOptions);
18
+ const amount = (0, currency_js_1.default)(value, { precision });
19
+ const base = (0, map_options_1._mapOptions)(localizeConfigs[locale].number);
20
+ const pattern = (0, apply_sign_to_pattern_1._applySignToPattern)(base.pattern, amount.value, options === null || options === void 0 ? void 0 : options.showPlusSign);
21
+ return amount.format({ ...base, pattern, precision });
19
22
  };
20
23
  }
21
24
  function createUseFormatNumber(localizeConfigs) {
@@ -16,4 +16,11 @@ describe("number formatter", function () {
16
16
  expect(formatNumberWithLocales("en", 2000.78)).toBe("2,001");
17
17
  expect(formatNumberWithLocales("en", 2000.78, { precision: 2 })).toBe("2,000.78");
18
18
  });
19
+ it("format number with sign", function () {
20
+ expect(formatNumberWithLocales("cs", 2000, { showPlusSign: true })).toBe("+2\xa0000");
21
+ expect(formatNumberWithLocales("cs", -2000, { showPlusSign: true })).toBe("-2\xa0000");
22
+ expect(formatNumberWithLocales("cs", 0, { showPlusSign: true })).toBe("0");
23
+ expect(formatNumberWithLocales("cs", 0.4, { showPlusSign: true })).toBe("0");
24
+ expect(formatNumberWithLocales("cs", 0.04, { showPlusSign: true, precision: 2 })).toBe("+0,04");
25
+ });
19
26
  });
@@ -22,4 +22,9 @@ describe("percentage formatter", function () {
22
22
  expect(formatPercentageWithLocales("en", 0.782, "up")).toBe("79\xa0%");
23
23
  expect(formatPercentageWithLocales("en", 0.788, "down")).toBe("78\xa0%");
24
24
  });
25
+ it("format percentage with sign", function () {
26
+ expect(formatPercentageWithLocales("cs", 0.5, null, { showPlusSign: true })).toBe("+50\xa0%");
27
+ expect(formatPercentageWithLocales("cs", -0.5, null, { showPlusSign: true })).toBe("-50\xa0%");
28
+ expect(formatPercentageWithLocales("cs", 0, null, { showPlusSign: true })).toBe("0\xa0%");
29
+ });
25
30
  });
package/src/types.d.ts CHANGED
@@ -4,6 +4,7 @@ import { FC, Provider } from "react";
4
4
  import { TimeZone as CoreTimeZone } from "./utils/time-zone";
5
5
  export type FormatNumberOptions = {
6
6
  precision?: number;
7
+ showPlusSign?: boolean;
7
8
  };
8
9
  export type FormatNumberFunction<Locales extends string> = (locale: Locales, value: number, options?: FormatNumberOptions) => string;
9
10
  export type UseFormatNumberFunction = () => (value: number, options?: FormatNumberOptions) => string;
@@ -21,6 +22,7 @@ export type FormatMoneyOptions = {
21
22
  hideSymbol?: boolean;
22
23
  precision?: number;
23
24
  preferIsoCode?: boolean;
25
+ showPlusSign?: boolean;
24
26
  };
25
27
  export type FormatMoneyFunction<Locales extends string> = (locale: Locales, money: Money, options?: FormatMoneyOptions) => string;
26
28
  export type UseFormatMoneyFunction = () => (money: Money, options?: FormatMoneyOptions) => string;
@@ -31,6 +33,7 @@ export type FormatMoneyComponent = FC<{
31
33
  export type RoundingType = "nearest" | "up" | "down";
32
34
  export type FormatPercentageOptions = {
33
35
  precision?: number;
36
+ showPlusSign?: boolean;
34
37
  };
35
38
  export type FormatPercentageFunction<Locales extends string> = (locale: Locales, value: number, roundingType?: RoundingType | null, options?: FormatPercentageOptions) => string;
36
39
  export type UseFormatPercentageFunction = () => (value: number, roundingType?: RoundingType | null, options?: FormatPercentageOptions) => string;
@@ -0,0 +1 @@
1
+ export declare const _applySignToPattern: (pattern: string | undefined, value: number, showPlusSign?: boolean) => string | undefined;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports._applySignToPattern = void 0;
4
+ const _applySignToPattern = (pattern, value, showPlusSign) => (pattern !== undefined && showPlusSign && value > 0 ? `+${pattern}` : pattern);
5
+ exports._applySignToPattern = _applySignToPattern;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const apply_sign_to_pattern_1 = require("./apply-sign-to-pattern");
4
+ describe("_applySignToPattern", () => {
5
+ it("prepends + to a positive value when showPlusSign is enabled", () => {
6
+ expect((0, apply_sign_to_pattern_1._applySignToPattern)("#", 500, true)).toBe("+#");
7
+ expect((0, apply_sign_to_pattern_1._applySignToPattern)("#\xa0!", 500, true)).toBe("+#\xa0!");
8
+ });
9
+ it("does not touch zero or negative values", () => {
10
+ expect((0, apply_sign_to_pattern_1._applySignToPattern)("#", 0, true)).toBe("#");
11
+ expect((0, apply_sign_to_pattern_1._applySignToPattern)("#", -500, true)).toBe("#");
12
+ });
13
+ it("returns the pattern unchanged when showPlusSign is falsy", () => {
14
+ expect((0, apply_sign_to_pattern_1._applySignToPattern)("#", 500)).toBe("#");
15
+ expect((0, apply_sign_to_pattern_1._applySignToPattern)("#", 500, false)).toBe("#");
16
+ });
17
+ });