@limetech/lime-elements 40.21.0 → 40.22.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/cjs/language-s0bKUCvO.js +58 -0
  3. package/dist/cjs/lime-elements.cjs.js +1 -1
  4. package/dist/cjs/limel-action-bar_3.cjs.entry.js +2 -2
  5. package/dist/cjs/limel-ai-avatar.cjs.entry.js +2 -2
  6. package/dist/cjs/limel-badge_7.cjs.entry.js +12 -2
  7. package/dist/cjs/limel-callout.cjs.entry.js +2 -2
  8. package/dist/cjs/limel-chart.cjs.entry.js +524 -76
  9. package/dist/cjs/limel-chip-set.cjs.entry.js +2 -2
  10. package/dist/cjs/limel-chip_2.cjs.entry.js +2 -2
  11. package/dist/cjs/limel-code-diff.cjs.entry.js +2 -2
  12. package/dist/cjs/limel-code-editor.cjs.entry.js +2 -2
  13. package/dist/cjs/limel-collapsible-section.cjs.entry.js +2 -2
  14. package/dist/cjs/limel-date-picker.cjs.entry.js +1 -1
  15. package/dist/cjs/limel-dock.cjs.entry.js +2 -2
  16. package/dist/cjs/limel-drag-handle_2.cjs.entry.js +2 -2
  17. package/dist/cjs/limel-email-viewer.cjs.entry.js +2 -2
  18. package/dist/cjs/limel-file-viewer.cjs.entry.js +164 -2
  19. package/dist/cjs/limel-file.cjs.entry.js +2 -2
  20. package/dist/cjs/limel-flatpickr-adapter.cjs.entry.js +2 -2
  21. package/dist/cjs/limel-list-item.cjs.entry.js +2 -2
  22. package/dist/cjs/limel-pagination-jump.cjs.entry.js +2 -2
  23. package/dist/cjs/limel-pagination.cjs.entry.js +14 -26
  24. package/dist/cjs/limel-picker.cjs.entry.js +70 -27
  25. package/dist/cjs/limel-profile-picture.cjs.entry.js +2 -2
  26. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +2 -2
  27. package/dist/cjs/limel-resize-handle.cjs.entry.js +2 -2
  28. package/dist/cjs/limel-side-panel.cjs.entry.js +2 -2
  29. package/dist/cjs/limel-slider.cjs.entry.js +2 -2
  30. package/dist/cjs/limel-snackbar.cjs.entry.js +2 -2
  31. package/dist/cjs/limel-table.cjs.entry.js +2 -2
  32. package/dist/cjs/limel-text-editor-link-menu.cjs.entry.js +2 -2
  33. package/dist/cjs/loader.cjs.js +1 -1
  34. package/dist/cjs/{translations-CqJEWcfw.js → translations-JLP5icQ9.js} +1 -1
  35. package/dist/collection/components/chart/chart.js +116 -45
  36. package/dist/collection/components/chart/format.js +302 -0
  37. package/dist/collection/components/chart/options.js +26 -14
  38. package/dist/collection/components/chart/scale.js +132 -19
  39. package/dist/collection/components/input-field/input-field.js +10 -0
  40. package/dist/collection/components/pagination/pagination.js +13 -25
  41. package/dist/collection/components/picker/picker.js +67 -24
  42. package/dist/collection/util/language.js +29 -0
  43. package/dist/esm/language-VamkUr90.js +55 -0
  44. package/dist/esm/lime-elements.js +1 -1
  45. package/dist/esm/limel-action-bar_3.entry.js +2 -2
  46. package/dist/esm/limel-ai-avatar.entry.js +2 -2
  47. package/dist/esm/limel-badge_7.entry.js +12 -2
  48. package/dist/esm/limel-callout.entry.js +2 -2
  49. package/dist/esm/limel-chart.entry.js +524 -76
  50. package/dist/esm/limel-chip-set.entry.js +2 -2
  51. package/dist/esm/limel-chip_2.entry.js +2 -2
  52. package/dist/esm/limel-code-diff.entry.js +2 -2
  53. package/dist/esm/limel-code-editor.entry.js +2 -2
  54. package/dist/esm/limel-collapsible-section.entry.js +2 -2
  55. package/dist/esm/limel-date-picker.entry.js +1 -1
  56. package/dist/esm/limel-dock.entry.js +2 -2
  57. package/dist/esm/limel-drag-handle_2.entry.js +2 -2
  58. package/dist/esm/limel-email-viewer.entry.js +2 -2
  59. package/dist/esm/limel-file-viewer.entry.js +164 -2
  60. package/dist/esm/limel-file.entry.js +2 -2
  61. package/dist/esm/limel-flatpickr-adapter.entry.js +2 -2
  62. package/dist/esm/limel-list-item.entry.js +2 -2
  63. package/dist/esm/limel-pagination-jump.entry.js +2 -2
  64. package/dist/esm/limel-pagination.entry.js +14 -26
  65. package/dist/esm/limel-picker.entry.js +71 -28
  66. package/dist/esm/limel-profile-picture.entry.js +2 -2
  67. package/dist/esm/limel-prosemirror-adapter.entry.js +2 -2
  68. package/dist/esm/limel-resize-handle.entry.js +2 -2
  69. package/dist/esm/limel-side-panel.entry.js +2 -2
  70. package/dist/esm/limel-slider.entry.js +2 -2
  71. package/dist/esm/limel-snackbar.entry.js +2 -2
  72. package/dist/esm/limel-table.entry.js +2 -2
  73. package/dist/esm/limel-text-editor-link-menu.entry.js +2 -2
  74. package/dist/esm/loader.js +1 -1
  75. package/dist/esm/{translations-kct5Kybu.js → translations-D9ZGrMhe.js} +1 -1
  76. package/dist/lime-elements/lime-elements.esm.js +1 -1
  77. package/dist/lime-elements/{p-6e9eeb0a.entry.js → p-0a7cbdc3.entry.js} +1 -1
  78. package/dist/lime-elements/{p-d35d2859.entry.js → p-0f96e94d.entry.js} +1 -1
  79. package/dist/lime-elements/{p-a7be03e1.entry.js → p-1a0c5e45.entry.js} +1 -1
  80. package/dist/lime-elements/{p-5e408c27.entry.js → p-401d5c92.entry.js} +1 -1
  81. package/dist/lime-elements/{p-6d2d25b9.entry.js → p-430db3b9.entry.js} +1 -1
  82. package/dist/lime-elements/{p-a6c6a31b.entry.js → p-43b62587.entry.js} +1 -1
  83. package/dist/lime-elements/{p-958396b5.entry.js → p-48dccb83.entry.js} +1 -1
  84. package/dist/lime-elements/{p-814b4934.entry.js → p-5c918155.entry.js} +1 -1
  85. package/dist/lime-elements/{p-22e9e327.entry.js → p-66101ed6.entry.js} +1 -1
  86. package/dist/lime-elements/{p-d8559847.entry.js → p-7ab72b52.entry.js} +1 -1
  87. package/dist/lime-elements/{p-7ab726c2.entry.js → p-8d9a2a22.entry.js} +1 -1
  88. package/dist/lime-elements/{p-55fa19ae.entry.js → p-8de4f0f3.entry.js} +1 -1
  89. package/dist/lime-elements/p-8e80a884.entry.js +56 -0
  90. package/dist/lime-elements/{p-bffb5ed2.entry.js → p-99984c66.entry.js} +1 -1
  91. package/dist/lime-elements/{p-56e2fa48.entry.js → p-9d46f720.entry.js} +1 -1
  92. package/dist/lime-elements/p-9e2b426c.entry.js +1 -0
  93. package/dist/lime-elements/{p-Dzp8dXTo.js → p-C9dBa_Im.js} +1 -1
  94. package/dist/lime-elements/p-VamkUr90.js +1 -0
  95. package/dist/lime-elements/{p-3ac39bd5.entry.js → p-b1c7033b.entry.js} +2 -2
  96. package/dist/lime-elements/{p-47fe76a8.entry.js → p-b5451350.entry.js} +1 -1
  97. package/dist/lime-elements/{p-9edd79a8.entry.js → p-c75f3b0d.entry.js} +1 -1
  98. package/dist/lime-elements/{p-4c080cd9.entry.js → p-ca4247c4.entry.js} +1 -1
  99. package/dist/lime-elements/{p-1a7d1d2b.entry.js → p-d53421af.entry.js} +1 -1
  100. package/dist/lime-elements/{p-6d9b89db.entry.js → p-d8901e39.entry.js} +1 -1
  101. package/dist/lime-elements/{p-933ffaa8.entry.js → p-d89553b3.entry.js} +1 -1
  102. package/dist/lime-elements/{p-496b2437.entry.js → p-d93b8ce0.entry.js} +1 -1
  103. package/dist/lime-elements/{p-23067836.entry.js → p-e97340c4.entry.js} +1 -1
  104. package/dist/lime-elements/{p-7b87e547.entry.js → p-ec0c113d.entry.js} +1 -1
  105. package/dist/lime-elements/p-f197000b.entry.js +159 -0
  106. package/dist/lime-elements/{p-07f3565b.entry.js → p-f6bc6658.entry.js} +1 -1
  107. package/dist/lime-elements/{p-7fcd48c6.entry.js → p-fa1aa440.entry.js} +1 -1
  108. package/dist/types/components/chart/chart.d.ts +32 -7
  109. package/dist/types/components/chart/chart.types.d.ts +15 -1
  110. package/dist/types/components/chart/format.d.ts +54 -0
  111. package/dist/types/components/chart/options.d.ts +3 -4
  112. package/dist/types/components/chart/scale.d.ts +39 -7
  113. package/dist/types/components/input-field/input-field.d.ts +1 -0
  114. package/dist/types/components/pagination/pagination.d.ts +6 -12
  115. package/dist/types/components/picker/picker.d.ts +10 -2
  116. package/dist/types/components.d.ts +36 -8
  117. package/dist/types/util/language.d.ts +19 -0
  118. package/package.json +2 -2
  119. package/dist/cjs/language-v-MMS6ym.js +0 -28
  120. package/dist/esm/language-ChagAaNf.js +0 -26
  121. package/dist/lime-elements/p-7d6a12fe.entry.js +0 -56
  122. package/dist/lime-elements/p-ChagAaNf.js +0 -1
  123. package/dist/lime-elements/p-a807a45f.entry.js +0 -159
  124. package/dist/lime-elements/p-cd479c23.entry.js +0 -1
