@lion/ui 0.1.0 → 0.1.2

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.
Files changed (158) hide show
  1. package/components/calendar/src/LionCalendar.js +2 -3
  2. package/components/calendar/test/lion-calendar.test.js +5 -3
  3. package/components/form-core/src/validate/ValidateMixin.js +7 -3
  4. package/components/form-core/src/validate/validators/DateValidators.js +1 -1
  5. package/components/form-core/test/lion-field.test.js +4 -2
  6. package/components/form-core/test/validate/DateValidators.test.js +1 -1
  7. package/components/form-core/test-suites/FormatMixin.suite.js +1 -1
  8. package/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.js +6 -4
  9. package/components/form-integrations/test/form-group-methods.test.js +3 -3
  10. package/components/form-integrations/test/form-integrations.test.js +3 -3
  11. package/components/form-integrations/test/form-validation-integrations.test.js +11 -10
  12. package/components/form-integrations/test/helpers/umbrella-form.js +3 -3
  13. package/components/input-amount/src/LionInputAmount.js +2 -2
  14. package/components/input-amount/src/formatters.js +5 -1
  15. package/components/input-amount/src/parsers.js +1 -1
  16. package/components/input-amount/test/formatters.test.js +5 -3
  17. package/components/input-date/src/LionInputDate.js +1 -1
  18. package/components/input-date/test/lion-input-date.test.js +91 -43
  19. package/components/input-datepicker/src/LionInputDatepicker.js +1 -1
  20. package/components/input-email/src/LionInputEmail.js +1 -1
  21. package/components/input-iban/src/LionInputIban.js +1 -1
  22. package/components/input-iban/src/validators.js +14 -8
  23. package/components/input-range/src/LionInputRange.js +1 -1
  24. package/components/input-tel/src/LionInputTel.js +3 -3
  25. package/components/input-tel/test-suites/LionInputTel.suite.js +3 -2
  26. package/components/input-tel-dropdown/src/LionInputTelDropdown.js +7 -4
  27. package/components/localize/src/LocalizeManager.js +19 -3
  28. package/components/localize/src/LocalizeMixin.js +24 -9
  29. package/components/localize/src/date/formatDate.js +4 -3
  30. package/components/localize/src/date/parseDate.js +5 -3
  31. package/components/localize/src/getLocalizeManager.js +74 -0
  32. package/components/localize/src/number/formatNumber.js +5 -4
  33. package/components/localize/src/number/getCurrencyName.js +3 -2
  34. package/components/localize/src/number/parseNumber.js +3 -2
  35. package/components/localize/src/number/utils/emptyStringWhenNumberNan.js +3 -2
  36. package/components/localize/src/singleton.js +6 -9
  37. package/components/localize/src/utils/getLocale.js +3 -8
  38. package/components/localize/test/LocalizeManager.test.js +19 -2
  39. package/components/localize/test/LocalizeMixin.test.js +17 -15
  40. package/components/localize/test/date/formatDate.test.js +25 -24
  41. package/components/localize/test/date/getDateFormatBasedOnLocale.test.js +8 -3
  42. package/components/localize/test/date/parseDate.test.js +22 -7
  43. package/components/localize/test/getLocalizeManager.test.js +31 -0
  44. package/components/localize/test/localize.test.js +5 -4
  45. package/components/localize/test/number/formatNumber.test.js +31 -29
  46. package/components/localize/test/number/formatNumberToParts.test.js +5 -3
  47. package/components/localize/test/number/getCurrencyName.test.js +1 -1
  48. package/components/localize/test/number/parseNumber.test.js +37 -19
  49. package/components/localize/test/side-effect-free-entrypoint.test.js +38 -0
  50. package/components/localize/test-helpers/localizeTearDown.js +5 -4
  51. package/components/localize/types/LocalizeMixinTypes.ts +8 -1
  52. package/components/pagination/src/LionPagination.js +1 -1
  53. package/components/progress-indicator/src/LionProgressIndicator.js +3 -2
  54. package/components/select/src/LionSelect.js +15 -0
  55. package/components/select/test/lion-select.test.js +25 -0
  56. package/components/select-rich/src/LionSelectRich.js +6 -0
  57. package/components/select-rich/test/lion-select-rich.test.js +33 -0
  58. package/components/validate-messages/src/loadDefaultFeedbackMessages.js +2 -2
  59. package/components/validate-messages/test/loadDefaultFeedbackMessagesNoSideEffects.js +6 -4
  60. package/custom-elements.json +740 -15
  61. package/dist-types/components/calendar/src/LionCalendar.d.ts.map +1 -1
  62. package/dist-types/components/form-core/src/validate/ValidateMixin.d.ts.map +1 -1
  63. package/dist-types/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.d.ts.map +1 -1
  64. package/dist-types/components/input-amount/src/formatters.d.ts.map +1 -1
  65. package/dist-types/components/input-date/src/LionInputDate.d.ts +1 -1
  66. package/dist-types/components/input-iban/src/validators.d.ts.map +1 -1
  67. package/dist-types/components/input-tel/test-suites/LionInputTel.suite.d.ts.map +1 -1
  68. package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts.map +1 -1
  69. package/dist-types/components/localize/src/LocalizeManager.d.ts +6 -2
  70. package/dist-types/components/localize/src/LocalizeManager.d.ts.map +1 -1
  71. package/dist-types/components/localize/src/LocalizeMixin.d.ts.map +1 -1
  72. package/dist-types/components/localize/src/date/formatDate.d.ts.map +1 -1
  73. package/dist-types/components/localize/src/date/parseDate.d.ts.map +1 -1
  74. package/dist-types/components/localize/src/getLocalizeManager.d.ts +60 -0
  75. package/dist-types/components/localize/src/getLocalizeManager.d.ts.map +1 -0
  76. package/dist-types/components/localize/src/number/formatNumber.d.ts.map +1 -1
  77. package/dist-types/components/localize/src/number/getCurrencyName.d.ts.map +1 -1
  78. package/dist-types/components/localize/src/number/parseNumber.d.ts.map +1 -1
  79. package/dist-types/components/localize/src/number/utils/emptyStringWhenNumberNan.d.ts.map +1 -1
  80. package/dist-types/components/localize/src/singleton.d.ts +5 -3
  81. package/dist-types/components/localize/src/singleton.d.ts.map +1 -1
  82. package/dist-types/components/localize/src/utils/getLocale.d.ts.map +1 -1
  83. package/dist-types/components/localize/test/getLocalizeManager.test.d.ts +2 -0
  84. package/dist-types/components/localize/test/getLocalizeManager.test.d.ts.map +1 -0
  85. package/dist-types/components/localize/test/side-effect-free-entrypoint.test.d.ts +2 -0
  86. package/dist-types/components/localize/test/side-effect-free-entrypoint.test.d.ts.map +1 -0
  87. package/dist-types/components/localize/test-helpers/localizeTearDown.d.ts.map +1 -1
  88. package/dist-types/components/localize/types/LocalizeMixinTypes.d.ts +2 -0
  89. package/dist-types/components/localize/types/LocalizeMixinTypes.d.ts.map +1 -1
  90. package/dist-types/components/progress-indicator/src/LionProgressIndicator.d.ts.map +1 -1
  91. package/dist-types/components/select/src/LionSelect.d.ts +1 -0
  92. package/dist-types/components/select/src/LionSelect.d.ts.map +1 -1
  93. package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
  94. package/dist-types/exports/localize-no-side-effects.d.ts +18 -0
  95. package/dist-types/exports/types/core.d.ts +3 -0
  96. package/dist-types/exports/types/core.d.ts.map +1 -1
  97. package/dist-types/exports/types/form-core.d.ts +16 -0
  98. package/dist-types/exports/types/form-core.d.ts.map +1 -1
  99. package/dist-types/exports/types/localize.d.ts +1 -0
  100. package/dist-types/exports/types/localize.d.ts.map +1 -1
  101. package/dist-types/exports/types/overlays.d.ts +1 -0
  102. package/dist-types/exports/types/overlays.d.ts.map +1 -1
  103. package/dist-types/tsconfig.tsbuildinfo +1 -1
  104. package/docs/components/checkbox-group/overview.md +2 -2
  105. package/docs/components/collapsible/overview.md +1 -1
  106. package/docs/components/combobox/overview.md +1 -1
  107. package/docs/components/combobox/use-cases.md +1 -1
  108. package/docs/components/dialog/overview.md +1 -1
  109. package/docs/components/fieldset/overview.md +5 -5
  110. package/docs/components/form/overview.md +3 -3
  111. package/docs/components/form/use-cases.md +2 -2
  112. package/docs/components/icon/use-cases.md +1 -1
  113. package/docs/components/input/overview.md +1 -1
  114. package/docs/components/input-amount/overview.md +3 -3
  115. package/docs/components/input-date/overview.md +3 -3
  116. package/docs/components/input-datepicker/overview.md +5 -5
  117. package/docs/components/input-email/overview.md +2 -2
  118. package/docs/components/input-iban/overview.md +2 -2
  119. package/docs/components/input-range/overview.md +2 -2
  120. package/docs/components/input-stepper/overview.md +1 -1
  121. package/docs/components/input-tel/overview.md +1 -1
  122. package/docs/components/input-tel/use-cases.md +6 -6
  123. package/docs/components/input-tel-dropdown/overview.md +1 -1
  124. package/docs/components/radio-group/overview.md +3 -3
  125. package/docs/components/select-rich/use-cases.md +1 -1
  126. package/docs/components/tabs/examples.md +1 -1
  127. package/docs/fundamentals/index.md +4 -4
  128. package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +1 -1
  129. package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +1 -1
  130. package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +1 -1
  131. package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +1 -1
  132. package/docs/fundamentals/node-tools/providence-analytics/overview.md +3 -3
  133. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  134. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  135. package/docs/fundamentals/systems/core/overview.md +1 -1
  136. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  137. package/docs/fundamentals/systems/form/model-value.md +1 -1
  138. package/docs/fundamentals/systems/form/overview.md +28 -28
  139. package/docs/fundamentals/systems/form/use-cases.md +3 -3
  140. package/docs/fundamentals/systems/form/validate.md +1 -1
  141. package/docs/fundamentals/systems/localize/overview.md +4 -4
  142. package/docs/fundamentals/systems/overlays/assets/umbrella-form.js +3 -3
  143. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  144. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  145. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  146. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  147. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  148. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  149. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  150. package/docs/guides/index.md +8 -8
  151. package/docs/guides/principles/index.md +5 -5
  152. package/docs/guides/principles/styling.md +1 -1
  153. package/exports/localize-no-side-effects.js +19 -0
  154. package/exports/types/core.ts +3 -0
  155. package/exports/types/form-core.ts +16 -0
  156. package/exports/types/localize.ts +1 -0
  157. package/exports/types/overlays.ts +1 -0
  158. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { Unparseable } from '@lion/ui/form-core.js';
