@aemforms/af-formatters 0.22.23 → 0.22.26

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.
@@ -1,53 +1,97 @@
1
1
  /*************************************************************************
2
- * ADOBE CONFIDENTIAL
3
- * ___________________
4
- *
5
- * Copyright 2022 Adobe
6
- * All Rights Reserved.
7
- *
8
- * NOTICE: All information contained herein is, and remains
9
- * the property of Adobe and its suppliers, if any. The intellectual
10
- * and technical concepts contained herein are proprietary to Adobe
11
- * and its suppliers and are protected by all applicable intellectual
12
- * property laws, including trade secret and copyright laws.
13
- * Dissemination of this information or reproduction of this material
14
- * is strictly forbidden unless prior written permission is obtained
15
- * from Adobe.
16
-
17
- * Adobe permits you to use and modify this file solely in accordance with
18
- * the terms of the Adobe license agreement accompanying it.
19
- *************************************************************************/
20
-
2
+ * ADOBE CONFIDENTIAL
3
+ * ___________________
4
+ *
5
+ * Copyright 2022 Adobe
6
+ * All Rights Reserved.
7
+ *
8
+ * NOTICE: All information contained herein is, and remains
9
+ * the property of Adobe and its suppliers, if any. The intellectual
10
+ * and technical concepts contained herein are proprietary to Adobe
11
+ * and its suppliers and are protected by all applicable intellectual
12
+ * property laws, including trade secret and copyright laws.
13
+ * Dissemination of this information or reproduction of this material
14
+ * is strictly forbidden unless prior written permission is obtained
15
+ * from Adobe.
16
+ **************************************************************************/
17
+ /**
18
+ * https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
19
+ * Credit: https://git.corp.adobe.com/dc/dfl/blob/master/src/patterns/parseDateTimeSkeleton.js
20
+ * Created a separate library to be used elsewhere as well.
21
+ */
21
22
  const DATE_TIME_REGEX =
23
+ // eslint-disable-next-line max-len
22
24
  /(?:[Eec]{1,6}|G{1,5}|[Qq]{1,5}|(?:[yYur]+|U{1,5})|[ML]{1,5}|d{1,2}|D{1,3}|F{1}|[abB]{1,5}|[hkHK]{1,2}|w{1,2}|W{1}|m{1,2}|s{1,2}|[zZOvV]{1,5}|[zZOvVxX]{1,3}|S{1,3}|'(?:[^']|'')*')|[^a-zA-Z']+/g;
23
- const ShorthandStyles = ["full", "long", "medium", "short"];
24
- function getSkeleton(skeleton, language) {
25
+
26
+ export const ShorthandStyles = ["full", "long", "medium", "short"]
27
+
28
+ const testDate = new Date(2000, 2, 1, 2, 3, 4);
29
+
30
+ /**
31
+ * Since the formatted month names are different than standalone month names, we need to identify the correct option
32
+ * to pass for formatting
33
+ * @param value {string} formatted value of the month
34
+ * @param language {string} language in which the month is formatted
35
+ * @param isMediumFormatStyle {boolean} if shorthand style used for formatting was medium
36
+ */
37
+ function deduceMonthOption(value,
38
+ language,
39
+ isMediumFormatStyle) {
40
+
41
+ const formattedMarch = value;
42
+ const longMarch = new Intl.DateTimeFormat(language, {month: 'long'}).formatToParts(testDate)[0].value;
43
+ const shortMarch = new Intl.DateTimeFormat(language, {month: 'short'}).formatToParts(testDate)[0].value;
44
+
45
+ const monthOptions = {
46
+ [longMarch]: 'long',
47
+ [shortMarch]: 'short',
48
+ '03': '2-digit',
49
+ '3': 'numeric'
50
+ }
51
+ if (formattedMarch !== undefined) {
52
+ monthOptions[formattedMarch] = isMediumFormatStyle ? 'short' : 'long'
53
+ }
54
+ return monthOptions[value];
55
+ }
56
+
57
+ export function getSkeleton(skeleton, language) {
25
58
  if (ShorthandStyles.find(type => skeleton.includes(type))) {
26
59
  const parsed = parseDateStyle(skeleton, language);
27
- const result = [];
60
+ const result = []
28
61
  const symbols = {
29
62
  month : 'M',
30
63
  year : 'Y',
31
64
  day : 'd'
32
- };
65
+ }
33
66
  parsed.forEach(([type, option, length]) => {
34
67
  if (type in symbols) {
35
- result.push(Array(length).fill(symbols[type]).join(''));
68
+ result.push(Array(length).fill(symbols[type]).join(''))
36
69
  } else if (type === 'literal') {
37
- result.push(option);
70
+ result.push(option)
38
71
  }
39
- });
72
+ })
40
73
  return result.join('');
