anu-verzum 2.2.3 → 2.2.4

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.
@@ -3,7 +3,6 @@ export declare abstract class Component<P extends Record<string, any> = Props, S
3
3
  props: P;
4
4
  state: S;
5
5
  context: Record<string, any>;
6
- /** @internal Set by the reconciler. */
7
6
  __fiber?: any;
8
7
  static isAnuComponent: boolean;
9
8
  constructor(props: P, context?: Record<string, any>);
@@ -6,8 +6,6 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.Component = void 0;
7
7
  var _reconciler = require("../reconciler");
8
8
  class Component {
9
- /** @internal Set by the reconciler. */
10
-
11
9
  static isAnuComponent = true;
12
10
  constructor(props, context) {
13
11
  this.props = props || {};
@@ -7,12 +7,8 @@ exports.createContext = void 0;
7
7
  var _utils = require("../../misc/utils");
8
8
  var _Component = require("./Component");
9
9
  const createContext = context => {
10
- const providerContext = {
11
- defaultContext: {
12
- value: context
13
- },
14
- value: {},
15
- subscribers: new Set()
10
+ const defaultContext = {
11
+ value: context
16
12
  };
17
13
  const getPureProps = props => {
18
14
  const pureProps = {};
@@ -26,17 +22,18 @@ const createContext = context => {
26
22
  class ContextProvider extends _Component.Component {
27
23
  constructor(props) {
28
24
  super(props);
29
- providerContext.value = {
25
+ this.value = {
30
26
  ...getPureProps(props)
31
27
  };
28
+ this.subscribers = new Set();
32
29
  }
33
30
  componentDidUpdate() {
34
31
  const pureProps = getPureProps(this.props);
35
- if (!(0, _utils.deepEqual)(providerContext.value, pureProps)) {
36
- providerContext.value = {
32
+ if (!(0, _utils.deepEqual)(this.value, pureProps)) {
33
+ this.value = {
37
34
  ...pureProps
38
35
  };
39
- providerContext.subscribers.forEach(consumer => consumer.setState());
36
+ this.subscribers.forEach(consumer => consumer.setState());
40
37
  }
41
38
  }
42
39
  render() {
@@ -52,23 +49,45 @@ const createContext = context => {
52
49
  }
53
50
  }
54
51
  }
52
+ const resolveProvider = consumer => {
53
+ let fiber = consumer.__fiber?.parent;
54
+ while (fiber) {
55
+ if (fiber.stateNode instanceof ContextProvider) {
56
+ return fiber.stateNode;
57
+ }
58
+ fiber = fiber.parent;
59
+ }
60
+ return null;
61
+ };
55
62
  class ContextConsumer extends _Component.Component {
56
- componentDidMount() {
57
- providerContext.subscribers.add(this);
63
+ provider = null;
64
+ subscribeTo(provider) {
65
+ if (this.provider === provider) {
66
+ return;
67
+ }
68
+ if (this.provider) {
69
+ this.provider.subscribers.delete(this);
70
+ }
71
+ this.provider = provider;
72
+ if (provider) {
73
+ provider.subscribers.add(this);
74
+ }
58
75
  }
59
76
  componentWillUnmount() {
60
- providerContext.subscribers.delete(this);
77
+ if (this.provider) {
78
+ this.provider.subscribers.delete(this);
79
+ this.provider = null;
80
+ }
61
81
  }
62
82
  render() {
63
83
  const children = this.props.children;
64
- const {
65
- value,
66
- defaultContext
67
- } = providerContext;
68
84
  try {
69
85
  if (!children || children.length !== 1) {
70
86
  throw new Error('Context Component must have exactly one child element!');
71
87
  }
88
+ const provider = resolveProvider(this);
89
+ this.subscribeTo(provider);
90
+ const value = provider ? provider.value : defaultContext.value;
72
91
  const {
73
92
  type
74
93
  } = children[0];
@@ -3,6 +3,7 @@ export interface IntlProviderProps extends Props {
3
3
  locale: string | null;
4
4
  messages: Record<string, Record<string, string>>;
5
5
  defaultLocale?: string;
6
+ options?: FormatNumberOptions & AbbreviateNumberOptions;
6
7
  }
7
8
  export interface FormattedMessageProps extends Props {
8
9
  id: string;
@@ -21,12 +22,12 @@ export interface ParseNumberOptions {
21
22
  locale?: string;
22
23
  }
23
24
  declare const Intl: {
24
- abbreviateNumber: (value: number, options?: AbbreviateNumberOptions) => string | number;
25
+ abbreviateNumber: (value: number | string | null | undefined, options?: FormatNumberOptions & AbbreviateNumberOptions) => string | number | null | undefined;
25
26
  formatNumber: (value: number, options?: FormatNumberOptions) => string;
26
27
  parseNumber: (text: string, options?: ParseNumberOptions) => number | null;
27
28
  FormattedMessage: ({ id, values, defaultMessage }: FormattedMessageProps) => AnuElement;
28
29
  formatMessage: (id: string, values?: Record<string, string | number>, defaultMessage?: string) => string;
29
- Provider: ({ locale, messages, defaultLocale, children }: IntlProviderProps) => AnuElement | null;
30
+ Provider: ({ locale, messages, defaultLocale, options, children }: IntlProviderProps) => AnuElement | null;
30
31
  };
31
32
  export default Intl;
32
33
  export declare const __testing: {
@@ -9,17 +9,44 @@ var _Context = require("./Context");
9
9
  const _Intl = (0, _Context.createContext)({});
10
10
  let __messagesContext = {
11
11
  locale: undefined,
12
- messages: {}
12
+ messages: {},
13
+ options: {}
14
+ };
15
+ const ABBREVIATE_KEYS = ['units', 'decimalPlaces', 'decimalSign'];
16
+ const pickNumberFormatOptions = options => {
17
+ const result = {};
18
+ Object.keys(options).forEach(key => {
19
+ if (key !== 'locale' && ABBREVIATE_KEYS.indexOf(key) === -1 && options[key] !== undefined) {
20
+ result[key] = options[key];
21
+ }
22
+ });
23
+ return result;
24
+ };
25
+ const computeAggregatedOptions = (locale, userOptions = {}) => {
26
+ const engineDefaults = {};
27
+ try {
28
+ Object.assign(engineDefaults, new globalThis.Intl.NumberFormat(locale || undefined).resolvedOptions());
29
+ delete engineDefaults.locale;
30
+ } catch (err) {
31
+ console.error(err);
32
+ }
33
+ const abbreviateOptions = {};
34
+ ABBREVIATE_KEYS.forEach(key => {
35
+ if (userOptions[key] !== undefined) {
36
+ abbreviateOptions[key] = userOptions[key];
37
+ }
38
+ });
39
+ return {
40
+ ...engineDefaults,
41
+ ...pickNumberFormatOptions(userOptions),
42
+ ...abbreviateOptions
43
+ };
13
44
  };
14
-
15
- // `Intl.NumberFormatOptions` here refers to the global ECMAScript Intl namespace
16
- // (type space), not the local `Intl` API object declared at the bottom of this
17
- // file — a `const` introduces only a value binding, so the type reference is safe.
18
-
19
45
  const IntlProvider = ({
20
46
  locale,
21
47
  messages,
22
48
  defaultLocale,
49
+ options,
23
50
  children
24
51
  }) => {
25
52
  let selectedMessage;
@@ -42,7 +69,8 @@ const IntlProvider = ({
42
69
  locale: locale,
43
70
  messages: {
44
71
  ...selectedMessage
45
- }
72
+ },
73
+ options: computeAggregatedOptions(locale, options)
46
74
  };
47
75
  return (0, _elements.createElement)(_Intl.ContextProvider, {
48
76
  locale,
@@ -118,76 +146,89 @@ const formatMessage = (id, values, defaultMessage) => {
118
146
  }
119
147
  return textValue;
120
148
  };
121
-
122
- // Reduce any BCP 47 tag to its lowercase language subtag, e.g. 'hu-HU' / 'HU' → 'hu'.
123
- // Used for the exact dictionary-key lookups below (UNITS / DECIMAL_SIGN), which key
124
- // on the language only — so a Provider locale of 'hu', 'HU', or 'hu-HU' all resolve
125
- // to the same Hungarian entry. (formatNumber/parseNumber keep the full tag, since the
126
- // region subtag is meaningful to Intl.NumberFormat — e.g. en-US vs en-GB vs en-IN.)
127
149
  const getLanguageSubtag = locale => locale ? locale.toLowerCase().split('-')[0] : undefined;
150
+ const resolveLocale = locale => locale || __messagesContext.locale || undefined;
151
+ const getAggregatedNumberFormatOptions = () => pickNumberFormatOptions(__messagesContext.options || {});
152
+ const getDecimalSeparator = (locale, numberingSystem) => {
153
+ try {
154
+ const parts = new globalThis.Intl.NumberFormat(resolveLocale(locale), numberingSystem ? {
155
+ numberingSystem
156
+ } : {}).formatToParts(1.1);
157
+ return parts.find(p => p.type === 'decimal')?.value ?? '.';
158
+ } catch {
159
+ return '.';
160
+ }
161
+ };
128
162
  const abbreviateNumber = (value, options = {}) => {
129
- const getByLocale = values => values[getLanguageSubtag(__messagesContext.locale) || 'default'] || values['default'];
163
+ let numericValue;
164
+ if (typeof value === 'string') {
165
+ const parsed = parseNumber(value, options.locale ? {
166
+ locale: options.locale
167
+ } : {});
168
+ if (parsed === null) {
169
+ return value;
170
+ }
171
+ numericValue = parsed;
172
+ } else if (typeof value === 'number' && !isNaN(value)) {
173
+ numericValue = value;
174
+ } else {
175
+ return value;
176
+ }
177
+ const merged = {
178
+ ...(__messagesContext.options || {}),
179
+ ...options
180
+ };
181
+ const decimalPlaces = merged.decimalPlaces ?? 2;
182
+ const customDecimalSign = merged.decimalSign;
183
+ const getByLocale = values => values[getLanguageSubtag(options.locale || __messagesContext.locale) || 'default'] || values['default'];
130
184
  const UNITS = {
131
185
  hu: ['E', 'm', 'M', 'b'],
132
186
  default: ['K', 'M', 'B', 'T']
133
187
  };
134
- const DECIMAL_SIGN = {
135
- hu: ',',
136
- default: '.'
137
- };
138
- if (typeof value === 'number' && !isNaN(value)) {
139
- const defaultAbbreviateNumberOptions = {
140
- units: getByLocale(UNITS),
141
- decimalPlaces: 2,
142
- decimalSign: getByLocale(DECIMAL_SIGN)
143
- };
144
- const isNegative = value < 0;
145
- const opts = {
146
- ...defaultAbbreviateNumberOptions,
147
- ...options
148
- };
149
- const {
150
- units,
151
- decimalPlaces,
152
- decimalSign
153
- } = opts;
154
- const decPlaces = Math.pow(10, decimalPlaces);
155
- let unit = '';
156
- let result = Math.abs(value);
157
- for (let i = units.length - 1; i >= 0; i--) {
158
- const size = Math.pow(10, (i + 1) * 3);
159
- if (size <= result) {
160
- result = Math.round(result * decPlaces / size) / decPlaces;
161
- if (result === 1000 && i < units.length - 1) {
162
- result = 1;
163
- i++;
164
- }
165
- unit = units[i];
166
- break;
188
+ const units = merged.units ?? getByLocale(UNITS);
189
+ const isNegative = numericValue < 0;
190
+ const decPlaces = Math.pow(10, decimalPlaces);
191
+ let unit = '';
192
+ let result = Math.abs(numericValue);
193
+ for (let i = units.length - 1; i >= 0; i--) {
194
+ const size = Math.pow(10, (i + 1) * 3);
195
+ if (size <= result) {
196
+ result = Math.round(result * decPlaces / size) / decPlaces;
197
+ if (result === 1000 && i < units.length - 1) {
198
+ result = 1;
199
+ i++;
167
200
  }
201
+ unit = units[i];
202
+ break;
168
203
  }
169
- let resultStr = `${result}`;
170
- if (decimalSign) {
171
- resultStr = resultStr.replace('.', decimalSign);
204
+ }
205
+ const numberFormatOptions = pickNumberFormatOptions(options);
206
+ let resultStr = formatNumber(result, {
207
+ maximumFractionDigits: decimalPlaces,
208
+ ...numberFormatOptions,
209
+ locale: options.locale
210
+ });
211
+ if (customDecimalSign) {
212
+ const localeDecimal = getDecimalSeparator(options.locale, numberFormatOptions.numberingSystem ?? __messagesContext.options.numberingSystem);
213
+ if (localeDecimal && localeDecimal !== customDecimalSign) {
214
+ resultStr = resultStr.split(localeDecimal).join(customDecimalSign);
172
215
  }
173
- return `${isNegative ? '-' : ''}${resultStr}${unit}`;
174
216
  }
175
- return value;
217
+ return `${isNegative ? '-' : ''}${resultStr}${unit}`;
176
218
  };
177
-
178
- // The local `Intl` object below shadows the global ECMAScript `Intl`, so number
179
- // formatting/parsing must reach the real `Intl.NumberFormat` via `globalThis.Intl`.
180
- const resolveLocale = locale => locale || __messagesContext.locale || undefined;
181
219
  const formatNumber = (value, options = {}) => {
182
220
  const {
183
- locale,
184
- ...numberFormatOptions
221
+ locale
185
222
  } = options;
186
223
  if (typeof value !== 'number' || isNaN(value)) {
187
224
  return String(value);
188
225
  }
226
+ const effective = {
227
+ ...getAggregatedNumberFormatOptions(),
228
+ ...pickNumberFormatOptions(options)
229
+ };
189
230
  try {
190
- return new globalThis.Intl.NumberFormat(resolveLocale(locale), numberFormatOptions).format(value);
231
+ return new globalThis.Intl.NumberFormat(resolveLocale(locale), effective).format(value);
191
232
  } catch (err) {
192
233
  console.error(err);
193
234
  return String(value);
@@ -197,29 +238,26 @@ const parseNumber = (text, options = {}) => {
197
238
  if (typeof text !== 'string') {
198
239
  return null;
199
240
  }
241
+ const numberingSystem = options.numberingSystem ?? __messagesContext.options.numberingSystem;
200
242
  try {
201
- // Discover the locale's grouping/decimal/minus marks from a known sample,
202
- // then normalize the input back into a plain JS-parseable number string.
203
- const parts = new globalThis.Intl.NumberFormat(resolveLocale(options.locale)).formatToParts(-12345.6);
243
+ const parts = new globalThis.Intl.NumberFormat(resolveLocale(options.locale), {
244
+ ...(numberingSystem ? {
245
+ numberingSystem
246
+ } : {}),
247
+ useGrouping: true
248
+ }).formatToParts(-12345.6);
204
249
  const groupSign = parts.find(p => p.type === 'group')?.value ?? '';
205
250
  const decimalSign = parts.find(p => p.type === 'decimal')?.value ?? '.';
206
251
  const minusSign = parts.find(p => p.type === 'minusSign')?.value ?? '-';
207
252
  let normalized = text.trim();
208
253
  const isNegative = normalized.indexOf('-') > -1 || !!minusSign && normalized.indexOf(minusSign) > -1;
209
-
210
- // Drop grouping separators: the explicit locale group sign plus any
211
- // whitespace (JS `\s` covers NBSP/NNBSP, which several locales group with).
212
254
  if (groupSign) {
213
255
  normalized = normalized.split(groupSign).join('');
214
256
  }
215
257
  normalized = normalized.replace(/\s/g, '');
216
-
217
- // Convert the locale decimal sign to a dot.
218
258
  if (decimalSign && decimalSign !== '.') {
219
259
  normalized = normalized.split(decimalSign).join('.');
220
260
  }
221
-
222
- // Keep only digits and the decimal dot; sign is reapplied from isNegative.
223
261
  normalized = normalized.replace(/[^0-9.]/g, '');
224
262
  if (normalized === '' || normalized === '.') {
225
263
  return null;
@@ -250,7 +288,8 @@ const __testing = exports.__testing = {
250
288
  }
251
289
  __messagesContext = {
252
290
  locale: undefined,
253
- messages: {}
291
+ messages: {},
292
+ options: {}
254
293
  };
255
294
  }
256
295
  };
@@ -51,16 +51,10 @@ const updateDomProperties = (dom, prevProps, nextProps, isSvgElement = false) =>
51
51
  }
52
52
  } else {
53
53
  if (name === 'className' || name === 'htmlFor') {
54
- // Both have working IDL aliases (el.className / el.htmlFor).
55
54
  el[name] = nextProps[name];
56
55
  } else if (name.includes('-') || name === 'role') {
57
56
  dom.setAttribute(name, nextProps[name]);
58
57
  } else if (dom.nodeType === 1 && /[A-Z]/.test(name)) {
59
- // camelCase HTML attribute (inputMode, autoComplete, …): the
60
- // DOM name is the lowercased key, except for the hyphenated
61
- // cases captured in HTML_ATTRIBUTE_NAME_MAP. Setting these as
62
- // IDL properties is unreliable (e.g. el.autoComplete is not a
63
- // reflected property), so route them through setAttribute.
64
58
  dom.setAttribute(HTML_ATTRIBUTE_NAME_MAP[name] ?? name.toLowerCase(), nextProps[name]);
65
59
  } else {
66
60
  el[name] = nextProps[name];
@@ -160,6 +160,7 @@ const updateClassComponent = wipFiber => {
160
160
  instance.state = nextState;
161
161
  wipFiber.partialState = undefined;
162
162
  delete wipFiber.partialStateCallback;
163
+ wipFiber.stateNode.__fiber = wipFiber;
163
164
  const newChildElements = wipFiber.stateNode.render();
164
165
  reconcileChildrenArray(wipFiber, newChildElements);
165
166
  };
package/dist/index.d.ts CHANGED
@@ -101,12 +101,12 @@ declare const Anu: {
101
101
  getAllUrlParamNames: () => string[];
102
102
  };
103
103
  Intl: {
104
- abbreviateNumber: (value: number, options?: import(".").AbbreviateNumberOptions) => string | number;
104
+ abbreviateNumber: (value: number | string | null | undefined, options?: import(".").FormatNumberOptions & import(".").AbbreviateNumberOptions) => string | number | null | undefined;
105
105
  formatNumber: (value: number, options?: import(".").FormatNumberOptions) => string;
106
106
  parseNumber: (text: string, options?: import(".").ParseNumberOptions) => number | null;
107
107
  FormattedMessage: ({ id, values, defaultMessage }: import("./core/components/Intl").FormattedMessageProps) => AnuElement;
108
108
  formatMessage: (id: string, values?: Record<string, string | number>, defaultMessage?: string) => string;
109
- Provider: ({ locale, messages, defaultLocale, children }: import("./core/components/Intl").IntlProviderProps) => AnuElement | null;
109
+ Provider: ({ locale, messages, defaultLocale, options, children }: import("./core/components/Intl").IntlProviderProps) => AnuElement | null;
110
110
  };
111
111
  Connector: {
112
112
  connect: <TState = any, TOwnProps extends Record<string, any> = Record<string, any>, TStateProps extends Record<string, any> = Record<string, any>, TDispatchProps extends Record<string, any> = Record<string, any>>(mapStateToProps?: ((state: TState, ownProps: TOwnProps) => TStateProps) | null, mapDispatchToProps?: ((dispatch: any, ownProps: TOwnProps) => TDispatchProps) | null) => (WrappedComponent: import("./core/elements").ComponentConstructor<TStateProps & TDispatchProps & TOwnProps>) => import("./core/elements").ComponentConstructor;
@@ -15,9 +15,6 @@ const queryAllByLabelText = (container, label) => {
15
15
  }
16
16
  const forAttr = labelEl.getAttribute('for');
17
17
  if (forAttr) {
18
- // The `for` attribute is already an element id, so resolve it with
19
- // getElementById — no CSS.escape needed (jsdom omits the browser-only
20
- // `CSS` global), and it handles any id characters. Scope to container.
21
18
  const doc = container.ownerDocument ?? document;
22
19
  const input = doc.getElementById(forAttr);
23
20
  if (input && container.contains(input)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anu-verzum",
3
- "version": "2.2.3",
3
+ "version": "2.2.4",
4
4
  "description": "A \"React-like\" UI library that supports JSX syntax, Redux-like state management, array-rendering, i18n, routing and many more.",
5
5
  "keywords": [
6
6
  "anu-verzum",