2
- import { LocalizeMixin, localize } from '@lion/ui/localize.js';
2
+ import { LocalizeMixin } from '@lion/ui/localize-no-side-effects.js';
3
3
  import { LionInput } from '@lion/ui/input.js';
4
4
 
5
5
  import { PhoneUtilManager } from './PhoneUtilManager.js';
@@ -125,7 +125,7 @@ export class LionInputTel extends LocalizeMixin(LionInput) {
125
125
  // @ts-expect-error
126
126
  // eslint-disable-next-line class-methods-use-this
127
127
  get fieldName() {
128
- return localize.msg('lion-input-tel:phoneNumber');
128
+ return this._localizeManager.msg('lion-input-tel:phoneNumber');
129
129
  }
130
130
 
131
131
  /**
@@ -227,7 +227,7 @@ export class LionInputTel extends LocalizeMixin(LionInput) {
227
227
  onLocaleUpdated() {
228
228
  super.onLocaleUpdated();
229
229
 
230
- const localeSplitted = localize.locale.split('-');
230
+ const localeSplitted = this._localizeManager.locale.split('-');
231
231
  /**
232
232
  * @protected
233
233
  * @type {RegionCode}
@@ -9,7 +9,7 @@ import {
9
9
  } from '@open-wc/testing';
10
10
  import sinon from 'sinon';
11
11
  import { mimicUserInput } from '@lion/ui/form-core-test-helpers.js';
12
- import { localize } from '@lion/ui/localize.js';
12
+ import { getLocalizeManager } from '@lion/ui/localize-no-side-effects.js';
13
13
  import { Unparseable } from '@lion/ui/form-core.js';
14
14
  import { LionInputTel, PhoneNumber, PhoneUtilManager } from '@lion/ui/input-tel.js';
15
15
  import { mockPhoneUtilManager, restorePhoneUtilManager } from '@lion/ui/input-tel-test-helpers.js';
@@ -23,7 +23,8 @@ const fixture = /** @type {(arg: string | TemplateResult) => Promise<LionInputTe
23
23
  const fixtureSync = /** @type {(arg: string | TemplateResult) => LionInputTel} */ (_fixtureSync);
24
24
 
25
25
  const getRegionCodeBasedOnLocale = () => {
26
- const localeSplitted = localize.locale.split('-');
26
+ const localizeManager = getLocalizeManager();
27
+ const localeSplitted = localizeManager.locale.split('-');
27
28
  return /** @type {RegionCode} */ (localeSplitted[localeSplitted.length - 1]).toUpperCase();
28
29
  };
29
30
 
@@ -2,7 +2,7 @@ import { html, css } from 'lit';
2
2
  import { ref, createRef } from 'lit/directives/ref.js';
3
3
 
4
4
  import { LionInputTel } from '@lion/ui/input-tel.js';
5
- import { localize } from '@lion/ui/localize.js';
5
+ import { getLocalizeManager } from '@lion/ui/localize-no-side-effects.js';
6
6
  import { getFlagSymbol } from './getFlagSymbol.js';
7
7
 
8
8
  /**
@@ -80,6 +80,7 @@ export class LionInputTelDropdown extends LionInputTel {
80
80
  * @type {TemplateDataForDropdownInputTel}
81
81
  */
82
82
  get _templateDataDropdown() {
83
+ const localizeManager = getLocalizeManager();
83
84
  const refs = {
84
85
  dropdown: {
85
86
  ref: this.refs.dropdown,
@@ -91,10 +92,12 @@ export class LionInputTelDropdown extends LionInputTel {
91
92
  'model-value-changed': this._onDropdownValueChange,
92
93
  },
93
94
  labels: {
94
- selectCountry: localize.msg('lion-input-tel:selectCountry'),
95
- allCountries: this._allCountriesLabel || localize.msg('lion-input-tel:allCountries'),
95
+ selectCountry: localizeManager.msg('lion-input-tel:selectCountry'),
96
+ allCountries:
97
+ this._allCountriesLabel || localizeManager.msg('lion-input-tel:allCountries'),
96
98
  preferredCountries:
97
- this._preferredCountriesLabel || localize.msg('lion-input-tel:suggestedCountries'),
99
+ this._preferredCountriesLabel ||
100
+ localizeManager.msg('lion-input-tel:suggestedCountries'),
98
101
  },
99
102
  },
100
103
  };
@@ -18,6 +18,7 @@ export class LocalizeManager {
18
18
  autoLoadOnLocaleChange = false,
19
19
  fallbackLocale = '',
20
20
  showKeyAsFallback = false,
21
+ allowOverridesForExistingNamespaces = false,
21
22
  } = {}) {
22
23
  /** @private */
23
24
  this.__delegationTarget = document.createDocumentFragment();
@@ -28,6 +29,9 @@ export class LocalizeManager {
28
29
  /** @protected */
29
30
  this._showKeyAsFallback = showKeyAsFallback;
30
31
 
32
+ /** @private */
33
+ this.__allowOverridesForExistingNamespaces = allowOverridesForExistingNamespaces;
34
+
31
35
  /**
32
36
  * @type {Object.<string, Object.<string, Object>>}
33
37
  * @private
@@ -199,17 +203,29 @@ export class LocalizeManager {
199
203
  * @param {string} locale
200
204
  * @param {string} namespace
201
205
  * @param {object} data
202
- * @throws {Error} Namespace can be added only once, for a given locale
206
+ * @throws {Error} Namespace can be added only once, for a given locale unless allowOverridesForExistingNamespaces
207
+ * is set to `true`
203
208
  */
204
209
  addData(locale, namespace, data) {
205
- if (this._isNamespaceInCache(locale, namespace)) {
210
+ if (
211
+ !this.__allowOverridesForExistingNamespaces &&
212
+ this._isNamespaceInCache(locale, namespace)
213
+ ) {
206
214
  throw new Error(
207
215
  `Namespace "${namespace}" has been already added for the locale "${locale}".`,
208
216
  );
209
217
  }
210
218
 
211
219
  this.__storage[locale] = this.__storage[locale] || {};
212
- this.__storage[locale][namespace] = data;
220
+
221
+ if (this.__allowOverridesForExistingNamespaces) {
222
+ this.__storage[locale][namespace] = {
223
+ ...this.__storage[locale][namespace],
224
+ ...data,
225
+ };
226
+ } else {
227
+ this.__storage[locale][namespace] = data;
228
+ }
213
229
  }
214
230
 
215
231
  /**
@@ -1,7 +1,7 @@
1
1
  import { dedupeMixin } from '@open-wc/dedupe-mixin';
2
2
  import { nothing } from 'lit';
3
3
  import { until } from 'lit/directives/until.js';
4
- import { localize } from './singleton.js';
4
+ import { getLocalizeManager } from './getLocalizeManager.js';
5
5
 
6
6
  /**
7
7
  * @typedef {import('lit/directive.js').DirectiveResult} DirectiveResult
@@ -34,6 +34,8 @@ const LocalizeMixinImplementation = superclass =>
34
34
  constructor() {
35
35
  super();
36
36
 
37
+ this._localizeManager = getLocalizeManager();
38
+
37
39
  /** @private */
38
40
  this.__boundLocalizeOnLocaleChanged =
39
41
  /** @param {...Object} args */
@@ -74,14 +76,23 @@ const LocalizeMixinImplementation = superclass =>
74
76
  if (this.localizeNamespacesLoaded) {
75
77
  this.localizeNamespacesLoaded.then(() => this.onLocaleReady());
76
78
  }
77
- localize.addEventListener('__localeChanging', this.__boundLocalizeOnLocaleChanging);
78
- localize.addEventListener('localeChanged', this.__boundLocalizeOnLocaleChanged);
79
+ this._localizeManager.addEventListener(
80
+ '__localeChanging',
81
+ this.__boundLocalizeOnLocaleChanging,
82
+ );
83
+ this._localizeManager.addEventListener('localeChanged', this.__boundLocalizeOnLocaleChanged);
79
84
  }
80
85
 
81
86
  disconnectedCallback() {
82
87
  super.disconnectedCallback();
83
- localize.removeEventListener('__localeChanging', this.__boundLocalizeOnLocaleChanging);
84
- localize.removeEventListener('localeChanged', this.__boundLocalizeOnLocaleChanged);
88
+ this._localizeManager.removeEventListener(
89
+ '__localeChanging',
90
+ this.__boundLocalizeOnLocaleChanging,
91
+ );
92
+ this._localizeManager.removeEventListener(
93
+ 'localeChanged',
94
+ this.__boundLocalizeOnLocaleChanged,
95
+ );
85
96
  }
86
97
 
87
98
  /**
@@ -93,7 +104,7 @@ const LocalizeMixinImplementation = superclass =>
93
104
  */
94
105
  msgLit(keys, variables, options) {
95
106
  if (this.__localizeMessageSync) {
96
- return localize.msg(keys, variables, options);
107
+ return this._localizeManager.msg(keys, variables, options);
97
108
  }
98
109
 
99
110
  if (!this.localizeNamespacesLoaded) {
@@ -101,7 +112,9 @@ const LocalizeMixinImplementation = superclass =>
101
112
  }
102
113
 
103
114
  return until(
104
- this.localizeNamespacesLoaded.then(() => localize.msg(keys, variables, options)),
115
+ this.localizeNamespacesLoaded.then(() =>
116
+ this._localizeManager.msg(keys, variables, options),
117
+ ),
105
118
  nothing,
106
119
  );
107
120
  }
@@ -125,12 +138,14 @@ const LocalizeMixinImplementation = superclass =>
125
138
 
126
139
  /** @private */
127
140
  __localizeStartLoadingNamespaces() {
128
- this.localizeNamespacesLoaded = localize.loadNamespaces(this.__getUniqueNamespaces());
141
+ this.localizeNamespacesLoaded = this._localizeManager.loadNamespaces(
142
+ this.__getUniqueNamespaces(),
143
+ );
129
144
  }
130
145
 
131
146
  /**
132
147
  * Start loading namespaces on the event that is sent immediately
133
- * when localize.locale changes --> 'localeChanging'
148
+ * when this._localizeManager.locale changes --> 'localeChanging'
134
149
  * @private
135
150
  */
136
151
  __localizeOnLocaleChanging() {
@@ -1,5 +1,5 @@
1
1
  import { getLocale } from '../utils/getLocale.js';
2
- import { localize } from '../singleton.js';
2
+ import { getLocalizeManager } from '../getLocalizeManager.js';
3
3
  import { normalizeIntlDate } from './utils/normalizeIntlDate.js';
4
4
 
5
5
  /** @typedef {import('../../types/LocalizeMixinTypes.js').DatePostProcessor} DatePostProcessor */
@@ -12,6 +12,7 @@ import { normalizeIntlDate } from './utils/normalizeIntlDate.js';
12
12
  * @returns {string}
13
13
  */
14
14
  export function formatDate(date, options) {
15
+ const localizeManager = getLocalizeManager();
15
16
  if (!(date instanceof Date)) {
16
17
  return '';
17
18
  }
@@ -36,8 +37,8 @@ export function formatDate(date, options) {
36
37
  formattedDate = '';
37
38
  }
38
39
 
39
- if (localize.formatDateOptions.postProcessors.size > 0) {
40
- Array.from(localize.formatDateOptions.postProcessors).forEach(([locale, fn]) => {
40
+ if (localizeManager.formatDateOptions.postProcessors.size > 0) {
41
+ Array.from(localizeManager.formatDateOptions.postProcessors).forEach(([locale, fn]) => {
41
42
  if (locale === computedLocale) {
42
43
  formattedDate = fn(formattedDate);
43
44
  }
@@ -1,5 +1,5 @@
1
1
  import { getDateFormatBasedOnLocale } from './getDateFormatBasedOnLocale.js';
2
- import { localize } from '../singleton.js';
2
+ import { getLocalizeManager } from '../getLocalizeManager.js';
3
3
  import { addLeadingZero } from './utils/addLeadingZero.js';
4
4
 
5
5
  /**
@@ -29,10 +29,12 @@ const memoizedGetDateFormatBasedOnLocale = memoize(getDateFormatBasedOnLocale);
29
29
  * @returns {Date | undefined}
30
30
  */
31
31
  export function parseDate(dateString) {
32
+ const localizeManager = getLocalizeManager();
33
+
32
34
  const stringToParse = addLeadingZero(dateString);
33
35
  let parsedString;
34
36
 
35
- switch (memoizedGetDateFormatBasedOnLocale(localize.locale)) {
37
+ switch (memoizedGetDateFormatBasedOnLocale(localizeManager.locale)) {
36
38
  case 'day-month-year':
37
39
  parsedString = `${stringToParse.slice(6, 10)}/${stringToParse.slice(
38
40
  3,
@@ -56,7 +58,7 @@ export function parseDate(dateString) {
56
58
  }
57
59
 
58
60
  const [year, month, day] = parsedString.split('/').map(Number);
59
- const parsedDate = new Date(Date.UTC(year, month - 1, day));
61
+ const parsedDate = new Date(new Date(year, month - 1, day));
60
62
 
61
63
  // Check if parsedDate is not `Invalid Date` or that the date has changed (e.g. the not existing 31.02.2020)
62
64
  if (
@@ -0,0 +1,74 @@
1
+ // @ts-ignore
2
+ import { singletonManager } from 'singleton-manager';
3
+ import { LocalizeManager } from './LocalizeManager.js';
4
+
5
+ /**
6
+ * Side-effect-free alternative for `localize` (the globally shared instance of LocalizeManager).
7
+ * When this function is imported, no side-effect happened yet, i.e. no global instance was registered yet.
8
+ * The side effect-free approach generates:
9
+ * - smaller, optimized bundles
10
+ * - a predictable loading order, that allows for:
11
+ * - deduping strategies when multiple instances of the localizeManager are on a page
12
+ * - providing a customized extension of LocalizeManager
13
+ *
14
+ * Use it like this:
15
+ *
16
+ * ```js
17
+ * function myFunction() {
18
+ * // note that 'localizeManager' is the same as former 'localize'
19
+ * const localizeManager = getLocalizeManger();
20
+ * // ...
21
+ * }
22
+ * ```
23
+ *
24
+ * In a class, we advise a shared instance:
25
+ * ```js
26
+ * class MyClass {
27
+ * constructor() {
28
+ * this._localizeManager = getLocalizeManger();
29
+ * }
30
+ * // ...
31
+ * }
32
+ * ```
33
+ *
34
+ * Make sure to always call this method inside a function or class (otherwise side effects are created)
35
+ *
36
+ * Do you want to register your own LocalizeManager?
37
+ * Make sure it's registered before anyone called `getLocalizeManager()`
38
+ *
39
+ * ```js
40
+ * import { singletonManager } from 'singleton-manager';
41
+ * import { getLocalizeManger } from '@lion/ui/localize-no-side-effects.js';
42
+ *
43
+ * // First register your own LocalizeManager (for deduping or other reasons)
44
+ * singletonManager.set('lion/ui::localize::0.x', class MyLocalizeManager extends LocalizeManager {});
45
+ *
46
+ * // Now, all your code gets the right instance
47
+ * function myFuntion() {
48
+ * const localizeManager = getLocalizeManager();
49
+ * // ...
50
+ * }
51
+ *
52
+ * class myClass() {
53
+ * constructor() {
54
+ * this._localizeManager = getLocalizeManager();
55
+ * // ...
56
+ * }
57
+ * }
58
+ * ```
59
+ *
60
+ * @returns {LocalizeManager}
61
+ */
62
+ export function getLocalizeManager() {
63
+ if (singletonManager.has('@lion/ui::localize::0.x')) {
64
+ return singletonManager.get('@lion/ui::localize::0.x');
65
+ }
66
+
67
+ const localizeManager = new LocalizeManager({
68
+ autoLoadOnLocaleChange: true,
69
+ fallbackLocale: 'en-GB',
70
+ });
71
+ singletonManager.set('@lion/ui::localize::0.x', localizeManager);
72
+
73
+ return localizeManager;
74
+ }
@@ -1,6 +1,6 @@
1
1
  /** @typedef {import('../../types/LocalizeMixinTypes.js').NumberPostProcessor} NumberPostProcessor */
2
2
 
3
- import { localize } from '../singleton.js';
3
+ import { getLocalizeManager } from '../getLocalizeManager.js';
4
4
  import { getLocale } from '../utils/getLocale.js';
5
5
  import { formatNumberToParts } from './formatNumberToParts.js';
6
6
 
@@ -14,12 +14,13 @@ import { formatNumberToParts } from './formatNumberToParts.js';
14
14
  * @returns {string}
15
15
  */
16
16
  export function formatNumber(number, options = /** @type {FormatOptions} */ ({})) {
17
+ const localizeManager = getLocalizeManager();
17
18
  if (number === undefined || number === null) return '';
18
19
  const formattedToParts = formatNumberToParts(number, options);
19
20
  // If number is not a number
20
21
  if (
21
22
  formattedToParts === options.returnIfNaN ||
22
- formattedToParts === localize.formatNumberOptions.returnIfNaN
23
+ formattedToParts === localizeManager.formatNumberOptions.returnIfNaN
23
24
  ) {
24
25
  return /** @type {string} */ (formattedToParts);
25
26
  }
@@ -33,8 +34,8 @@ export function formatNumber(number, options = /** @type {FormatOptions} */ ({})
33
34
 
34
35
  const computedLocale = getLocale(options && options.locale);
35
36
 
36
- if (localize.formatNumberOptions.postProcessors.size > 0) {
37
- Array.from(localize.formatNumberOptions.postProcessors).forEach(([locale, fn]) => {
37
+ if (localizeManager.formatNumberOptions.postProcessors.size > 0) {
38
+ Array.from(localizeManager.formatNumberOptions.postProcessors).forEach(([locale, fn]) => {
38
39
  if (locale === computedLocale) {
39
40
  printNumberOfParts = fn(printNumberOfParts);
40
41
  }
@@ -1,4 +1,4 @@
1
- import { localize } from '../singleton.js';
1
+ import { getLocalizeManager } from '../getLocalizeManager.js';
2
2
  import { formatNumberToParts } from './formatNumberToParts.js';
3
3
  import { forceCurrencyNameForPHPEnGB } from './utils/normalize-get-currency-name/forceCurrencyNameForPHPEnGB.js';
4
4
 
@@ -11,6 +11,7 @@ import { forceCurrencyNameForPHPEnGB } from './utils/normalize-get-currency-name
11
11
  * @returns {string} currency name like 'US dollar'
12
12
  */
13
13
  export function getCurrencyName(currencyIso, options) {
14
+ const localizeManager = getLocalizeManager();
14
15
  const parts = /** @type {FormatNumberPart[]} */ (
15
16
  formatNumberToParts(1, {
16
17
  ...options,
@@ -23,7 +24,7 @@ export function getCurrencyName(currencyIso, options) {
23
24
  .filter(p => p.type === 'currency')
24
25
  .map(o => o.value)
25
26
  .join(' ');
26
- const locale = options?.locale || localize.locale;
27
+ const locale = options?.locale || localizeManager.locale;
27
28
  if (currencyIso === 'PHP' && locale === 'en-GB') {
28
29
  currencyName = forceCurrencyNameForPHPEnGB(currencyName);
29
30
  }
@@ -104,7 +104,8 @@ export function parseNumber(value, options) {
104
104
  if (!containsNumbers) {
105
105
  return undefined;
106
106
  }
107
- const matchedInput = value.match(/[0-9,.\- ]/g);
107
+ const valueM = value.replace(/[−]/g, '-'); // U+002D, Hyphen-Minus, &#45;
108
+ const matchedInput = valueM.match(/[0-9,.\- ]/g);
108
109
  if (!matchedInput) {
109
110
  return undefined;
110
111
  }
@@ -112,7 +113,7 @@ export function parseNumber(value, options) {
112
113
  const parseMode = getParseMode(cleanedInput, options);
113
114
  switch (parseMode) {
114
115
  case 'unparseable': {
115
- const cleanedInputMatchStr = cleanedInput.match(/[0-9]/g)?.join('');
116
+ const cleanedInputMatchStr = cleanedInput.match(/[0-9-]/g)?.join('');
116
117
  if (!cleanedInputMatchStr) {
117
118
  return NaN;
118
119
  }
@@ -1,4 +1,4 @@
1
- import { localize } from '../../singleton.js';
1
+ import { getLocalizeManager } from '../../getLocalizeManager.js';
2
2
 
3
3
  /**
4
4
  * When number is NaN we should return an empty string or returnIfNaN param
@@ -7,5 +7,6 @@ import { localize } from '../../singleton.js';
7
7
  * @returns {string}
8
8
  */
9
9
  export function emptyStringWhenNumberNan(returnIfNaN) {
10
- return returnIfNaN || localize.formatNumberOptions.returnIfNaN;
10
+ const localizeManager = getLocalizeManager();
11
+ return returnIfNaN || localizeManager.formatNumberOptions.returnIfNaN;
11
12
  }
@@ -1,11 +1,8 @@
1
- import { singletonManager } from 'singleton-manager';
2
- import { LocalizeManager } from './LocalizeManager.js';
1
+ import { getLocalizeManager } from './getLocalizeManager.js';
3
2
 
4
- /** @type {LocalizeManager} */
3
+ /**
4
+ * Use the side-effect-free `const localizeManager = getLocalizeManager()` instead.
5
+ * @deprecated
6
+ */
5
7
  // eslint-disable-next-line import/no-mutable-exports
6
- export const localize =
7
- /** @type {LocalizeManager} */ (singletonManager.get('@lion/ui::localize::0.x')) ||
8
- new LocalizeManager({
9
- autoLoadOnLocaleChange: true,
10
- fallbackLocale: 'en-GB',
11
- });
8
+ export const localize = getLocalizeManager();
@@ -1,4 +1,4 @@
1
- import { localize } from '../singleton.js';
1
+ import { getLocalizeManager } from '../getLocalizeManager.js';
2
2
 
3
3
  /**
4
4
  * Gets the locale to use
@@ -7,11 +7,6 @@ import { localize } from '../singleton.js';
7
7
  * @returns {string}
8
8
  */
9
9
  export function getLocale(locale) {
10
- if (locale) {
11
- return locale;
12
- }
13
- if (localize && localize.locale) {
14
- return localize.locale;
15
- }
16
- return 'en-GB';
10
+ const localizeManager = getLocalizeManager();
11
+ return locale || localizeManager?.locale || 'en-GB';
17
12
  }
@@ -4,7 +4,7 @@ import sinon from 'sinon';
4
4
  import { fetchMock } from '@bundled-es-modules/fetch-mock';
5
5
  import { setupFakeImport, resetFakeImport, fakeImport } from '@lion/ui/localize-test-helpers.js';
6
6
 
7
- import { LocalizeManager } from '@lion/ui/localize.js';
7
+ import { LocalizeManager } from '@lion/ui/localize-no-side-effects.js';
8
8
 
9
9
  /**
10
10
  * @param {LocalizeManager} localizeManagerEl
@@ -155,7 +155,7 @@ describe('LocalizeManager', () => {
155
155
  });
156
156
  });
157
157
 
158
- it('prevents mutating existing data for the same locale & namespace', () => {
158
+ it('prevents mutating existing data for the same locale & namespace when "allowOverridesForExistingNamespaces" option is not given in constructor', () => {
159
159
  manager = new LocalizeManager();
160
160
  const { storage } = getProtectedMembers(manager);
161
161
 
@@ -169,6 +169,23 @@ describe('LocalizeManager', () => {
169
169
  'en-GB': { 'lion-hello': { greeting: 'Hi!' } },
170
170
  });
171
171
  });
172
+
173
+ it('allows mutating existing data for the same locale & namespace when "allowOverridesForExistingNamespaces" option is set to "true" in constructor', () => {
174
+ manager = new LocalizeManager({ allowOverridesForExistingNamespaces: true });
175
+ const { storage } = getProtectedMembers(manager);
176
+
177
+ manager.addData('en-GB', 'lion-hello', { greeting: 'Hi!' });
178
+
179
+ expect(storage).to.deep.equal({
180
+ 'en-GB': { 'lion-hello': { greeting: 'Hi!' } },
181
+ });
182
+
183
+ manager.addData('en-GB', 'lion-hello', { greeting: 'Hi!', alternative: 'Hello!' });
184
+
185
+ expect(storage).to.deep.equal({
186
+ 'en-GB': { 'lion-hello': { greeting: 'Hi!', alternative: 'Hello!' } },
187
+ });
188
+ });
172
189
  });
173
190
 
174
191
  describe('loading via dynamic imports', () => {
@@ -12,7 +12,7 @@ import {
12
12
  unsafeStatic,
13
13
  } from '@open-wc/testing';
14
14
  import sinon from 'sinon';
15
- import { localize, LocalizeMixin } from '@lion/ui/localize.js';
15
+ import { LocalizeMixin, getLocalizeManager } from '@lion/ui/localize-no-side-effects.js';
16
16
  import {
17
17
  fakeImport,
18
18
  localizeTearDown,
@@ -26,6 +26,8 @@ import {
26
26
  */
27
27
 
28
28
  describe('LocalizeMixin', () => {
29
+ const localizeManager = getLocalizeManager();
30
+
29
31
  afterEach(() => {
30
32
  resetFakeImport();
31
33
  localizeTearDown();
@@ -48,7 +50,7 @@ describe('LocalizeMixin', () => {
48
50
 
49
51
  setupEmptyFakeImportsFor(['my-element'], ['en-GB']);
50
52
 
51
- const loadNamespaceSpy = sinon.spy(localize, 'loadNamespace');
53
+ const loadNamespaceSpy = sinon.spy(localizeManager, 'loadNamespace');
52
54
  await fixture(html`<${tag}></${tag}>`);
53
55
 
54
56
  expect(loadNamespaceSpy.callCount).to.equal(1);
@@ -88,7 +90,7 @@ describe('LocalizeMixin', () => {
88
90
 
89
91
  setupEmptyFakeImportsFor(['default', 'parent-element', 'child-element'], ['en-GB']);
90
92
 
91
- const loadNamespaceSpy = sinon.spy(localize, 'loadNamespace');
93
+ const loadNamespaceSpy = sinon.spy(localizeManager, 'loadNamespace');
92
94
 
93
95
  await fixture(html`<${tag}></${tag}>`);
94
96
  expect(loadNamespaceSpy.callCount).to.equal(3);
@@ -151,7 +153,7 @@ describe('LocalizeMixin', () => {
151
153
 
152
154
  await el.localizeNamespacesLoaded;
153
155
 
154
- localize.locale = 'nl-NL';
156
+ localizeManager.locale = 'nl-NL';
155
157
  await el.localizeNamespacesLoaded;
156
158
  expect(onLocaleChangedSpy.callCount).to.equal(0);
157
159
 
@@ -159,7 +161,7 @@ describe('LocalizeMixin', () => {
159
161
  wrapper.appendChild(el);
160
162
 
161
163
  // Changing locale will result in onLocaleChanged to be invoked
162
- localize.locale = 'ru-RU';
164
+ localizeManager.locale = 'ru-RU';
163
165
  await el.localizeNamespacesLoaded;
164
166
  expect(onLocaleChangedSpy.callCount).to.equal(2);
165
167
  expect(onLocaleChangedSpy.calledWithExactly('ru-RU', 'nl-NL')).to.be.true;
@@ -183,10 +185,10 @@ describe('LocalizeMixin', () => {
183
185
  onLocaleChanged(newLocale, oldLocale) {
184
186
  super.onLocaleChanged(newLocale, oldLocale);
185
187
 
186
- // Can call localize.msg immediately, without having to await localize.loadingComplete
188
+ // Can call localizeManager.msg immediately, without having to await localizeManager.loadingComplete
187
189
  // This is because localeChanged event is fired only after awaiting loading
188
190
  // unless the user disables _autoLoadOnLocaleChange property
189
- this.foo = localize.msg('my-element:foo');
191
+ this.foo = localizeManager.msg('my-element:foo');
190
192
  }
191
193
  }
192
194
 
@@ -198,11 +200,11 @@ describe('LocalizeMixin', () => {
198
200
  const wrapper = await fixture('<div></div>');
199
201
  wrapper.appendChild(el);
200
202
 
201
- localize.locale = 'nl-NL';
203
+ localizeManager.locale = 'nl-NL';
202
204
  await el.localizeNamespacesLoaded;
203
205
  expect(el.foo).to.equal('bar-nl-NL');
204
206
 
205
- localize.locale = 'ru-RU';
207
+ localizeManager.locale = 'ru-RU';
206
208
  await el.localizeNamespacesLoaded;
207
209
  expect(el.foo).to.equal('bar-ru-RU');
208
210
  });
@@ -233,7 +235,7 @@ describe('LocalizeMixin', () => {
233
235
  await el.localizeNamespacesLoaded;
234
236
  expect(onLocaleUpdatedSpy.callCount).to.equal(1);
235
237
 
236
- localize.locale = 'nl-NL';
238
+ localizeManager.locale = 'nl-NL';
237
239
  await el.localizeNamespacesLoaded;
238
240
  expect(onLocaleUpdatedSpy.callCount).to.equal(2);
239
241
  });
@@ -261,7 +263,7 @@ describe('LocalizeMixin', () => {
261
263
 
262
264
  async onLocaleUpdated() {
263
265
  super.onLocaleUpdated();
264
- this.label = localize.msg('my-element:label');
266
+ this.label = localizeManager.msg('my-element:label');
265
267
  }
266
268
  }
267
269
 
@@ -272,7 +274,7 @@ describe('LocalizeMixin', () => {
272
274
  await nextFrame(); // needed as both are added to the micro task que
273
275
  expect(el.label).to.equal('one');
274
276
 
275
- localize.locale = 'nl-NL';
277
+ localizeManager.locale = 'nl-NL';
276
278
  await el.localizeNamespacesLoaded;
277
279
  expect(el.label).to.equal('two');
278
280
  });
@@ -297,7 +299,7 @@ describe('LocalizeMixin', () => {
297
299
 
298
300
  el.connectedCallback();
299
301
 
300
- localize.locale = 'nl-NL';
302
+ localizeManager.locale = 'nl-NL';
301
303
  await el.localizeNamespacesLoaded;
302
304
 
303
305
  // await next frame for requestUpdate to be fired
@@ -325,7 +327,7 @@ describe('LocalizeMixin', () => {
325
327
  const tagString = defineCE(MyElement);
326
328
  const el = /** @type {MyElement} */ (document.createElement(tagString));
327
329
  el.connectedCallback();
328
- const lionLocalizeMessageSpy = sinon.spy(localize, 'msg');
330
+ const lionLocalizeMessageSpy = sinon.spy(localizeManager, 'msg');
329
331
 
330
332
  const messageDirective = el.msgLit('my-element:greeting');
331
333
  expect(lionLocalizeMessageSpy.callCount).to.equal(0);
@@ -442,7 +444,7 @@ describe('LocalizeMixin', () => {
442
444
  if (el.shadowRoot) {
443
445
  const p = /** @type {HTMLParagraphElement} */ (el.shadowRoot.querySelector('p'));
444
446
  expect(p.innerText).to.equal('Hi!');
445
- localize.locale = 'en-US';
447
+ localizeManager.locale = 'en-US';
446
448
  expect(p.innerText).to.equal('Hi!');
447
449
  await el.localizeNamespacesLoaded;
448
450
  await nextFrame(); // needed because msgLit relies on until directive to re-render