41
74
  }
42
75
  return skeleton;
43
76
  }
77
+
78
+ /**
79
+ *
80
+ * @param skeleton shorthand style for the date concatenated with shorthand style of time. The
81
+ * Shorthand style for both date and time is one of ['full', 'long', 'medium', 'short'].
82
+ * @param language {string} language to parse the date shorthand style
83
+ * @returns {[*,string][]}
84
+ */
44
85
  function parseDateStyle(skeleton, language) {
45
86
  const options = {};
87
+ // the skeleton could have two keywords -- one for date, one for time
46
88
  const styles = skeleton.split(/\s/).filter(s => s.length);
47
89
  options.dateStyle = styles[0];
48
90
  if (styles.length > 1) options.timeStyle = styles[1];
91
+
49
92
  const testDate = new Date(2000, 2, 1, 2, 3, 4);
50
93
  const parts = new Intl.DateTimeFormat(language, options).formatToParts(testDate);
94
+ // oddly, the formatted month name can be different from the standalone month name
51
95
  const formattedMarch = parts.find(p => p.type === 'month').value;
52
96
  const longMarch = new Intl.DateTimeFormat(language, {month: 'long'}).formatToParts(testDate)[0].value;
53
97
  const shortMarch = new Intl.DateTimeFormat(language, {month: 'short'}).formatToParts(testDate)[0].value;
@@ -71,7 +115,12 @@ function parseDateStyle(skeleton, language) {
71
115
  });
72
116
  return result;
73
117
  }