@@ -6,7 +6,8 @@ import { SVGRenderer } from "echarts/renderers";
6
6
  import translate from "../../global/translations";
7
7
  import { findNavigationTarget, findTabStop, isNavigationKey, NO_NAVIGATION_TARGET, } from "../../util/keyboard-navigation";
8
8
  import { drawChart, fitItemTexts, getItemIndex, getItemLabel, locateItem, } from "./options";
9
- import { formatValue, getAxisRange, MAX_AXIS_STEPS, usesAxisIncrement, } from "./scale";
9
+ import { getAxisRange, isDate, MAX_AXIS_STEPS, usesAxisIncrement, } from "./scale";
10
+ import { canWriteIn, formatValue, isCurrencyCode } from "./format";
10
11
  import { createThemeReader, watchTheme, } from "./theme";
11
12
  const orientations = ['horizontal', 'vertical'];
12
13
  const extensions = [
@@ -29,6 +30,8 @@ const extensions = [
29
30
  * @exampleComponent limel-example-chart-orientation
30
31
  * @exampleComponent limel-example-chart-max-value
31
32
  * @exampleComponent limel-example-chart-display-item-percentage
33
+ * @exampleComponent limel-example-chart-value-format
34
+ * @exampleComponent limel-example-chart-value-format-date
32
35
  * @exampleComponent limel-example-chart-type-bar
33
36
  * @exampleComponent limel-example-chart-type-dot
34
37
  * @exampleComponent limel-example-chart-type-area
@@ -53,8 +56,8 @@ const extensions = [
53
56
  export class Chart {
54
57
  constructor() {
55
58
  /**
56
- * Defines the language for translations.
57
- * Will translate the translatable strings on the components.
59
+ * Defines the language for translations, and for the way values are
60
+ * written, such as `1 234,5` in Swedish.
58
61
  */
59
62
  this.language = 'en';
60
63
  /**
@@ -94,6 +97,16 @@ export class Chart {
94
97
  * Does not have any effect on chart types which generate circular forms.
95
98
  */
96
99
  this.orientation = 'landscape';
100
+ /**
101
+ * Defines how values are written: as numbers, as percentages where
102
+ * `0.25` is `25 %`, as amounts of `currency`, or as dates. With `date`,
103
+ * each value is a date in milliseconds since 1970, and the value axis
104
+ * steps through days, weeks, months or years. The axes, the values
105
+ * shown with `displayItemValue`, the tooltips, and the screen reader
106
+ * table all follow it, in the chart's `language`. An item's
107
+ * `formattedValue` is shown as it is.
108
+ */
109
+ this.valueFormat = 'number';
97
110
  /**
98
111
  * Indicates whether the chart is in a loading state.
99
112
  */
@@ -103,6 +116,7 @@ export class Chart {
103
116
  */
104
117
  this.focusedIndex = 0;
105
118
  this.themeMayHaveChanged = false;
119
+ this.warnings = new Map();
106
120
  this.invalidColors = new Set();
107
121
  this.setPlot = (element) => {
108
122
  this.plot = element;
@@ -167,9 +181,14 @@ export class Chart {
167
181
  };
168
182
  }
169
183
  componentWillLoad() {
170
- this.warnAboutAxisIncrement();
171
184
  this.warnAboutOldTypeName();
172
185
  }
186
+ componentWillRender() {
187
+ this.warnOnChange('axisIncrement', this.getAxisIncrementWarning());
188
+ this.warnOnChange('language', this.getLanguageWarning());
189
+ this.warnOnChange('currency', this.getCurrencyWarning());
190
+ this.warnOnChange('items', this.getDateWarning());
191
+ }
173
192
  connectedCallback() {
174
193
  this.draw();
175
194
  }
@@ -186,25 +205,60 @@ export class Chart {
186
205
  (_c = this.themeReader) === null || _c === void 0 ? void 0 : _c.dispose();
187
206
  this.themeReader = undefined;
188
207
  }
189
- warnAboutAxisIncrement() {
190
- const warning = this.getAxisIncrementWarning();
191
- if (warning && warning !== this.axisIncrementWarning) {
208
+ warnOnChange(prop, warning) {
209
+ if (warning && warning !== this.warnings.get(prop)) {
192
210
  console.warn(warning);
193
211
  }
194
- this.axisIncrementWarning = warning;
212
+ this.warnings.set(prop, warning);
195
213
  }
196
214
  warnAboutOldTypeName() {
197
215
  if (this.type === 'stacked-bar') {
198
216
  console.warn('limel-chart: the type `stacked-bar` is now called `proportion-bar`. It draws the same chart, but please use the new name.');
199
217
  }
200
218
  }
219
+ getLanguageWarning() {
220
+ if (canWriteIn(this.language)) {
221
+ return;
222
+ }
223
+ return `limel-chart: \`language\` must be one that this browser can write values in, such as \`sv\`, but was ${JSON.stringify(this.language)}. Writing the values in English instead.`;
224
+ }
225
+ getCurrencyWarning() {
226
+ if (this.valueFormat !== 'currency' || isCurrencyCode(this.currency)) {
227
+ return;
228
+ }
229
+ return `limel-chart: \`currency\` must be a three-letter code such as \`SEK\` when \`valueFormat\` is \`currency\`, but was ${JSON.stringify(this.currency)}. Writing the values as numbers instead.`;
230
+ }
231
+ getDateWarning() {
232
+ var _a;
233
+ if (this.valueFormat !== 'date') {
234
+ return;
235
+ }
236
+ const wrong = ((_a = this.items) !== null && _a !== void 0 ? _a : [])
237
+ .flatMap((item) => [item.value].flat())
238
+ .find((value) => !isDate(value));
239
+ if (wrong === undefined) {
240
+ return;
241
+ }
242
+ const shown = typeof wrong === 'number' ? wrong : JSON.stringify(wrong);
243
+ return `limel-chart: with \`valueFormat\` \`date\`, each value must be a date in milliseconds since 1970, as \`Date.getTime()\` gives, but was ${shown}. Showing it as it is.`;
244
+ }
245
+ get valueStyle() {
246
+ return {
247
+ type: this.chartType,
248
+ language: this.language,
249
+ valueFormat: this.valueFormat,
250
+ currency: this.currency,
251
+ };
252
+ }
201
253
  get chartType() {
202
254
  return this.type === 'stacked-bar' ? 'proportion-bar' : this.type;
203
255
  }
204
256
  getAxisIncrementWarning() {
205
257
  var _a;
206
258
  const increment = this.axisIncrement;
207
- if (increment === undefined || increment === null) {
259
+ if (increment === undefined ||
260
+ increment === null ||
261
+ this.valueFormat === 'date') {
208
262
  return;
209
263
  }
210
264
  if (increment <= 0 || Number.isNaN(increment)) {
@@ -244,15 +298,10 @@ export class Chart {
244
298
  renderItems() {
245
299
  var _a, _b;
246
300
  const tabStop = findTabStop((_a = this.items) !== null && _a !== void 0 ? _a : [], this.focusedIndex);
247
- return (_b = this.items) === null || _b === void 0 ? void 0 : _b.map((item, index) => (h("tr", { "data-index": index, "aria-description": this.getRowDescription(index), tabIndex: index === tabStop ? 0 : -1, role: item.clickable ? 'button' : null, onClick: this.handleClick, onKeyDown: this.handleKeyDown, onFocus: this.handleFocus, onBlur: this.handleBlur }, h("td", null, item.text), h("td", null, formatValue(item, this.chartType)))));
301
+ return (_b = this.items) === null || _b === void 0 ? void 0 : _b.map((item, index) => (h("tr", { "data-index": index, "aria-description": this.getRowDescription(index), tabIndex: index === tabStop ? 0 : -1, role: item.clickable ? 'button' : null, onClick: this.handleClick, onKeyDown: this.handleKeyDown, onFocus: this.handleFocus, onBlur: this.handleBlur }, h("td", null, item.text), h("td", null, formatValue(item, this.valueStyle)))));
248
302
  }
249
303
  getRowDescription(index) {
250
- const label = getItemLabel({
251
- items: this.items,
252
- type: this.chartType,
253
- maxValue: this.maxValue,
254
- displayItemPercentage: this.displayItemPercentage,
255
- }, index);
304
+ const label = getItemLabel(Object.assign(Object.assign({}, this.valueStyle), { items: this.items, maxValue: this.maxValue, displayItemPercentage: this.displayItemPercentage }), index);
256
305
  return label === this.items[index].text ? undefined : label;
257
306
  }
258
307
  draw() {
@@ -375,25 +424,13 @@ export class Chart {
375
424
  }
376
425
  getSettings() {
377
426
  var _a;
378
- return {
379
- items: ((_a = this.items) !== null && _a !== void 0 ? _a : []).map((item) => ({
427
+ return Object.assign(Object.assign({ items: ((_a = this.items) !== null && _a !== void 0 ? _a : []).map((item) => ({
380
428
  text: item.text,
381
429
  value: item.value,
382
430
  formattedValue: item.formattedValue,
383
431
  color: item.color,
384
432
  clickable: item.clickable,
385
- })),
386
- type: this.chartType,
387
- orientation: this.orientation,
388
- maxValue: this.maxValue,
389
- axisIncrement: this.axisIncrement,
390
- displayAxisLabels: this.displayAxisLabels,
391
- itemsLabel: this.itemsLabel,
392
- valuesLabel: this.valuesLabel,
393
- displayItemText: this.displayItemText,
394
- displayItemValue: this.displayItemValue,
395
- displayItemPercentage: this.displayItemPercentage,
396
- };
433
+ })) }, this.valueStyle), { orientation: this.orientation, maxValue: this.maxValue, axisIncrement: this.axisIncrement, displayAxisLabels: this.displayAxisLabels, itemsLabel: this.itemsLabel, valuesLabel: this.valuesLabel, displayItemText: this.displayItemText, displayItemValue: this.displayItemValue, displayItemPercentage: this.displayItemPercentage });
397
434
  }
398
435
  withItemColors(settings, theme) {
399
436
  return Object.assign(Object.assign({}, settings), { items: settings.items.map((item) => (Object.assign(Object.assign({}, item), { color: this.getItemColor(item, theme) }))) });
@@ -499,7 +536,7 @@ export class Chart {
499
536
  "optional": false,
500
537
  "docs": {
501
538
  "tags": [],
502
- "text": "Defines the language for translations.\nWill translate the translatable strings on the components."
539
+ "text": "Defines the language for translations, and for the way values are\nwritten, such as `1 234,5` in Swedish."
503
540
  },
504
541
  "getter": false,
505
542
  "setter": false,
@@ -727,7 +764,7 @@ export class Chart {
727
764
  "optional": true,
728
765
  "docs": {
729
766
  "tags": [],
730
- "text": "Sets the value used to scale item values.\nFor `proportion-bar`, `pie`, `doughnut`, and `ring`, it is the denominator\nused as the whole. It defaults to the combined item values or range\nextents. The combined size can exceed `maxValue` and produce a total\nabove 100%.\nFor `bar`, `dot`, `area`, and `line`, it is the requested upper bound.\nThe chart can round that bound up to the next `axisIncrement`.\nHas no effect on `nps` or `scatter`."
767
+ "text": "Sets the value used to scale item values.\nFor `proportion-bar`, `pie`, `doughnut`, and `ring`, it is the denominator\nused as the whole. It defaults to the combined item values or range\nextents. The combined size can exceed `maxValue` and produce a total\nabove 100%.\nFor `bar`, `dot`, `area`, and `line`, it is the requested upper bound.\nThe chart can round that bound up to the next `axisIncrement`, or to\nthe end of its day when `valueFormat` is `date`.\nHas no effect on `nps` or `scatter`."
731
768
  },
732
769
  "getter": false,
733
770
  "setter": false,
@@ -746,13 +783,59 @@ export class Chart {
746
783
  "optional": true,
747
784
  "docs": {
748
785
  "tags": [],
749
- "text": "Sets the axis increment for `bar`, `dot`, `area`, and `line` charts.\nWhen it is not set, or not above zero, the chart works out a step\nfrom the values. It also does that when the step would divide the\naxis into more than 100 parts, so that it does not draw too many lines.\nHas no effect on other chart types."
786
+ "text": "Sets the axis increment for `bar`, `dot`, `area`, and `line` charts.\nWhen it is not set, or not above zero, the chart works out a step\nfrom the values. It also does that when the step would divide the\naxis into more than 100 parts, so that it does not draw too many lines.\nHas no effect on other chart types, or when `valueFormat` is `date`,\nwhere the axis steps through days, weeks, months or years by itself."
750
787
  },
751
788
  "getter": false,
752
789
  "setter": false,
753
790
  "reflect": true,
754
791
  "attribute": "axis-increment"
755
792
  },
793
+ "valueFormat": {
794
+ "type": "string",
795
+ "mutable": false,
796
+ "complexType": {
797
+ "original": "ChartValueFormat",
798
+ "resolved": "\"currency\" | \"date\" | \"number\" | \"percent\"",
799
+ "references": {
800
+ "ChartValueFormat": {
801
+ "location": "import",
802
+ "path": "./chart.types",
803
+ "id": "src/components/chart/chart.types.ts::ChartValueFormat",
804
+ "referenceLocation": "ChartValueFormat"
805
+ }
806
+ }
807
+ },
808
+ "required": false,
809
+ "optional": false,
810
+ "docs": {
811
+ "tags": [],
812
+ "text": "Defines how values are written: as numbers, as percentages where\n`0.25` is `25 %`, as amounts of `currency`, or as dates. With `date`,\neach value is a date in milliseconds since 1970, and the value axis\nsteps through days, weeks, months or years. The axes, the values\nshown with `displayItemValue`, the tooltips, and the screen reader\ntable all follow it, in the chart's `language`. An item's\n`formattedValue` is shown as it is."
813
+ },
814
+ "getter": false,
815
+ "setter": false,
816
+ "reflect": true,
817
+ "attribute": "value-format",
818
+ "defaultValue": "'number'"
819
+ },
820
+ "currency": {
821
+ "type": "string",
822
+ "mutable": false,
823
+ "complexType": {
824
+ "original": "string",
825
+ "resolved": "string",
826
+ "references": {}
827
+ },
828
+ "required": false,
829
+ "optional": true,
830
+ "docs": {
831
+ "tags": [],
832
+ "text": "The currency of the values when `valueFormat` is `currency`, as a\nthree-letter code such as `SEK`. Without one, the values are written\nas numbers."
833
+ },
834
+ "getter": false,
835
+ "setter": false,
836
+ "reflect": true,
837
+ "attribute": "currency"
838
+ },
756
839
  "loading": {
757
840
  "type": "boolean",
758
841
  "mutable": false,
@@ -808,18 +891,6 @@ export class Chart {
808
891
  static get elementRef() { return "host"; }
809
892
  static get watchers() {
810
893
  return [{
811
- "propName": "axisIncrement",
812
- "methodName": "warnAboutAxisIncrement"
813
- }, {
814
- "propName": "items",
815
- "methodName": "warnAboutAxisIncrement"
816
- }, {
817
- "propName": "maxValue",
818
- "methodName": "warnAboutAxisIncrement"
819
- }, {
820
- "propName": "type",
821
- "methodName": "warnAboutAxisIncrement"
822
- }, {
823
894
  "propName": "type",
824
895
  "methodName": "warnAboutOldTypeName"
825
896
  }];
@@ -0,0 +1,302 @@
1
+ import { getSupportedLocale } from "../../util/language";
2
+ import { getMaximumValue, getMinimumValue, isDate, } from "./scale";
3
+ const FALLBACK_LOCALE = 'en';
4
+ const PERCENT = 100;
5
+ /**
6
+ * The largest value on an axis from which its values are shortened,
7
+ * such as `1.2M` for 1 200 000.
8
+ */
9
+ const SHORTEN_FROM = 10000;
10
+ /**
11
+ * The most decimals `Intl` writes in every browser.
12
+ */
13
+ const MAX_DECIMALS = 20;
14
+ /**
15
+ * The most significant digits `Intl` writes.
16
+ */
17
+ const MAX_SIGNIFICANT_DIGITS = 21;
18
+ /**
19
+ * Up to three decimals, and six significant digits below one, so `0.0004`
20
+ * is not written as `0`.
21
+ */
22
+ const NUMBERS = {
23
+ options: { maximumFractionDigits: 3 },
24
+ small: { maximumSignificantDigits: 6 },
25
+ smallBelow: 1,
26
+ };
27
+ /**
28
+ * Up to one decimal, and two significant digits below 1 %, so `0.0004` is
29
+ * written as `0.04%` rather than `0%`.
30
+ */
31
+ const PERCENTAGES = {
32
+ options: { style: 'percent', maximumFractionDigits: 1 },
33
+ small: { style: 'percent', maximumSignificantDigits: 2 },
34
+ smallBelow: 0.01,
35
+ };
36
+ const SHARE_OPTIONS = {
37
+ style: 'percent',
38
+ minimumFractionDigits: 2,
39
+ maximumFractionDigits: 2,
40
+ };
41
+ /**
42
+ * The ticks of an axis of dates that are not known, whose dates are all
43
+ * written with their year.
44
+ */
45
+ const UNKNOWN_DATE_TICKS = { values: [], unit: 'day' };
46
+ const locales = new Map();
47
+ const numberFormats = new Map();
48
+ const currencyCodes = new Map();
49
+ const dateFormats = new Map();
50
+ /**
51
+ * The item's value as text: its `formattedValue` when set, otherwise its
52
+ * value, range, or scatter point, written in the chart's language and
53
+ * value format.
54
+ * @param item - the item
55
+ * @param style - how the chart writes values
56
+ */
57
+ export function formatValue(item, style) {
58
+ const { value, formattedValue } = item;
59
+ if (formattedValue) {
60
+ return formattedValue;
61
+ }
62
+ const write = getValueWriter(style);
63
+ if (!Array.isArray(value)) {
64
+ return write(value);
65
+ }
66
+ const [first, second] = value.map((part) => write(part));
67
+ if (style.type === 'scatter') {
68
+ return writePoint(first, second);
69
+ }
70
+ const [start, end] = [getMinimumValue(item), getMaximumValue(item)];
71
+ if (style.valueFormat === 'date' && isDate(start) && isDate(end)) {
72
+ return getDateFormat(style.language).formatRange(start, end);
73
+ }
74
+ return `${first} — ${second}`;
75
+ }
76
+ /**
77
+ * Writes the values of an axis in the chart's language and value format,
78
+ * with as many decimals as the step between them has. Amounts leave out
79
+ * their currency, which the item values and tooltips show. On an axis
80
+ * that reaches 10 000 or more, every number is shortened, such as `1.2M`,
81
+ * so the values stay short and alike. Dates are written by what the axis
82
+ * steps through: days, months or years, with the year on the first date
83
+ * and on the first date of each new year.
84
+ * @param range - the range of the axis
85
+ * @param style - how the chart writes values
86
+ * @param dates - the dates an axis of dates marks
87
+ */
88
+ export function getAxisValueFormatter(range, style, dates) {
89
+ const locale = getLocale(style.language);
90
+ if (style.valueFormat === 'date') {
91
+ return getDateAxisFormatter(locale, dates);
92
+ }
93
+ if (style.valueFormat === 'percent') {
94
+ const percentages = getNumberFormat(locale, {
95
+ style: 'percent',
96
+ maximumFractionDigits: countDecimals(range.increment * PERCENT),
97
+ });
98
+ return (value) => percentages.format(value);
99
+ }
100
+ const largest = Math.max(Math.abs(range.minValue), Math.abs(range.maxValue));
101
+ const numbers = getNumberFormat(locale, largest >= SHORTEN_FROM
102
+ ? {
103
+ notation: 'compact',
104
+ maximumSignificantDigits: countSignificantDigits(largest, range.increment),
105
+ }
106
+ : { maximumFractionDigits: countDecimals(range.increment) });
107
+ return (value) => numbers.format(value);
108
+ }
109
+ /**
110
+ * An item's share of the whole as a percentage with two decimals, such as
111
+ * `25.00%`, written in the chart's language.
112
+ * @param share - the share, in percent
113
+ * @param language - the language of the chart
114
+ */
115
+ export function formatShare(share, language) {
116
+ return getNumberFormat(getLocale(language), SHARE_OPTIONS).format(share / PERCENT);
117
+ }
118
+ /**
119
+ * Whether `currency` can be written as one: a three-letter code such as
120
+ * `SEK`.
121
+ * @param currency - the currency of the chart
122
+ */
123
+ export function isCurrencyCode(currency) {
124
+ let valid = currencyCodes.get(currency);
125
+ if (valid === undefined) {
126
+ valid = canWriteAmountsIn(currency);
127
+ currencyCodes.set(currency, valid);
128
+ }
129
+ return valid;
130
+ }
131
+ function canWriteAmountsIn(currency) {
132
+ try {
133
+ getNumberFormat(FALLBACK_LOCALE, { style: 'currency', currency });
134
+ return true;
135
+ }
136
+ catch (_a) {
137
+ return false;
138
+ }
139
+ }
140
+ /**
141
+ * Whether values can be written in `language`. When they cannot, they are
142
+ * written in English.
143
+ * @param language - the language of the chart
144
+ */
145
+ export function canWriteIn(language) {
146
+ return findLocale(language) !== undefined;
147
+ }
148
+ function getValueWriter(style) {
149
+ if (style.valueFormat === 'date') {
150
+ const dates = getDateFormat(style.language);
151
+ return (value) => (isDate(value) ? dates.format(value) : `${value}`);
152
+ }
153
+ const numbers = getValueFormat(style);
154
+ return (value) => numbers.format(value);
155
+ }
156
+ /**
157
+ * Writes the dates on an axis by what it steps through: day and month for
158
+ * days and weeks, the month for months, and the year for years. The first
159
+ * date, and the first date of each new year, also get their year, except
160
+ * January among months, which is written as its year alone.
161
+ * @param locale - the locale of the chart
162
+ * @param ticks - the dates the axis marks
163
+ */
164
+ function getDateAxisFormatter(locale, ticks = UNKNOWN_DATE_TICKS) {
165
+ const years = getDateTimeFormat(locale, { year: 'numeric' });
166
+ if (ticks.unit === 'year') {
167
+ return (value) => years.format(value);
168
+ }
169
+ const parts = ticks.unit === 'month'
170
+ ? { month: 'short' }
171
+ : { day: 'numeric', month: 'short' };
172
+ const dates = getDateTimeFormat(locale, parts);
173
+ const datesWithYear = getDateTimeFormat(locale, Object.assign(Object.assign({}, parts), { year: 'numeric' }));
174
+ return (value) => {
175
+ if (!isFirstOfItsYear(value, ticks.values)) {
176
+ return dates.format(value);
177
+ }
178
+ return ticks.unit === 'month' && new Date(value).getMonth() === 0
179
+ ? years.format(value)
180
+ : datesWithYear.format(value);
181
+ };
182
+ }
183
+ /**
184
+ * Whether `value` is the first of `ticks` in its year. A date that is not
185
+ * one of them counts as the first, so that it gets its year.
186
+ * @param value - the date
187
+ * @param ticks - the dates an axis marks
188
+ */
189
+ function isFirstOfItsYear(value, ticks) {
190
+ const index = ticks.indexOf(value);
191
+ return (index <= 0 ||
192
+ new Date(ticks[index - 1]).getFullYear() !==
193
+ new Date(value).getFullYear());
194
+ }
195
+ function getDateFormat(language) {
196
+ return getDateTimeFormat(getLocale(language), { dateStyle: 'medium' });
197
+ }
198
+ function getValueFormat(style) {
199
+ const locale = getLocale(style.language);
200
+ if (style.valueFormat === 'percent') {
201
+ return getNumberWriter(locale, PERCENTAGES);
202
+ }
203
+ if (style.valueFormat === 'currency' && isCurrencyCode(style.currency)) {
204
+ return getNumberFormat(locale, {
205
+ style: 'currency',
206
+ currency: style.currency,
207
+ trailingZeroDisplay: 'stripIfInteger',
208
+ });
209
+ }
210
+ return getNumberWriter(locale, NUMBERS);
211
+ }
212
+ /**
213
+ * The two values of a scatter point, separated by a semicolon when either
214
+ * of them has a comma in it, such as `(1,5; 2,5)` in Swedish.
215
+ * @param first - the first value, as text
216
+ * @param second - the second value, as text
217
+ */
218
+ function writePoint(first, second) {
219
+ const separator = `${first}${second}`.includes(',') ? '; ' : ', ';
220
+ return `(${first}${separator}${second})`;
221
+ }
222
+ function countDecimals(value) {
223
+ if (!Number.isFinite(value)) {
224
+ return 0;
225
+ }
226
+ return Math.min(MAX_DECIMALS, Math.max(0, -getLowestDigit(value)));
227
+ }
228
+ /**
229
+ * How many significant digits the values of an axis need for every step
230
+ * between them to show, such as four for `1.025M` on an axis that steps
231
+ * by 25 000 up to 2 000 000.
232
+ * @param largest - the largest value on the axis, ignoring its sign
233
+ * @param increment - the step between the values
234
+ */
235
+ function countSignificantDigits(largest, increment) {
236
+ if (!Number.isFinite(increment)) {
237
+ return MAX_SIGNIFICANT_DIGITS;
238
+ }
239
+ const digits = Math.floor(Math.log10(largest)) - getLowestDigit(increment);
240
+ return Math.min(MAX_SIGNIFICANT_DIGITS, Math.max(1, digits + 1));
241
+ }
242
+ /**
243
+ * The power of ten of the last digit of `value` that is not zero, such as
244
+ * 3 for 25 000 and -2 for 0.25.
245
+ * @param value - a finite number
246
+ */
247
+ function getLowestDigit(value) {
248
+ var _a, _b;
249
+ const [digits, exponent] = Number(value.toPrecision(12))
250
+ .toExponential()
251
+ .split('e', 2);
252
+ const decimals = (_b = (_a = digits.split('.', 2)[1]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
253
+ return Number(exponent) - decimals;
254
+ }
255
+ function getNumberWriter(locale, style) {
256
+ const numbers = getNumberFormat(locale, style.options);
257
+ const small = getNumberFormat(locale, style.small);
258
+ return {
259
+ format: (value) => value !== 0 && Math.abs(value) < style.smallBelow
260
+ ? small.format(value)
261
+ : numbers.format(value),
262
+ };
263
+ }
264
+ /**
265
+ * Making an `Intl.NumberFormat` costs far more than using one, and values
266
+ * are written on every render, so each one is made once.
267
+ * @param locale - the locale to write in
268
+ * @param options - how to write the numbers
269
+ */
270
+ function getNumberFormat(locale, options) {
271
+ const key = `${locale} ${JSON.stringify(options)}`;
272
+ let format = numberFormats.get(key);
273
+ if (!format) {
274
+ format = new Intl.NumberFormat(locale, options);
275
+ numberFormats.set(key, format);
276
+ }
277
+ return format;
278
+ }
279
+ function getDateTimeFormat(locale, options) {
280
+ const key = `${locale} ${JSON.stringify(options)}`;
281
+ let format = dateFormats.get(key);
282
+ if (!format) {
283
+ format = new Intl.DateTimeFormat(locale, options);
284
+ dateFormats.set(key, format);
285
+ }
286
+ return format;
287
+ }
288
+ /**
289
+ * The locale to write values in: the chart's language when `Intl` can
290
+ * write in it, otherwise English.
291
+ * @param language - the language of the chart
292
+ */
293
+ function getLocale(language) {
294
+ var _a;
295
+ return (_a = findLocale(language)) !== null && _a !== void 0 ? _a : FALLBACK_LOCALE;
296
+ }
297
+ function findLocale(language) {
298
+ if (!locales.has(language)) {
299
+ locales.set(language, getSupportedLocale(language));
300
+ }
301
+ return locales.get(language);
302
+ }