@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.
- package/CHANGELOG.md +22 -0
- package/dist/cjs/language-s0bKUCvO.js +58 -0
- package/dist/cjs/lime-elements.cjs.js +1 -1
- package/dist/cjs/limel-action-bar_3.cjs.entry.js +2 -2
- package/dist/cjs/limel-ai-avatar.cjs.entry.js +2 -2
- package/dist/cjs/limel-badge_7.cjs.entry.js +12 -2
- package/dist/cjs/limel-callout.cjs.entry.js +2 -2
- package/dist/cjs/limel-chart.cjs.entry.js +524 -76
- package/dist/cjs/limel-chip-set.cjs.entry.js +2 -2
- package/dist/cjs/limel-chip_2.cjs.entry.js +2 -2
- package/dist/cjs/limel-code-diff.cjs.entry.js +2 -2
- package/dist/cjs/limel-code-editor.cjs.entry.js +2 -2
- package/dist/cjs/limel-collapsible-section.cjs.entry.js +2 -2
- package/dist/cjs/limel-date-picker.cjs.entry.js +1 -1
- package/dist/cjs/limel-dock.cjs.entry.js +2 -2
- package/dist/cjs/limel-drag-handle_2.cjs.entry.js +2 -2
- package/dist/cjs/limel-email-viewer.cjs.entry.js +2 -2
- package/dist/cjs/limel-file-viewer.cjs.entry.js +164 -2
- package/dist/cjs/limel-file.cjs.entry.js +2 -2
- package/dist/cjs/limel-flatpickr-adapter.cjs.entry.js +2 -2
- package/dist/cjs/limel-list-item.cjs.entry.js +2 -2
- package/dist/cjs/limel-pagination-jump.cjs.entry.js +2 -2
- package/dist/cjs/limel-pagination.cjs.entry.js +14 -26
- package/dist/cjs/limel-picker.cjs.entry.js +70 -27
- package/dist/cjs/limel-profile-picture.cjs.entry.js +2 -2
- package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +2 -2
- package/dist/cjs/limel-resize-handle.cjs.entry.js +2 -2
- package/dist/cjs/limel-side-panel.cjs.entry.js +2 -2
- package/dist/cjs/limel-slider.cjs.entry.js +2 -2
- package/dist/cjs/limel-snackbar.cjs.entry.js +2 -2
- package/dist/cjs/limel-table.cjs.entry.js +2 -2
- package/dist/cjs/limel-text-editor-link-menu.cjs.entry.js +2 -2
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/{translations-CqJEWcfw.js → translations-JLP5icQ9.js} +1 -1
- package/dist/collection/components/chart/chart.js +116 -45
- package/dist/collection/components/chart/format.js +302 -0
- package/dist/collection/components/chart/options.js +26 -14
- package/dist/collection/components/chart/scale.js +132 -19
- package/dist/collection/components/input-field/input-field.js +10 -0
- package/dist/collection/components/pagination/pagination.js +13 -25
- package/dist/collection/components/picker/picker.js +67 -24
- package/dist/collection/util/language.js +29 -0
- package/dist/esm/language-VamkUr90.js +55 -0
- package/dist/esm/lime-elements.js +1 -1
- package/dist/esm/limel-action-bar_3.entry.js +2 -2
- package/dist/esm/limel-ai-avatar.entry.js +2 -2
- package/dist/esm/limel-badge_7.entry.js +12 -2
- package/dist/esm/limel-callout.entry.js +2 -2
- package/dist/esm/limel-chart.entry.js +524 -76
- package/dist/esm/limel-chip-set.entry.js +2 -2
- package/dist/esm/limel-chip_2.entry.js +2 -2
- package/dist/esm/limel-code-diff.entry.js +2 -2
- package/dist/esm/limel-code-editor.entry.js +2 -2
- package/dist/esm/limel-collapsible-section.entry.js +2 -2
- package/dist/esm/limel-date-picker.entry.js +1 -1
- package/dist/esm/limel-dock.entry.js +2 -2
- package/dist/esm/limel-drag-handle_2.entry.js +2 -2
- package/dist/esm/limel-email-viewer.entry.js +2 -2
- package/dist/esm/limel-file-viewer.entry.js +164 -2
- package/dist/esm/limel-file.entry.js +2 -2
- package/dist/esm/limel-flatpickr-adapter.entry.js +2 -2
- package/dist/esm/limel-list-item.entry.js +2 -2
- package/dist/esm/limel-pagination-jump.entry.js +2 -2
- package/dist/esm/limel-pagination.entry.js +14 -26
- package/dist/esm/limel-picker.entry.js +71 -28
- package/dist/esm/limel-profile-picture.entry.js +2 -2
- package/dist/esm/limel-prosemirror-adapter.entry.js +2 -2
- package/dist/esm/limel-resize-handle.entry.js +2 -2
- package/dist/esm/limel-side-panel.entry.js +2 -2
- package/dist/esm/limel-slider.entry.js +2 -2
- package/dist/esm/limel-snackbar.entry.js +2 -2
- package/dist/esm/limel-table.entry.js +2 -2
- package/dist/esm/limel-text-editor-link-menu.entry.js +2 -2
- package/dist/esm/loader.js +1 -1
- package/dist/esm/{translations-kct5Kybu.js → translations-D9ZGrMhe.js} +1 -1
- package/dist/lime-elements/lime-elements.esm.js +1 -1
- package/dist/lime-elements/{p-6e9eeb0a.entry.js → p-0a7cbdc3.entry.js} +1 -1
- package/dist/lime-elements/{p-d35d2859.entry.js → p-0f96e94d.entry.js} +1 -1
- package/dist/lime-elements/{p-a7be03e1.entry.js → p-1a0c5e45.entry.js} +1 -1
- package/dist/lime-elements/{p-5e408c27.entry.js → p-401d5c92.entry.js} +1 -1
- package/dist/lime-elements/{p-6d2d25b9.entry.js → p-430db3b9.entry.js} +1 -1
- package/dist/lime-elements/{p-a6c6a31b.entry.js → p-43b62587.entry.js} +1 -1
- package/dist/lime-elements/{p-958396b5.entry.js → p-48dccb83.entry.js} +1 -1
- package/dist/lime-elements/{p-814b4934.entry.js → p-5c918155.entry.js} +1 -1
- package/dist/lime-elements/{p-22e9e327.entry.js → p-66101ed6.entry.js} +1 -1
- package/dist/lime-elements/{p-d8559847.entry.js → p-7ab72b52.entry.js} +1 -1
- package/dist/lime-elements/{p-7ab726c2.entry.js → p-8d9a2a22.entry.js} +1 -1
- package/dist/lime-elements/{p-55fa19ae.entry.js → p-8de4f0f3.entry.js} +1 -1
- package/dist/lime-elements/p-8e80a884.entry.js +56 -0
- package/dist/lime-elements/{p-bffb5ed2.entry.js → p-99984c66.entry.js} +1 -1
- package/dist/lime-elements/{p-56e2fa48.entry.js → p-9d46f720.entry.js} +1 -1
- package/dist/lime-elements/p-9e2b426c.entry.js +1 -0
- package/dist/lime-elements/{p-Dzp8dXTo.js → p-C9dBa_Im.js} +1 -1
- package/dist/lime-elements/p-VamkUr90.js +1 -0
- package/dist/lime-elements/{p-3ac39bd5.entry.js → p-b1c7033b.entry.js} +2 -2
- package/dist/lime-elements/{p-47fe76a8.entry.js → p-b5451350.entry.js} +1 -1
- package/dist/lime-elements/{p-9edd79a8.entry.js → p-c75f3b0d.entry.js} +1 -1
- package/dist/lime-elements/{p-4c080cd9.entry.js → p-ca4247c4.entry.js} +1 -1
- package/dist/lime-elements/{p-1a7d1d2b.entry.js → p-d53421af.entry.js} +1 -1
- package/dist/lime-elements/{p-6d9b89db.entry.js → p-d8901e39.entry.js} +1 -1
- package/dist/lime-elements/{p-933ffaa8.entry.js → p-d89553b3.entry.js} +1 -1
- package/dist/lime-elements/{p-496b2437.entry.js → p-d93b8ce0.entry.js} +1 -1
- package/dist/lime-elements/{p-23067836.entry.js → p-e97340c4.entry.js} +1 -1
- package/dist/lime-elements/{p-7b87e547.entry.js → p-ec0c113d.entry.js} +1 -1
- package/dist/lime-elements/p-f197000b.entry.js +159 -0
- package/dist/lime-elements/{p-07f3565b.entry.js → p-f6bc6658.entry.js} +1 -1
- package/dist/lime-elements/{p-7fcd48c6.entry.js → p-fa1aa440.entry.js} +1 -1
- package/dist/types/components/chart/chart.d.ts +32 -7
- package/dist/types/components/chart/chart.types.d.ts +15 -1
- package/dist/types/components/chart/format.d.ts +54 -0
- package/dist/types/components/chart/options.d.ts +3 -4
- package/dist/types/components/chart/scale.d.ts +39 -7
- package/dist/types/components/input-field/input-field.d.ts +1 -0
- package/dist/types/components/pagination/pagination.d.ts +6 -12
- package/dist/types/components/picker/picker.d.ts +10 -2
- package/dist/types/components.d.ts +36 -8
- package/dist/types/util/language.d.ts +19 -0
- package/package.json +2 -2
- package/dist/cjs/language-v-MMS6ym.js +0 -28
- package/dist/esm/language-ChagAaNf.js +0 -26
- package/dist/lime-elements/p-7d6a12fe.entry.js +0 -56
- package/dist/lime-elements/p-ChagAaNf.js +0 -1
- package/dist/lime-elements/p-a807a45f.entry.js +0 -159
- package/dist/lime-elements/p-cd479c23.entry.js +0 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { format } from "echarts/core";
|
|
2
|
-
import {
|
|
2
|
+
import { getAxisRange, getDateRange, getDateTicks, getMaximumValue, getMinimumValue, getScatterRanges, getXValue, getYValue, getShare, getValueSpan, getWholeRange, isPartOfWhole, isRangeItem, } from "./scale";
|
|
3
|
+
import { formatShare, formatValue, getAxisValueFormatter, } from "./format";
|
|
3
4
|
const DOT_SIZE = 8;
|
|
4
5
|
const LINE_DOT_SIZE = 6;
|
|
5
6
|
const CONNECTOR_WIDTH = 2;
|
|
6
7
|
const DIMMED_OPACITY = 0.4;
|
|
7
8
|
const AREA_OPACITY = 0.4;
|
|
8
9
|
const PADDING = 8;
|
|
9
|
-
const PERCENT_DECIMALS = 2;
|
|
10
10
|
const NPS_MIN = -100;
|
|
11
11
|
const NPS_MAX = 100;
|
|
12
12
|
const NPS_START_ANGLE = 200;
|
|
@@ -96,8 +96,8 @@ export function getItemLabel(settings, index) {
|
|
|
96
96
|
return item.text;
|
|
97
97
|
}
|
|
98
98
|
const whole = getWholeRange(settings.items, settings.maxValue);
|
|
99
|
-
const share = getShare(item, whole).
|
|
100
|
-
return `${item.text} (${share}
|
|
99
|
+
const share = formatShare(getShare(item, whole), settings.language);
|
|
100
|
+
return `${item.text} (${share})`;
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
103
|
* The tooltip of an item, as HTML: its label and its value.
|
|
@@ -109,7 +109,7 @@ export function getTooltip(settings, index) {
|
|
|
109
109
|
if (!item) {
|
|
110
110
|
return '';
|
|
111
111
|
}
|
|
112
|
-
return [getItemLabel(settings, index), formatValue(item, settings
|
|
112
|
+
return [getItemLabel(settings, index), formatValue(item, settings)]
|
|
113
113
|
.map((text) => format.encodeHTML(text))
|
|
114
114
|
.join('<br/>');
|
|
115
115
|
}
|
|
@@ -138,12 +138,17 @@ function drawSeries(settings, theme, itemSpace) {
|
|
|
138
138
|
}
|
|
139
139
|
function drawOnAxes(settings, theme, itemSpace) {
|
|
140
140
|
const { items, orientation } = settings;
|
|
141
|
-
const range =
|
|
141
|
+
const range = settings.valueFormat === 'date'
|
|
142
|
+
? getDateRange(items.flatMap((item) => [
|
|
143
|
+
getMinimumValue(item),
|
|
144
|
+
getMaximumValue(item),
|
|
145
|
+
]), settings.maxValue)
|
|
146
|
+
: getAxisRange(items, settings.maxValue, settings.axisIncrement);
|
|
142
147
|
const itemTextAxis = getItemTextAxis(settings);
|
|
143
148
|
const categoryAxis = Object.assign(Object.assign(Object.assign({}, getCategoryAxis(settings)), { axisLabel: Object.assign({ show: settings.displayItemText, color: theme.textColor }, (itemTextAxis &&
|
|
144
149
|
itemSpace &&
|
|
145
150
|
getItemTextLayout(itemTextAxis, itemSpace))), axisTick: hidden, axisLine: { lineStyle: { color: theme.axisLineColor } } }), getAxisName(settings, settings.itemsLabel, theme));
|
|
146
|
-
const valueAxis = getValueAxis(range, getAxisName(settings, settings.valuesLabel, theme), theme);
|
|
151
|
+
const valueAxis = getValueAxis(range, getAxisName(settings, settings.valuesLabel, theme), settings, theme);
|
|
147
152
|
const { series, seriesIndex } = getAxisSeries(settings, theme);
|
|
148
153
|
const option = {
|
|
149
154
|
grid: getGrid(theme),
|
|
@@ -306,10 +311,15 @@ function getRangeStarts(items) {
|
|
|
306
311
|
}
|
|
307
312
|
function drawScatter(settings, theme) {
|
|
308
313
|
const { items } = settings;
|
|
309
|
-
const ranges =
|
|
314
|
+
const ranges = settings.valueFormat === 'date'
|
|
315
|
+
? {
|
|
316
|
+
x: getDateRange(items.map((item) => getXValue(item))),
|
|
317
|
+
y: getDateRange(items.map((item) => getYValue(item))),
|
|
318
|
+
}
|
|
319
|
+
: getScatterRanges(items);
|
|
310
320
|
const landscape = settings.orientation === 'landscape';
|
|
311
|
-
const firstAxis = getValueAxis(ranges.x, getAxisName(settings, settings.itemsLabel, theme), theme);
|
|
312
|
-
const secondAxis = getValueAxis(ranges.y, getAxisName(settings, settings.valuesLabel, theme), theme);
|
|
321
|
+
const firstAxis = getValueAxis(ranges.x, getAxisName(settings, settings.itemsLabel, theme), settings, theme);
|
|
322
|
+
const secondAxis = getValueAxis(ranges.y, getAxisName(settings, settings.valuesLabel, theme), settings, theme);
|
|
313
323
|
const scatter = {
|
|
314
324
|
type: 'scatter',
|
|
315
325
|
symbolSize: theme.scatterItemSize,
|
|
@@ -531,8 +541,10 @@ function getGrid(theme) {
|
|
|
531
541
|
borderWidth: 0,
|
|
532
542
|
};
|
|
533
543
|
}
|
|
534
|
-
function getValueAxis(range, name, theme) {
|
|
535
|
-
|
|
544
|
+
function getValueAxis(range, name, settings, theme) {
|
|
545
|
+
const dates = settings.valueFormat === 'date' && getDateTicks(range);
|
|
546
|
+
const ticks = dates && { customValues: dates.values };
|
|
547
|
+
return Object.assign(Object.assign(Object.assign({ type: 'value', min: range.minValue, max: range.maxValue, interval: dates ? undefined : range.increment, axisLabel: Object.assign({ show: true, color: theme.textColor, formatter: getAxisValueFormatter(range, settings, dates || undefined) }, ticks) }, (ticks && { axisTick: ticks })), { axisLine: { lineStyle: { color: theme.axisLineColor } }, splitLine: { lineStyle: { color: theme.axisLineColor } } }), name);
|
|
536
548
|
}
|
|
537
549
|
function getAxisName(settings, name, theme) {
|
|
538
550
|
if (!settings.displayAxisLabels) {
|
|
@@ -545,7 +557,7 @@ function getAxisName(settings, name, theme) {
|
|
|
545
557
|
};
|
|
546
558
|
}
|
|
547
559
|
function getValueLabel(settings, theme) {
|
|
548
|
-
return Object.assign(Object.assign({ show: settings.displayItemValue, position: settings.orientation === 'landscape' ? 'top' : 'right' }, getLabelStyle(theme)), { formatter: ({ dataIndex }) => formatValue(settings.items[dataIndex], settings
|
|
560
|
+
return Object.assign(Object.assign({ show: settings.displayItemValue, position: settings.orientation === 'landscape' ? 'top' : 'right' }, getLabelStyle(theme)), { formatter: ({ dataIndex }) => formatValue(settings.items[dataIndex], settings) });
|
|
549
561
|
}
|
|
550
562
|
function getLabelStyle(theme) {
|
|
551
563
|
return { color: theme.textColor, textBorderWidth: 0 };
|
|
@@ -556,7 +568,7 @@ function getPointLabel(settings, item) {
|
|
|
556
568
|
parts.push(item.text);
|
|
557
569
|
}
|
|
558
570
|
if (settings.displayItemValue) {
|
|
559
|
-
parts.push(formatValue(item, settings
|
|
571
|
+
parts.push(formatValue(item, settings));
|
|
560
572
|
}
|
|
561
573
|
return parts.join(' ');
|
|
562
574
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const PERCENT = 100;
|
|
2
2
|
const STEP_COUNT = 10;
|
|
3
|
+
const DAY = 24 * 60 * 60 * 1000;
|
|
3
4
|
/**
|
|
4
5
|
* The most steps that an `axisIncrement` may divide a value axis into.
|
|
5
6
|
*/
|
|
@@ -102,6 +103,114 @@ export function getAxisRange(items, maxValue, axisIncrement) {
|
|
|
102
103
|
: getNiceStep(span);
|
|
103
104
|
return roundOutwards(minRange, finalMaxRange, increment);
|
|
104
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Whether `value` is a date in milliseconds that `Date` can hold. A date
|
|
108
|
+
* in nanoseconds is not, and neither is a date given as text.
|
|
109
|
+
* @param value - the value to check
|
|
110
|
+
*/
|
|
111
|
+
export function isDate(value) {
|
|
112
|
+
return (typeof value === 'number' && Number.isFinite(new Date(value).getTime()));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The range of an axis of dates, in milliseconds: from the start of the
|
|
116
|
+
* earliest day to the latest date, or to `maxValue` when that is set, as an
|
|
117
|
+
* axis of numbers ends at `maxValue`. An end during a day is moved to the
|
|
118
|
+
* end of that day. Unlike an axis of numbers, it does not reach down to
|
|
119
|
+
* zero, which would be 1970, and values that are not dates are left out.
|
|
120
|
+
* Its increment is a day, though `getDateTicks` decides what the axis
|
|
121
|
+
* marks.
|
|
122
|
+
* @param values - the dates on the axis
|
|
123
|
+
* @param maxValue - the requested upper bound, if any
|
|
124
|
+
*/
|
|
125
|
+
export function getDateRange(values, maxValue) {
|
|
126
|
+
const dates = values.filter((value) => isDate(value));
|
|
127
|
+
const start = new Date(dates.length > 0 ? Math.min(...dates) : Date.now());
|
|
128
|
+
start.setHours(0, 0, 0, 0);
|
|
129
|
+
const end = new Date(isDate(maxValue) ? maxValue : Math.max(start.getTime(), ...dates));
|
|
130
|
+
if (end.getTime() <= start.getTime()) {
|
|
131
|
+
end.setTime(addDays(start, 1).getTime());
|
|
132
|
+
}
|
|
133
|
+
else if (end.getTime() > startOfDay(end)) {
|
|
134
|
+
end.setTime(addDays(new Date(startOfDay(end)), 1).getTime());
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
minValue: start.getTime(),
|
|
138
|
+
maxValue: end.getTime(),
|
|
139
|
+
totalRange: end.getTime() - start.getTime(),
|
|
140
|
+
increment: DAY,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const DAYS_IN_MONTH = 30.44;
|
|
144
|
+
const DAYS_IN_YEAR = 365.25;
|
|
145
|
+
const DATE_STEP_COUNT = 6;
|
|
146
|
+
const dayStep = (count) => ({
|
|
147
|
+
unit: 'day',
|
|
148
|
+
days: count,
|
|
149
|
+
first: (date) => date,
|
|
150
|
+
next: (date) => addDays(date, count),
|
|
151
|
+
});
|
|
152
|
+
const weekStep = (count) => ({
|
|
153
|
+
unit: 'day',
|
|
154
|
+
days: 7 * count,
|
|
155
|
+
first: (date) => addDays(date, (8 - date.getDay()) % 7),
|
|
156
|
+
next: (date) => addDays(date, 7 * count),
|
|
157
|
+
});
|
|
158
|
+
const monthStep = (count) => ({
|
|
159
|
+
unit: 'month',
|
|
160
|
+
days: DAYS_IN_MONTH * count,
|
|
161
|
+
first: (date) => {
|
|
162
|
+
let month = date.getDate() === 1 ? date : getFirstOfMonth(date, 1);
|
|
163
|
+
while (month.getMonth() % count !== 0) {
|
|
164
|
+
month = getFirstOfMonth(month, 1);
|
|
165
|
+
}
|
|
166
|
+
return month;
|
|
167
|
+
},
|
|
168
|
+
next: (date) => getFirstOfMonth(date, count),
|
|
169
|
+
});
|
|
170
|
+
const yearStep = (count) => ({
|
|
171
|
+
unit: 'year',
|
|
172
|
+
days: DAYS_IN_YEAR * count,
|
|
173
|
+
first: (date) => {
|
|
174
|
+
let year = date.getFullYear();
|
|
175
|
+
if (date.getTime() > createDate(year, 0, 1).getTime()) {
|
|
176
|
+
year += 1;
|
|
177
|
+
}
|
|
178
|
+
return createDate(Math.ceil(year / count) * count, 0, 1);
|
|
179
|
+
},
|
|
180
|
+
next: (date) => createDate(date.getFullYear() + count, 0, 1),
|
|
181
|
+
});
|
|
182
|
+
const DATE_STEPS = [
|
|
183
|
+
dayStep(1),
|
|
184
|
+
dayStep(2),
|
|
185
|
+
weekStep(1),
|
|
186
|
+
weekStep(2),
|
|
187
|
+
monthStep(1),
|
|
188
|
+
monthStep(3),
|
|
189
|
+
monthStep(6),
|
|
190
|
+
yearStep(1),
|
|
191
|
+
yearStep(2),
|
|
192
|
+
yearStep(5),
|
|
193
|
+
yearStep(10),
|
|
194
|
+
];
|
|
195
|
+
/**
|
|
196
|
+
* The dates to mark on an axis of dates: days, Mondays, or the first days
|
|
197
|
+
* of months, quarters, half years or years, whichever divides the axis
|
|
198
|
+
* into at most about six steps. Dates take more room than numbers, so
|
|
199
|
+
* they get fewer steps.
|
|
200
|
+
* @param range - the range of the axis, from `getDateRange`
|
|
201
|
+
* @returns the dates to mark, and whether they are days, months or years
|
|
202
|
+
*/
|
|
203
|
+
export function getDateTicks(range) {
|
|
204
|
+
var _a;
|
|
205
|
+
const days = (range.maxValue - range.minValue) / DAY;
|
|
206
|
+
const decades = days / DAYS_IN_YEAR / 10;
|
|
207
|
+
const step = (_a = DATE_STEPS.find((candidate) => days / candidate.days <= DATE_STEP_COUNT)) !== null && _a !== void 0 ? _a : yearStep(10 * Math.ceil(decades / DATE_STEP_COUNT));
|
|
208
|
+
const ticks = [];
|
|
209
|
+
for (let date = step.first(new Date(range.minValue)); date.getTime() <= range.maxValue; date = step.next(date)) {
|
|
210
|
+
ticks.push(date.getTime());
|
|
211
|
+
}
|
|
212
|
+
return { values: ticks, unit: step.unit };
|
|
213
|
+
}
|
|
105
214
|
/**
|
|
106
215
|
* The range of a chart whose items are parts of one whole: `maxValue`
|
|
107
216
|
* when set, otherwise the combined item values or range extents. The value
|
|
@@ -144,25 +253,6 @@ export function getScatterRanges(items) {
|
|
|
144
253
|
export function getShare(item, whole) {
|
|
145
254
|
return (getValueSpan(item) / whole.totalRange) * PERCENT;
|
|
146
255
|
}
|
|
147
|
-
/**
|
|
148
|
-
* The item's value as text: its `formattedValue` when set, otherwise its
|
|
149
|
-
* value, range, or scatter point.
|
|
150
|
-
* @param item - the item
|
|
151
|
-
* @param type - the chart type
|
|
152
|
-
*/
|
|
153
|
-
export function formatValue(item, type) {
|
|
154
|
-
const { value, formattedValue } = item;
|
|
155
|
-
if (formattedValue) {
|
|
156
|
-
return formattedValue;
|
|
157
|
-
}
|
|
158
|
-
if (Array.isArray(value)) {
|
|
159
|
-
if (type === 'scatter') {
|
|
160
|
-
return `(${value[0]}, ${value[1]})`;
|
|
161
|
-
}
|
|
162
|
-
return `${value[0]} — ${value[1]}`;
|
|
163
|
-
}
|
|
164
|
-
return `${value}`;
|
|
165
|
-
}
|
|
166
256
|
function isUsableStep(step, span) {
|
|
167
257
|
return step > 0 && span / step <= MAX_AXIS_STEPS;
|
|
168
258
|
}
|
|
@@ -181,6 +271,29 @@ function getZeroAnchoredRange(values) {
|
|
|
181
271
|
const maxValue = Math.max(0, ...values);
|
|
182
272
|
return roundOutwards(minValue, maxValue, getNiceStep(maxValue - minValue));
|
|
183
273
|
}
|
|
274
|
+
function addDays(date, days) {
|
|
275
|
+
return createDate(date.getFullYear(), date.getMonth(), date.getDate() + days);
|
|
276
|
+
}
|
|
277
|
+
function getFirstOfMonth(date, monthsLater) {
|
|
278
|
+
return createDate(date.getFullYear(), date.getMonth() + monthsLater, 1);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* The start of a day in local time. Unlike `new Date(year, month, day)`, it
|
|
282
|
+
* keeps the years 0 to 99 rather than reading them as 1900 to 1999, see
|
|
283
|
+
* https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-makefullyear
|
|
284
|
+
* @param year - the full year
|
|
285
|
+
* @param month - the month, from 0 for January
|
|
286
|
+
* @param day - the day of the month
|
|
287
|
+
*/
|
|
288
|
+
function createDate(year, month, day) {
|
|
289
|
+
const date = new Date(0);
|
|
290
|
+
date.setFullYear(year, month, day);
|
|
291
|
+
date.setHours(0, 0, 0, 0);
|
|
292
|
+
return date;
|
|
293
|
+
}
|
|
294
|
+
function startOfDay(date) {
|
|
295
|
+
return new Date(date).setHours(0, 0, 0, 0);
|
|
296
|
+
}
|
|
184
297
|
function roundOutwards(minValue, maxValue, increment) {
|
|
185
298
|
const lowerEnd = Math.floor(minValue / increment) * increment;
|
|
186
299
|
const upperEnd = Math.ceil(maxValue / increment) * increment;
|
|
@@ -524,10 +524,20 @@ export class InputField {
|
|
|
524
524
|
this.showCompletions = false;
|
|
525
525
|
return;
|
|
526
526
|
}
|
|
527
|
+
if (event.key === ARROW_UP && this.isKeyPressedOnFirstOption(event)) {
|
|
528
|
+
event.preventDefault();
|
|
529
|
+
this.setFocus();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
527
532
|
if ([TAB, ENTER].includes(event.key) || isTextEditingKey(event)) {
|
|
528
533
|
this.setFocus();
|
|
529
534
|
}
|
|
530
535
|
};
|
|
536
|
+
this.isKeyPressedOnFirstOption = (event) => {
|
|
537
|
+
const list = event.currentTarget;
|
|
538
|
+
const firstOption = list.shadowRoot.querySelector('.mdc-deprecated-list-item');
|
|
539
|
+
return !!firstOption && event.composedPath().includes(firstOption);
|
|
540
|
+
};
|
|
531
541
|
this.handleCloseMenu = () => {
|
|
532
542
|
this.showCompletions = false;
|
|
533
543
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { h, Host, } from "@stencil/core";
|
|
2
2
|
import translate from "../../global/translations";
|
|
3
3
|
import { createRandomString } from "../../util/random-string";
|
|
4
|
-
import {
|
|
4
|
+
import { getSupportedLocale } from "../../util/language";
|
|
5
5
|
import { getPageSlots } from "./pagination.util";
|
|
6
6
|
const FIRST_PAGE = 1;
|
|
7
7
|
const DEFAULT_PAGE_SIZE = 100;
|
|
@@ -304,7 +304,7 @@ export class Pagination {
|
|
|
304
304
|
}
|
|
305
305
|
render() {
|
|
306
306
|
const { slots } = this.view;
|
|
307
|
-
return (h(Host, { key: '
|
|
307
|
+
return (h(Host, { key: '7c47cdc658859b4cd8199ef92ebd9bc48275a8ae' }, h("nav", { key: '3bba45547a7183a4b1df58294a56788f9b2e078f', "aria-label": this.translate('pagination.label'), "aria-busy": this.loading ? 'true' : null }, this.renderArrow('previous'), slots.map((slot, index) => this.renderSlot(slot, index)), this.renderArrow('next')), h("span", { key: '60fff33d8e135b1605d0a0f68b6c1ede1b571281', class: "live-region", "aria-live": "polite" }, this.liveMessage)));
|
|
308
308
|
}
|
|
309
309
|
/**
|
|
310
310
|
* Without a count we know where the user is but not how far the set goes,
|
|
@@ -620,9 +620,7 @@ export class Pagination {
|
|
|
620
620
|
}
|
|
621
621
|
/**
|
|
622
622
|
* What the jump form is given, so that the one warning about a language
|
|
623
|
-
* this browser cannot write numbers in describes both of us.
|
|
624
|
-
* accepts but does not support, `zz`, resolves to the viewer's own locale
|
|
625
|
-
* rather than throwing, so asking the formatter is the only way to know.
|
|
623
|
+
* this browser cannot write numbers in describes both of us.
|
|
626
624
|
*/
|
|
627
625
|
get languageForNumbers() {
|
|
628
626
|
var _a;
|
|
@@ -630,31 +628,21 @@ export class Pagination {
|
|
|
630
628
|
return this.settledLanguage;
|
|
631
629
|
}
|
|
632
630
|
/**
|
|
633
|
-
* `
|
|
634
|
-
*
|
|
635
|
-
*
|
|
636
|
-
* `src/util/language.ts`
|
|
637
|
-
*
|
|
638
|
-
* A tag it cannot parse throws; a well-formed tag it does not support, like
|
|
639
|
-
* `zz`, does not — it quietly resolves to whatever locale the machine runs
|
|
640
|
-
* in. Both end up formatting numbers in something other than what was
|
|
641
|
-
* asked for, so both are worth saying out loud.
|
|
631
|
+
* This is called from `render`, so a language that `Intl` cannot write
|
|
632
|
+
* numbers in must not throw, or the control would draw nothing at all —
|
|
633
|
+
* the single state a user cannot click their way out of.
|
|
634
|
+
* `getSupportedLocale` in `src/util/language.ts` decides which languages
|
|
635
|
+
* those are.
|
|
642
636
|
*
|
|
643
637
|
* @returns a formatter, in the given language where that is possible
|
|
644
638
|
*/
|
|
645
639
|
createNumberFormat() {
|
|
646
640
|
const { language } = this;
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
this.settledLanguage = language;
|
|
653
|
-
return format;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
catch (_a) {
|
|
657
|
-
// Fall through to the warning below.
|
|
641
|
+
const locale = getSupportedLocale(language);
|
|
642
|
+
if (locale) {
|
|
643
|
+
this.warnedAbout.delete('language');
|
|
644
|
+
this.settledLanguage = language;
|
|
645
|
+
return new Intl.NumberFormat(locale);
|
|
658
646
|
}
|
|
659
647
|
this.warnOnce('language', language, () => `\`language\` must be one this browser can write numbers in, but was ${JSON.stringify(language)}. Using ${FALLBACK_LANGUAGE}.`);
|
|
660
648
|
// Where the translations land for the same unusable value, rather than
|
|
@@ -10,6 +10,8 @@ import { debounce } from "lodash-es";
|
|
|
10
10
|
const SEARCH_DEBOUNCE = 300;
|
|
11
11
|
const CHIP_SET_TAG_NAME = 'limel-chip-set';
|
|
12
12
|
const DEFAULT_SEARCHER_MAX_RESULTS = 20;
|
|
13
|
+
const MAX_FRAMES_TO_WAIT_FOR_LIST = 20;
|
|
14
|
+
const ENABLED_LIST_ITEM = '.mdc-deprecated-list-item:not(.mdc-deprecated-list-item--disabled)';
|
|
13
15
|
/**
|
|
14
16
|
* @exampleComponent limel-example-picker-basic
|
|
15
17
|
* @exampleComponent limel-example-picker-multiple
|
|
@@ -98,7 +100,8 @@ export class Picker {
|
|
|
98
100
|
this.chips = [];
|
|
99
101
|
// Should NOT be decorated with State(), since this
|
|
100
102
|
// should not trigger a re-render by itself.
|
|
101
|
-
this.
|
|
103
|
+
this.isEditing = false;
|
|
104
|
+
this.suggestionHasFocus = false;
|
|
102
105
|
this.suggestionsMessage = createLiveMessage(() => this.getSuggestionsMessage());
|
|
103
106
|
this.getValueId = (item) => {
|
|
104
107
|
const value = item.value;
|
|
@@ -182,6 +185,7 @@ export class Picker {
|
|
|
182
185
|
this.suggestionsMessage.update();
|
|
183
186
|
}
|
|
184
187
|
disconnectedCallback() {
|
|
188
|
+
this.forgetSuggestionFocus();
|
|
185
189
|
this.debouncedSearch.cancel();
|
|
186
190
|
this.suggestionsMessage.update.cancel();
|
|
187
191
|
}
|
|
@@ -196,9 +200,10 @@ export class Picker {
|
|
|
196
200
|
this.host.setAttribute('tabindex', '0');
|
|
197
201
|
}
|
|
198
202
|
async componentWillUpdate() {
|
|
199
|
-
this.
|
|
203
|
+
this.isEditing = false;
|
|
200
204
|
if (this.chipSet) {
|
|
201
|
-
this.
|
|
205
|
+
this.isEditing =
|
|
206
|
+
this.suggestionHasFocus || (await this.chipSet.getEditMode());
|
|
202
207
|
}
|
|
203
208
|
}
|
|
204
209
|
render() {
|
|
@@ -207,7 +212,7 @@ export class Picker {
|
|
|
207
212
|
if (!this.multiple) {
|
|
208
213
|
props.maxItems = 1;
|
|
209
214
|
}
|
|
210
|
-
return (h(Host, { key: '
|
|
215
|
+
return (h(Host, { key: '1670bff5822f930a3eca69ad2a016cea978b3822' }, h("limel-chip-set", Object.assign({ key: '173a7facd5271992e792ce6a56a34a8890c9135f', type: "input", inputType: "search", label: this.label, helperText: this.helperText, leadingIcon: this.leadingIcon, value: this.chips, disabled: this.disabled, invalid: this.invalid, delimiter: this.renderDelimiter(), readonly: this.readonly, required: this.required, searchLabel: this.searchLabel, language: this.language, onInput: this.handleTextInput, onKeyDown: this.handleInputKeyDown, onChange: this.handleChange, onInteract: this.handleInteract, onStartEdit: this.handleInputFieldFocus, onStopEdit: this.handleStopEditAndBlur, emptyInputOnBlur: false, emptyInputOnChange: false, clearAllButton: this.multiple && !this.isEditing, suggestionListId: this.canOpenList() ? this.portalId : undefined, suggestionListOpen: this.suggestionsEngaged && dropdownContent.length > 0 }, props)), this.renderPortal(dropdownContent), h("span", { key: 'c151104c5059aadc3cfa526267184b663e87bebd', class: "suggestions-message", "aria-live": "polite", ref: this.suggestionsMessage.setElement })));
|
|
211
216
|
}
|
|
212
217
|
onChangeValue() {
|
|
213
218
|
this.chips = this.createChips(this.value);
|
|
@@ -280,7 +285,7 @@ export class Picker {
|
|
|
280
285
|
return !this.readonly && !this.isFull();
|
|
281
286
|
}
|
|
282
287
|
shouldShowDropDownContent() {
|
|
283
|
-
return this.canOpenList() && !!this.
|
|
288
|
+
return this.canOpenList() && !!this.isEditing;
|
|
284
289
|
}
|
|
285
290
|
getDropdownContent() {
|
|
286
291
|
var _a;
|
|
@@ -349,10 +354,20 @@ export class Picker {
|
|
|
349
354
|
this.chipSet.setFocus();
|
|
350
355
|
return;
|
|
351
356
|
}
|
|
357
|
+
if (event.key === ARROW_UP && this.isKeyPressedOnFirstItem(event)) {
|
|
358
|
+
event.preventDefault();
|
|
359
|
+
this.chipSet.setFocus();
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
352
362
|
if ([TAB, ENTER].includes(event.key) || isTextEditingKey(event)) {
|
|
353
363
|
this.chipSet.setFocus();
|
|
354
364
|
}
|
|
355
365
|
}
|
|
366
|
+
isKeyPressedOnFirstItem(event) {
|
|
367
|
+
const list = event.currentTarget;
|
|
368
|
+
const firstItem = list.shadowRoot.querySelector(ENABLED_LIST_ITEM);
|
|
369
|
+
return !!firstItem && event.composedPath().includes(firstItem);
|
|
370
|
+
}
|
|
356
371
|
renderPortal(content = []) {
|
|
357
372
|
const dropdownZIndex = getComputedStyle(this.host).getPropertyValue('--dropdown-z-index');
|
|
358
373
|
return (h("limel-portal", { visible: content.length > 0, containerId: this.portalId, inheritParentWidth: true, containerStyle: { 'z-index': dropdownZIndex } }, h("limel-menu-surface", { open: content.length > 0, allowClicksElement: this.host, style: {
|
|
@@ -386,6 +401,7 @@ export class Picker {
|
|
|
386
401
|
const query = event.detail;
|
|
387
402
|
this.textValue = query;
|
|
388
403
|
this.suggestionsEngaged = true;
|
|
404
|
+
this.forgetSuggestionFocus();
|
|
389
405
|
this.debouncedSearch(query);
|
|
390
406
|
// If the search-query is an empty string, bypass debouncing.
|
|
391
407
|
if (query === '') {
|
|
@@ -435,6 +451,7 @@ export class Picker {
|
|
|
435
451
|
if (!event.detail) {
|
|
436
452
|
return;
|
|
437
453
|
}
|
|
454
|
+
this.suggestionHasFocus = false;
|
|
438
455
|
this.action.emit(event.detail.value);
|
|
439
456
|
this.items = [];
|
|
440
457
|
}
|
|
@@ -443,6 +460,7 @@ export class Picker {
|
|
|
443
460
|
* Prevent focus if the picker has a value and does not support multiple values
|
|
444
461
|
*/
|
|
445
462
|
handleInputFieldFocus() {
|
|
463
|
+
this.forgetSuggestionFocus();
|
|
446
464
|
const query = this.textValue;
|
|
447
465
|
this.debouncedSearch(query);
|
|
448
466
|
}
|
|
@@ -480,7 +498,6 @@ export class Picker {
|
|
|
480
498
|
* @param event - event
|
|
481
499
|
*/
|
|
482
500
|
handleInputKeyDown(event) {
|
|
483
|
-
var _a;
|
|
484
501
|
this.suggestionsEngaged = true;
|
|
485
502
|
const isUp = event.key === ARROW_UP;
|
|
486
503
|
const isDown = event.key === ARROW_DOWN;
|
|
@@ -494,28 +511,47 @@ export class Picker {
|
|
|
494
511
|
if (!isUp && !isDown) {
|
|
495
512
|
return;
|
|
496
513
|
}
|
|
514
|
+
const position = isDown ? 'first' : 'last';
|
|
515
|
+
if (this.focusSuggestion(position)) {
|
|
516
|
+
event.preventDefault();
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
this.suggestionToFocusWhenShown = position;
|
|
520
|
+
}
|
|
521
|
+
focusSuggestion(position) {
|
|
522
|
+
var _a;
|
|
497
523
|
const list = (_a = document.querySelector(` #${this.portalId} limel-list[data-suggestions]`)) !== null && _a !== void 0 ? _a : document.querySelector(` #${this.portalId} limel-list`);
|
|
498
524
|
if (!list) {
|
|
499
|
-
return;
|
|
525
|
+
return false;
|
|
500
526
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
527
|
+
// Match by class only (not `:first-child`) so a leading
|
|
528
|
+
// `ListSeparator` doesn't shadow the first focusable item.
|
|
529
|
+
const items = list.shadowRoot.querySelectorAll(ENABLED_LIST_ITEM);
|
|
530
|
+
const item = position === 'first' ? items[0] : [...items].at(-1);
|
|
531
|
+
item === null || item === void 0 ? void 0 : item.focus();
|
|
532
|
+
this.suggestionHasFocus =
|
|
533
|
+
!!item && list.shadowRoot.activeElement === item;
|
|
534
|
+
return this.suggestionHasFocus;
|
|
535
|
+
}
|
|
536
|
+
focusSuggestionWhenShown(position) {
|
|
537
|
+
cancelAnimationFrame(this.suggestionFocusFrame);
|
|
538
|
+
let frames = 0;
|
|
539
|
+
const focusSuggestion = () => {
|
|
540
|
+
if (this.focusSuggestion(position)) {
|
|
506
541
|
return;
|
|
507
542
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
543
|
+
frames += 1;
|
|
544
|
+
if (frames < MAX_FRAMES_TO_WAIT_FOR_LIST) {
|
|
545
|
+
this.suggestionFocusFrame =
|
|
546
|
+
requestAnimationFrame(focusSuggestion);
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
this.suggestionFocusFrame = requestAnimationFrame(focusSuggestion);
|
|
550
|
+
}
|
|
551
|
+
forgetSuggestionFocus() {
|
|
552
|
+
this.suggestionHasFocus = false;
|
|
553
|
+
this.suggestionToFocusWhenShown = undefined;
|
|
554
|
+
cancelAnimationFrame(this.suggestionFocusFrame);
|
|
519
555
|
}
|
|
520
556
|
handleSearchResult(query, result) {
|
|
521
557
|
var _a;
|
|
@@ -534,6 +570,11 @@ export class Picker {
|
|
|
534
570
|
}
|
|
535
571
|
this.items = this.prependSearchHeader(query, nextItems);
|
|
536
572
|
this.loading = false;
|
|
573
|
+
const position = this.suggestionToFocusWhenShown;
|
|
574
|
+
this.suggestionToFocusWhenShown = undefined;
|
|
575
|
+
if (position && this.hasPickableItems(nextItems)) {
|
|
576
|
+
this.focusSuggestionWhenShown(position);
|
|
577
|
+
}
|
|
537
578
|
}
|
|
538
579
|
hasPickableItems(items) {
|
|
539
580
|
return items.some((item) => !('separator' in item));
|
|
@@ -556,7 +597,8 @@ export class Picker {
|
|
|
556
597
|
/**
|
|
557
598
|
* Shared prelude for any flow that ends the current search session:
|
|
558
599
|
* wipe the chip-set's visible text, reset the picker's `textValue`,
|
|
559
|
-
*
|
|
600
|
+
* cancel any in-flight debounced search, and forget that a suggestion
|
|
601
|
+
* has the focus, or that an arrow key is waiting for the suggestions.
|
|
560
602
|
*
|
|
561
603
|
* Used by `clearInputField` (which then drops the dropdown
|
|
562
604
|
* entirely) and `resetSearchToDefault` (which re-runs the searcher
|
|
@@ -566,6 +608,7 @@ export class Picker {
|
|
|
566
608
|
this.chipSet.emptyInput();
|
|
567
609
|
this.textValue = '';
|
|
568
610
|
this.debouncedSearch.cancel();
|
|
611
|
+
this.forgetSuggestionFocus();
|
|
569
612
|
}
|
|
570
613
|
clearInputField() {
|
|
571
614
|
this.clearTextValue();
|
|
@@ -22,3 +22,32 @@ export function getPrimarySubtag(language) {
|
|
|
22
22
|
}
|
|
23
23
|
return language.toLowerCase().split('-', 1)[0];
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* The locale that `Intl` writes numbers and dates in for `language`, or
|
|
27
|
+
* `undefined` when it cannot write in that language. `Intl` throws on a
|
|
28
|
+
* tag it cannot read, such as `sv_SE`, and quietly uses the viewer's own
|
|
29
|
+
* locale for one it does not know, such as `zz`. A component that writes in
|
|
30
|
+
* its `language` while it renders checks it here, and falls back to English
|
|
31
|
+
* when this gives `undefined`.
|
|
32
|
+
*
|
|
33
|
+
* Examples:
|
|
34
|
+
* - "sv" => "sv"
|
|
35
|
+
* - "SV-se" => "sv-SE"
|
|
36
|
+
* - "sv_SE" => undefined
|
|
37
|
+
* - "zz" => undefined
|
|
38
|
+
* - undefined => undefined
|
|
39
|
+
*
|
|
40
|
+
* @param language - the language to write in
|
|
41
|
+
* @returns the locale, or `undefined`
|
|
42
|
+
*/
|
|
43
|
+
export function getSupportedLocale(language) {
|
|
44
|
+
if (typeof language !== 'string') {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return Intl.NumberFormat.supportedLocalesOf(language)[0];
|
|
49
|
+
}
|
|
50
|
+
catch (_a) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reduce a language to the primary subtag that our language maps are keyed by.
|
|
3
|
+
*
|
|
4
|
+
* A `language` prop is typed, but a custom element takes whatever string an
|
|
5
|
+
* attribute carries — commonly a full BCP 47 tag such as `nb-NO`, since a host
|
|
6
|
+
* application often derives the language from `document.documentElement.lang`.
|
|
7
|
+
* Anything that is not a string must resolve rather than throw, so that an
|
|
8
|
+
* unexpected value can never break a component's render.
|
|
9
|
+
*
|
|
10
|
+
* Examples:
|
|
11
|
+
* - "nb" => "nb"
|
|
12
|
+
* - "nb-NO" => "nb"
|
|
13
|
+
* - "SV-SE" => "sv"
|
|
14
|
+
* - undefined => ""
|
|
15
|
+
*
|
|
16
|
+
* @param language - the language to reduce
|
|
17
|
+
* @returns the lower-cased primary subtag, or an empty string
|
|
18
|
+
*/
|
|
19
|
+
function getPrimarySubtag(language) {
|
|
20
|
+
if (typeof language !== 'string') {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
return language.toLowerCase().split('-', 1)[0];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The locale that `Intl` writes numbers and dates in for `language`, or
|
|
27
|
+
* `undefined` when it cannot write in that language. `Intl` throws on a
|
|
28
|
+
* tag it cannot read, such as `sv_SE`, and quietly uses the viewer's own
|
|
29
|
+
* locale for one it does not know, such as `zz`. A component that writes in
|
|
30
|
+
* its `language` while it renders checks it here, and falls back to English
|
|
31
|
+
* when this gives `undefined`.
|
|
32
|
+
*
|
|
33
|
+
* Examples:
|
|
34
|
+
* - "sv" => "sv"
|
|
35
|
+
* - "SV-se" => "sv-SE"
|
|
36
|
+
* - "sv_SE" => undefined
|
|
37
|
+
* - "zz" => undefined
|
|
38
|
+
* - undefined => undefined
|
|
39
|
+
*
|
|
40
|
+
* @param language - the language to write in
|
|
41
|
+
* @returns the locale, or `undefined`
|
|
42
|
+
*/
|
|
43
|
+
function getSupportedLocale(language) {
|
|
44
|
+
if (typeof language !== 'string') {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return Intl.NumberFormat.supportedLocalesOf(language)[0];
|
|
49
|
+
}
|
|
50
|
+
catch (_a) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { getPrimarySubtag as a, getSupportedLocale as g };
|