74
- function parseDateTimeSkeleton(skeleton, language) {
118
+
119
+ /**
120
+ * Parse Date time skeleton into Intl.DateTimeFormatOptions parts
121
+ * Ref: https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
122
+ */
123
+ export function parseDateTimeSkeleton(skeleton, language) {
75
124
  if (ShorthandStyles.find(type => skeleton.includes(type))) {
76
125
  return parseDateStyle(skeleton, language);
77
126
  }
@@ -79,9 +128,11 @@ function parseDateTimeSkeleton(skeleton, language) {
79
128
  skeleton.replace(DATE_TIME_REGEX, match => {
80
129
  const len = match.length;
81
130
  switch (match[0]) {
131
+ // Era
82
132
  case 'G':
83
133
  result.push(['era', len === 4 ? 'long' : len === 5 ? 'narrow' : 'short', len]);
84
134
  break;
135
+ // Year
85
136
  case 'y':
86
137
  result.push(['year', len === 2 ? '2-digit' : 'numeric', len]);
87
138
  break;
@@ -92,13 +143,16 @@ function parseDateTimeSkeleton(skeleton, language) {
92
143
  throw new RangeError(
93
144
  '`Y/u/U/r` (year) patterns are not supported, use `y` instead'
94
145
  );
146
+ // Quarter
95
147
  case 'q':
96
148
  case 'Q':
97
149
  throw new RangeError('`q/Q` (quarter) patterns are not supported');
150
+ // Month
98
151
  case 'M':
99
152
  case 'L':
100
153
  result.push(['month', ['numeric', '2-digit', 'short', 'long', 'narrow'][len - 1], len]);
101
154
  break;
155
+ // Week
102
156
  case 'w':
103
157
  case 'W':
104
158
  throw new RangeError('`w/W` (week) patterns are not supported');
@@ -111,6 +165,7 @@ function parseDateTimeSkeleton(skeleton, language) {
111
165
  throw new RangeError(
112
166
  '`D/F/g` (day) patterns are not supported, use `d` instead'
113
167
  );
168
+ // Weekday
114
169
  case 'E':
115
170
  result.push(['weekday', ['short', 'short', 'short', 'long', 'narrow', 'narrow'][len - 1], len]);
116
171
  break;
@@ -126,14 +181,16 @@ function parseDateTimeSkeleton(skeleton, language) {
126
181
  }
127
182
  result.push(['weekday', ['short', 'long', 'narrow', 'short'][len - 3], len]);
128
183
  break;
129
- case 'a':
184
+ // Period
185
+ case 'a': // AM, PM
130
186
  result.push(['hour12', true, 1]);
131
187
  break;
132
- case 'b':
133
- case 'B':
188
+ case 'b': // am, pm, noon, midnight
189
+ case 'B': // flexible day periods
134
190
  throw new RangeError(
135
191
  '`b/B` (period) patterns are not supported, use `a` instead'
136
192
  );
193
+ // Hour
137
194
  case 'h':
138
195
  result.push(['hourCycle', 'h12']);
139
196
  result.push(['hour', ['numeric', '2-digit'][len - 1], len]);
@@ -156,9 +213,11 @@ function parseDateTimeSkeleton(skeleton, language) {
156
213
  throw new RangeError(
157
214
  '`j/J/C` (hour) patterns are not supported, use `h/H/K/k` instead'
158
215
  );
216
+ // Minute
159
217
  case 'm':
160
218
  result.push(['minute', ['numeric', '2-digit'][len - 1], len]);
161
219
  break;
220
+ // Second
162
221
  case 's':
163
222
  result.push(['second', ['numeric', '2-digit'][len - 1], len]);
164
223
  break;
@@ -169,19 +228,23 @@ function parseDateTimeSkeleton(skeleton, language) {
169
228
  throw new RangeError(
170
229
  '`S/A` (millisecond) patterns are not supported, use `s` instead'
171
230
  );
172
- case 'O':
231
+ // Zone
232
+ case 'O': // timeZone GMT-8 or GMT-08:00
173
233
  result.push(['timeZoneName', len < 4 ? 'shortOffset' : 'longOffset', len]);
174
234
  result.push(['x-timeZoneName', len < 4 ? 'O' : 'OOOO', len]);
175
235
  break;
176
- case 'X':
177
- case 'x':
178
- case 'Z':
236
+ case 'X': // 1, 2, 3, 4: The ISO8601 varios formats
237
+ case 'x': // 1, 2, 3, 4: The ISO8601 varios formats
238
+ case 'Z': // 1..3, 4, 5: The ISO8601 varios formats
239
+ // Z, ZZ, ZZZ should produce -0800
240
+ // ZZZZ should produce GMT-08:00
241
+ // ZZZZZ should produce -8:00 or -07:52:58
179
242
  result.push(['timeZoneName', 'longOffset', 1]);
180
243
  result.push(['x-timeZoneName', match, 1]);
181
244
  break;
182
- case 'z':
183
- case 'v':
184
- case 'V':
245
+ case 'z': // 1..3, 4: specific non-location format
246
+ case 'v': // 1, 4: generic non-location format
247
+ case 'V': // 1, 2, 3, 4: time zone ID or city
185
248
  throw new RangeError(
186
249
  'z/v/V` (timeZone) patterns are not supported, use `X/x/Z/O` instead'
187
250
  );
@@ -195,5 +258,3 @@ function parseDateTimeSkeleton(skeleton, language) {
195
258
  });
196
259
  return result;
197
260
  }
198
-
199
- export { ShorthandStyles, getSkeleton, parseDateTimeSkeleton };
@@ -1,22 +1,20 @@
1
1
  /*************************************************************************
2
- * ADOBE CONFIDENTIAL
3
- * ___________________
4
- *
5
- * Copyright 2022 Adobe
6
- * All Rights Reserved.
7
- *
8
- * NOTICE: All information contained herein is, and remains
9
- * the property of Adobe and its suppliers, if any. The intellectual
10
- * and technical concepts contained herein are proprietary to Adobe
11
- * and its suppliers and are protected by all applicable intellectual
12
- * property laws, including trade secret and copyright laws.
13
- * Dissemination of this information or reproduction of this material
14
- * is strictly forbidden unless prior written permission is obtained
15
- * from Adobe.
2
+ * ADOBE CONFIDENTIAL
3
+ * ___________________
4
+ *
5
+ * Copyright 2022 Adobe
6
+ * All Rights Reserved.
7
+ *
8
+ * NOTICE: All information contained herein is, and remains
9
+ * the property of Adobe and its suppliers, if any. The intellectual
10
+ * and technical concepts contained herein are proprietary to Adobe
11
+ * and its suppliers and are protected by all applicable intellectual
12
+ * property laws, including trade secret and copyright laws.
13
+ * Dissemination of this information or reproduction of this material
14
+ * is strictly forbidden unless prior written permission is obtained
15
+ * from Adobe.
16
+ **************************************************************************/
16
17
 
17
- * Adobe permits you to use and modify this file solely in accordance with
18
- * the terms of the Adobe license agreement accompanying it.
19
- *************************************************************************/
20
-
21
- export { formatDate, parseDate } from './DateParser.js';
22
- export { getSkeleton } from './SkeletonParser.js';
18
+ import { parseDate, formatDate } from './DateParser.js';
19
+ import {getSkeleton} from "./SkeletonParser.js";
20
+ export {parseDate, formatDate, getSkeleton}
package/lib/esm/index.js CHANGED
@@ -1,39 +1,17 @@
1
- /*************************************************************************
2
- * ADOBE CONFIDENTIAL
3
- * ___________________
4
- *
5
- * Copyright 2022 Adobe
6
- * All Rights Reserved.
7
- *
8
- * NOTICE: All information contained herein is, and remains
9
- * the property of Adobe and its suppliers, if any. The intellectual
10
- * and technical concepts contained herein are proprietary to Adobe
11
- * and its suppliers and are protected by all applicable intellectual
12
- * property laws, including trade secret and copyright laws.
13
- * Dissemination of this information or reproduction of this material
14
- * is strictly forbidden unless prior written permission is obtained
15
- * from Adobe.
16
-
17
- * Adobe permits you to use and modify this file solely in accordance with
18
- * the terms of the Adobe license agreement accompanying it.
19
- *************************************************************************/
20
-
21
- import { formatDate, parseDate } from './date/DateParser.js';
22
- export { getSkeleton as parseDateSkeleton } from './date/SkeletonParser.js';
23
- import { formatNumber, parseNumber } from './number/NumberParser.js';
24
- import './number/SkeletonParser.js';
25
- import './number/currencies.js';
1
+ import {parseDate, formatDate, getSkeleton as parseDateSkeleton} from "./date/index.js";
2
+ import {parseNumber, formatNumber} from "./number/NumberParser.js"
26
3
 
27
4
  const getCategory = function (skeleton) {
28
5
  const chkCategory = skeleton?.match(/^(?:(num|date)\|)?(.+)/);
29
6
  return [chkCategory?.[1], chkCategory?.[2]]
30
- };
31
- const format = function (value, locale, skeleton, timezone) {
32
- const [category, skelton] = getCategory(skeleton);
7
+ }
8
+
9
+ export const format = function (value, locale, skeleton, timezone) {
10
+ const [category, skelton] = getCategory(skeleton)
33
11
  switch (category) {
34
12
  case 'date':
35
13
  if (!(value instanceof Date)) {
36
- value = new Date(value);
14
+ value = new Date(value)
37
15
  }
38
16
  return formatDate(value, locale, skelton, timezone)
39
17
  case 'num':
@@ -41,9 +19,10 @@ const format = function (value, locale, skeleton, timezone) {
41
19
  default:
42
20
  throw `unable to deduce the format. The skeleton should be date|<format> for date formats and num|<format> for numbers`
43
21
  }
44
- };
45
- const parse = function (value, locale, skeleton, timezone) {
46
- const [category, skelton] = getCategory(skeleton);
22
+ }
23
+
24
+ export const parse = function (value, locale, skeleton, timezone) {
25
+ const [category, skelton] = getCategory(skeleton)
47
26
  switch (category) {
48
27
  case 'date':
49
28
  return parseDate(value, locale, skelton, timezone)
@@ -52,6 +31,6 @@ const parse = function (value, locale, skeleton, timezone) {
52
31
  default:
53
32
  throw `unable to deduce the format. The skeleton should be date|<format> for date formats and num|<format> for numbers`
54
33
  }
55
- };
34
+ }
56
35
 
57
- export { format, formatDate, formatNumber, parse, parseDate, parseNumber };
36
+ export {parseDate, formatDate, parseDateSkeleton, parseNumber, formatNumber}
@@ -1,51 +1,34 @@
1
- /*************************************************************************
2
- * ADOBE CONFIDENTIAL
3
- * ___________________
4
- *
5
- * Copyright 2022 Adobe
6
- * All Rights Reserved.
7
- *
8
- * NOTICE: All information contained herein is, and remains
9
- * the property of Adobe and its suppliers, if any. The intellectual
10
- * and technical concepts contained herein are proprietary to Adobe
11
- * and its suppliers and are protected by all applicable intellectual
12
- * property laws, including trade secret and copyright laws.
13
- * Dissemination of this information or reproduction of this material
14
- * is strictly forbidden unless prior written permission is obtained
15
- * from Adobe.
1
+ import {parseNumberSkeleton} from "./SkeletonParser.js";
16
2
 
17
- * Adobe permits you to use and modify this file solely in accordance with
18
- * the terms of the Adobe license agreement accompanying it.
19
- *************************************************************************/
20
-
21
- import { parseNumberSkeleton } from './SkeletonParser.js';
22
- import './currencies.js';
23
-
24
- function formatNumber(numberValue, language, skeletn) {
25
- if (skeletn.startsWith('num|')) {
26
- skeletn = skel.split('|')[1];
27
- }
3
+ export function formatNumber(numberValue, language, skeletn) {
28
4
  if (!skeletn) return numberValue
29
- language = language || "en";
5
+ language = language || "en"
30
6
  const {options, order} = parseNumberSkeleton(skeletn, language);
31
7
  return new Intl.NumberFormat(language, options).format(numberValue);
32
8
  }
9
+
33
10
  function getMetaInfo(language, skel) {
34
11
  const parts = {};
12
+ // gather digits and radix symbol
35
13
  let options = new Intl.NumberFormat(language, {style:'decimal', useGrouping:false}).formatToParts(9876543210.1);
36
14
  parts.digits = options.find(p => p.type === 'integer').value.split('').reverse();
37
15
  parts.decimal = options.find(p => p.type === 'decimal').value;
16
+
17
+ // extract type values from the parts
38
18
  const gather = type => {
39
19
  const find = options.find(p => p.type === type);
40
20
  if (find) parts[type] = find.value;
41
21
  };
22
+ // now gather the localized parts that correspond to the provided skeleton.
42
23
  const parsed = parseNumberSkeleton(skel);
43
24
  const nf = new Intl.NumberFormat(language, parsed);
44
25
  options = nf.formatToParts(-987654321);
45
26
  gather('group');
46
27
  gather('minusSign');
47
28
  gather('percentSign');
29
+ // it's possible to have multiple currency representations in a single value
48
30
  parts.currency = options.filter(p => p.type === 'currency').map(p => p.value);
31
+ // collect all literals. Most likely a literal is an accounting bracket
49
32
  parts.literal = options.filter(p => p.type === 'literal').map(p => p.value);
50
33
  options = nf.formatToParts(987654321);
51
34
  gather('plusSign');
@@ -53,11 +36,10 @@ function getMetaInfo(language, skel) {
53
36
  gather('unit');
54
37
  return parts;
55
38
  }
56
- function parseNumber(numberString, language, skel) {
39
+
40
+ export function parseNumber(numberString, language, skel) {
57
41
  try {
58
- if (skel.startsWith('num|')) {
59
- skel = skel.split('|')[1];
60
- }
42
+ // factor will be updated to reflect: negative, percent, exponent etc.
61
43
  let factor = 1;
62
44
  let number = numberString;
63
45
  const meta = getMetaInfo(language, skel);
@@ -92,9 +74,8 @@ function parseNumber(numberString, language, skel) {
92
74
  return numberString;
93
75
  }
94
76
  }
95
- function parseDefaultNumber(numberString, language) {
77
+
78
+ export function parseDefaultNumber(numberString, language) {
96
79
  const currency = currencies[language] || 'USD';
97
80
  return parseNumber(numberString, language, `currency/${currency}`);
98
81
  }
99
-
100
- export { formatNumber, parseDefaultNumber, parseNumber };