@lion/ui 0.13.0 → 0.14.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/components/accordion/src/LionAccordion.js +3 -2
- package/components/button/src/LionButton.js +6 -10
- package/components/button/src/LionButtonReset.js +2 -0
- package/components/button/src/LionButtonSubmit.js +2 -0
- package/components/button/test-suites/LionButton.suite.js +0 -14
- package/components/checkbox-group/src/LionCheckbox.js +3 -0
- package/components/checkbox-group/src/LionCheckboxGroup.js +2 -0
- package/components/checkbox-group/src/LionCheckboxIndeterminate.js +3 -0
- package/components/collapsible/src/LionCollapsible.js +4 -1
- package/components/combobox/src/LionCombobox.js +5 -0
- package/components/dialog/src/LionDialog.js +3 -0
- package/components/drawer/src/LionDrawer.js +9 -0
- package/components/fieldset/src/LionFieldset.js +1 -1
- package/components/form/src/LionForm.js +0 -1
- package/components/form-core/src/FormControlMixin.js +10 -0
- package/components/form-core/src/FormatMixin.js +1 -1
- package/components/form-core/src/validate/LionValidationFeedback.js +8 -10
- package/components/form-core/src/validate/ValidateMixin.js +23 -3
- package/components/form-core/test/validate/lion-validation-feedback.test.js +0 -22
- package/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.js +119 -0
- package/components/form-core/types/validate/validate.ts +1 -0
- package/components/icon/src/LionIcon.js +2 -0
- package/components/input-amount-dropdown/src/LionInputAmountDropdown.js +557 -0
- package/components/input-amount-dropdown/src/currencyUtil.js +324 -0
- package/components/input-amount-dropdown/src/formatters.js +24 -0
- package/components/input-amount-dropdown/src/localizeNamespaceLoader.js +7 -0
- package/components/input-amount-dropdown/src/parsers.js +23 -0
- package/components/input-amount-dropdown/src/serializers.js +24 -0
- package/components/input-amount-dropdown/src/validators.js +22 -0
- package/components/input-amount-dropdown/test/LionInputAmountDropdown.test.js +199 -0
- package/components/input-amount-dropdown/test-helpers/mimicUserChangingDropdown.js +22 -0
- package/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.js +322 -0
- package/components/input-amount-dropdown/translations/en.js +5 -0
- package/components/input-amount-dropdown/types/index.ts +234 -0
- package/components/input-file/src/LionInputFile.js +9 -0
- package/components/input-tel/src/LionInputTel.js +3 -0
- package/components/input-tel/types/index.ts +6 -0
- package/components/input-tel-dropdown/src/LionInputTelDropdown.js +2 -0
- package/components/listbox/src/LionListbox.js +2 -0
- package/components/localize/src/number/getFractionDigits.js +1 -1
- package/components/overlays/src/OverlayMixin.js +4 -0
- package/components/progress-indicator/src/LionProgressIndicator.js +4 -0
- package/components/radio-group/src/LionRadioGroup.js +3 -1
- package/components/select/src/LionSelect.js +4 -0
- package/components/select-rich/src/LionSelectInvoker.js +2 -0
- package/components/select-rich/src/LionSelectRich.js +2 -0
- package/components/steps/src/LionStep.js +0 -1
- package/components/steps/src/LionSteps.js +0 -1
- package/components/switch/src/LionSwitch.js +3 -0
- package/components/switch/src/LionSwitchButton.js +5 -0
- package/components/tabs/src/LionTabs.js +8 -0
- package/custom-elements.json +5581 -1475
- package/dist-types/components/accordion/src/LionAccordion.d.ts +3 -2
- package/dist-types/components/accordion/src/LionAccordion.d.ts.map +1 -1
- package/dist-types/components/button/src/LionButton.d.ts +2 -1
- package/dist-types/components/button/src/LionButton.d.ts.map +1 -1
- package/dist-types/components/button/src/LionButtonReset.d.ts +2 -0
- package/dist-types/components/button/src/LionButtonReset.d.ts.map +1 -1
- package/dist-types/components/button/src/LionButtonSubmit.d.ts +2 -0
- package/dist-types/components/button/src/LionButtonSubmit.d.ts.map +1 -1
- package/dist-types/components/button/test-suites/LionButton.suite.d.ts.map +1 -1
- package/dist-types/components/checkbox-group/src/LionCheckbox.d.ts +3 -0
- package/dist-types/components/checkbox-group/src/LionCheckbox.d.ts.map +1 -1
- package/dist-types/components/checkbox-group/src/LionCheckboxGroup.d.ts +2 -0
- package/dist-types/components/checkbox-group/src/LionCheckboxGroup.d.ts.map +1 -1
- package/dist-types/components/checkbox-group/src/LionCheckboxIndeterminate.d.ts +3 -0
- package/dist-types/components/checkbox-group/src/LionCheckboxIndeterminate.d.ts.map +1 -1
- package/dist-types/components/collapsible/src/LionCollapsible.d.ts +3 -1
- package/dist-types/components/collapsible/src/LionCollapsible.d.ts.map +1 -1
- package/dist-types/components/combobox/src/LionCombobox.d.ts +5 -0
- package/dist-types/components/combobox/src/LionCombobox.d.ts.map +1 -1
- package/dist-types/components/dialog/src/LionDialog.d.ts +3 -0
- package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
- package/dist-types/components/drawer/src/LionDrawer.d.ts +9 -0
- package/dist-types/components/drawer/src/LionDrawer.d.ts.map +1 -1
- package/dist-types/components/fieldset/src/LionFieldset.d.ts +1 -1
- package/dist-types/components/form/src/LionForm.d.ts.map +1 -1
- package/dist-types/components/form-core/src/FormControlMixin.d.ts.map +1 -1
- package/dist-types/components/form-core/src/validate/LionValidationFeedback.d.ts +4 -3
- package/dist-types/components/form-core/src/validate/LionValidationFeedback.d.ts.map +1 -1
- package/dist-types/components/form-core/src/validate/ValidateMixin.d.ts.map +1 -1
- package/dist-types/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.d.ts.map +1 -1
- package/dist-types/components/form-core/types/validate/validate.d.ts +1 -0
- package/dist-types/components/form-core/types/validate/validate.d.ts.map +1 -1
- package/dist-types/components/icon/src/LionIcon.d.ts +2 -0
- package/dist-types/components/icon/src/LionIcon.d.ts.map +1 -1
- package/dist-types/components/input-amount-dropdown/src/LionInputAmountDropdown.d.ts +221 -0
- package/dist-types/components/input-amount-dropdown/src/LionInputAmountDropdown.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/src/currencyUtil.d.ts +24 -0
- package/dist-types/components/input-amount-dropdown/src/currencyUtil.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/src/formatters.d.ts +4 -0
- package/dist-types/components/input-amount-dropdown/src/formatters.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/src/localizeNamespaceLoader.d.ts +2 -0
- package/dist-types/components/input-amount-dropdown/src/localizeNamespaceLoader.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/src/parsers.d.ts +2 -0
- package/dist-types/components/input-amount-dropdown/src/parsers.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/src/serializers.d.ts +3 -0
- package/dist-types/components/input-amount-dropdown/src/serializers.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/src/validators.d.ts +12 -0
- package/dist-types/components/input-amount-dropdown/src/validators.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/test/LionInputAmountDropdown.test.d.ts +7 -0
- package/dist-types/components/input-amount-dropdown/test/LionInputAmountDropdown.test.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/test-helpers/mimicUserChangingDropdown.d.ts +12 -0
- package/dist-types/components/input-amount-dropdown/test-helpers/mimicUserChangingDropdown.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.d.ts +13 -0
- package/dist-types/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/translations/en.d.ts +7 -0
- package/dist-types/components/input-amount-dropdown/translations/en.d.ts.map +1 -0
- package/dist-types/components/input-amount-dropdown/types/index.d.ts +86 -0
- package/dist-types/components/input-amount-dropdown/types/index.d.ts.map +1 -0
- package/dist-types/components/input-file/src/LionInputFile.d.ts +9 -0
- package/dist-types/components/input-file/src/LionInputFile.d.ts.map +1 -1
- package/dist-types/components/input-tel/src/LionInputTel.d.ts +3 -0
- package/dist-types/components/input-tel/src/LionInputTel.d.ts.map +1 -1
- package/dist-types/components/input-tel/types/index.d.ts +1 -1
- package/dist-types/components/input-tel/types/index.d.ts.map +1 -1
- package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts +2 -0
- package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts.map +1 -1
- package/dist-types/components/listbox/src/LionListbox.d.ts +2 -0
- package/dist-types/components/listbox/src/LionListbox.d.ts.map +1 -1
- package/dist-types/components/localize/src/number/getFractionDigits.d.ts +1 -1
- package/dist-types/components/overlays/src/OverlayMixin.d.ts +4 -0
- package/dist-types/components/overlays/src/OverlayMixin.d.ts.map +1 -1
- package/dist-types/components/progress-indicator/src/LionProgressIndicator.d.ts +3 -0
- package/dist-types/components/progress-indicator/src/LionProgressIndicator.d.ts.map +1 -1
- package/dist-types/components/radio-group/src/LionRadioGroup.d.ts +3 -1
- package/dist-types/components/radio-group/src/LionRadioGroup.d.ts.map +1 -1
- package/dist-types/components/select/src/LionSelect.d.ts +3 -0
- package/dist-types/components/select/src/LionSelect.d.ts.map +1 -1
- package/dist-types/components/select-rich/src/LionSelectInvoker.d.ts +2 -0
- package/dist-types/components/select-rich/src/LionSelectInvoker.d.ts.map +1 -1
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts +3 -1
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
- package/dist-types/components/steps/src/LionStep.d.ts +0 -1
- package/dist-types/components/steps/src/LionStep.d.ts.map +1 -1
- package/dist-types/components/steps/src/LionSteps.d.ts +0 -1
- package/dist-types/components/steps/src/LionSteps.d.ts.map +1 -1
- package/dist-types/components/switch/src/LionSwitch.d.ts +3 -0
- package/dist-types/components/switch/src/LionSwitch.d.ts.map +1 -1
- package/dist-types/components/switch/src/LionSwitchButton.d.ts +5 -0
- package/dist-types/components/switch/src/LionSwitchButton.d.ts.map +1 -1
- package/dist-types/components/tabs/src/LionTabs.d.ts +8 -0
- package/dist-types/components/tabs/src/LionTabs.d.ts.map +1 -1
- package/dist-types/exports/define/lion-input-amount-dropdown.d.ts +2 -0
- package/dist-types/exports/define/lion-input-amount-dropdown.d.ts.map +1 -0
- package/dist-types/exports/input-amount-dropdown-test-helpers.d.ts +2 -0
- package/dist-types/exports/input-amount-dropdown-test-helpers.d.ts.map +1 -0
- package/dist-types/exports/input-amount-dropdown-test-suites.d.ts +2 -0
- package/dist-types/exports/input-amount-dropdown-test-suites.d.ts.map +1 -0
- package/dist-types/exports/input-amount-dropdown.d.ts +2 -0
- package/dist-types/exports/input-amount-dropdown.d.ts.map +1 -0
- package/dist-types/tsconfig.tsbuildinfo +1 -1
- package/docs/components/accordion/api-table.md +8 -0
- package/docs/components/button/api-table.md +0 -3
- package/docs/components/checkbox-group/overview.md +2 -2
- package/docs/components/collapsible/api-table.md +7 -0
- package/docs/components/collapsible/overview.md +1 -1
- package/docs/components/combobox/api-table.md +7 -0
- package/docs/components/combobox/overview.md +1 -1
- package/docs/components/combobox/use-cases.md +1 -1
- package/docs/components/dialog/overview.md +1 -1
- package/docs/components/drawer/api-table.md +8 -0
- package/docs/components/fieldset/overview.md +5 -5
- package/docs/components/form/overview.md +3 -3
- package/docs/components/form/use-cases.md +2 -2
- package/docs/components/icon/use-cases.md +1 -1
- package/docs/components/input/overview.md +1 -1
- package/docs/components/input-amount/overview.md +3 -3
- package/docs/components/input-amount-dropdown/api-table.md +210 -0
- package/docs/components/input-amount-dropdown/index.md +13 -0
- package/docs/components/input-amount-dropdown/use-cases.md +103 -0
- package/docs/components/input-date/overview.md +3 -3
- package/docs/components/input-datepicker/overview.md +5 -5
- package/docs/components/input-email/overview.md +2 -2
- package/docs/components/input-file/api-table.md +7 -0
- package/docs/components/input-file/overview.md +1 -1
- package/docs/components/input-iban/overview.md +2 -2
- package/docs/components/input-range/overview.md +2 -2
- package/docs/components/input-stepper/overview.md +1 -1
- package/docs/components/input-tel/overview.md +1 -1
- package/docs/components/input-tel/use-cases.md +6 -6
- package/docs/components/input-tel-dropdown/overview.md +1 -1
- package/docs/components/listbox/use-cases.md +1 -1
- package/docs/components/radio-group/overview.md +3 -3
- package/docs/components/select/use-cases.md +1 -1
- package/docs/components/select-rich/api-table.md +0 -1
- package/docs/components/select-rich/use-cases.md +1 -1
- package/docs/components/tabs/api-table.md +7 -0
- package/docs/components/tabs/examples.md +1 -1
- package/docs/fundamentals/index.md +4 -4
- package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
- package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
- package/docs/fundamentals/rationales/accessibility.md +1 -1
- package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
- package/docs/fundamentals/systems/core/overview.md +1 -1
- package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
- package/docs/fundamentals/systems/form/model-value.md +1 -1
- package/docs/fundamentals/systems/form/overview.md +28 -28
- package/docs/fundamentals/systems/form/validate.md +19 -3
- package/docs/fundamentals/systems/localize/overview.md +4 -4
- package/docs/fundamentals/systems/overlays/configuration.md +2 -2
- package/docs/fundamentals/systems/overlays/overview.md +5 -5
- package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
- package/docs/fundamentals/tools/helpers/overview.md +1 -1
- package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
- package/docs/guides/how-to/create-a-custom-field.md +5 -5
- package/docs/guides/how-to/create-a-form.md +4 -4
- package/docs/guides/how-to/extend-a-native-input.md +2 -2
- package/docs/guides/index.md +8 -8
- package/docs/guides/principles/index.md +5 -5
- package/docs/guides/principles/styling.md +1 -1
- package/exports/define/lion-input-amount-dropdown.js +3 -0
- package/exports/input-amount-dropdown-test-helpers.js +1 -0
- package/exports/input-amount-dropdown-test-suites.js +1 -0
- package/exports/input-amount-dropdown.js +1 -0
- package/input-amount-dropdown-test-helpers.js +1 -0
- package/input-amount-dropdown-test-suites.js +1 -0
- package/input-amount-dropdown.js +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
import { html, css } from 'lit';
|
|
2
|
+
import { ref, createRef } from 'lit/directives/ref.js';
|
|
3
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
4
|
+
|
|
5
|
+
import { LionInputAmount } from '@lion/ui/input-amount.js';
|
|
6
|
+
import { currencyUtil } from './currencyUtil.js';
|
|
7
|
+
import { parseAmount } from './parsers.js';
|
|
8
|
+
import { formatAmount } from './formatters.js';
|
|
9
|
+
import { deserializer, serializer } from './serializers.js';
|
|
10
|
+
import { CurrencyAndAmount } from './validators.js';
|
|
11
|
+
import { localizeNamespaceLoader } from './localizeNamespaceLoader.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Note: one could consider to implement LionInputAmountDropdown as a
|
|
15
|
+
* [combobox](https://www.w3.org/TR/wai-aria-practices-1.2/#combobox).
|
|
16
|
+
* However, the currency dropdown does not directly set the textbox value, it only determines
|
|
17
|
+
* its region code. Therefore it does not comply to this criterium:
|
|
18
|
+
* "A combobox is an input widget with an associated popup that enables users to select a value for
|
|
19
|
+
* the combobox from a collection of possible values. In some implementations,
|
|
20
|
+
* the popup presents allowed values, while in other implementations, the popup presents suggested
|
|
21
|
+
* values, and users may either select one of the suggestions or type a value".
|
|
22
|
+
* We therefore decided to consider the dropdown a helper mechanism that does not set, but
|
|
23
|
+
* contributes to and helps format and validate the actual value.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {import('lit/directives/ref.js').Ref} Ref
|
|
28
|
+
* @typedef {import('lit').RenderOptions} RenderOptions
|
|
29
|
+
* @typedef {import('../../form-core/types/FormatMixinTypes.js').FormatHost} FormatHost
|
|
30
|
+
* @typedef {import('../../input-tel/types/index.js').RegionCode} RegionCode
|
|
31
|
+
* @typedef {import('../types/index.js').TemplateDataForDropdownInputAmount} TemplateDataForDropdownInputAmount
|
|
32
|
+
* @typedef {import('../types/index.js').OnDropdownChangeEvent} OnDropdownChangeEvent
|
|
33
|
+
* @typedef {import('../types/index.js').DropdownRef} DropdownRef
|
|
34
|
+
* @typedef {import('../types/index.js').RegionMeta} RegionMeta
|
|
35
|
+
* @typedef {import('../types/index.js').CurrencyCode} CurrencyCode
|
|
36
|
+
* @typedef {import('../../select-rich/src/LionSelectRich.js').LionSelectRich} LionSelectRich
|
|
37
|
+
* @typedef {import('../../overlays/src/OverlayController.js').OverlayController} OverlayController
|
|
38
|
+
* @typedef {import('../../form-core/types/FormatMixinTypes.js').FormatOptions} FormatOptions
|
|
39
|
+
* @typedef {FormatOptions & {locale?:string;currency:string|undefined}} AmountFormatOptions
|
|
40
|
+
* @typedef {TemplateDataForDropdownInputAmount & {data: {regionMetaList:RegionMeta[]}}} TemplateDataForIntlInputAmount
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* LionInputAmountDropdown renders a dropdown like element next to the text field, inside the
|
|
45
|
+
* prefix, or suffix, slot. This could be a LionSelect, a LionSelectRich or a native select.
|
|
46
|
+
* By default, the native `<select>` element is used for this, so that it's as lightweight as
|
|
47
|
+
* possible. Also, it doesn't need to be a `FormControl`, because it's purely a helper element
|
|
48
|
+
* to provide better UX: the modelValue (the text field) contains all needed info:
|
|
49
|
+
* the currency code following ISO 4217 and its corresponding currency symbol using Intl.
|
|
50
|
+
*
|
|
51
|
+
* @customElement lion-input-amount-dropdown
|
|
52
|
+
*/
|
|
53
|
+
// @ts-expect-error - The types returned by 'parser(...)' are incompatible between these types. AmountDropdownModelValue' is not assignable to type 'number' */
|
|
54
|
+
export class LionInputAmountDropdown extends LionInputAmount {
|
|
55
|
+
/**
|
|
56
|
+
* @configure LitElement
|
|
57
|
+
* @type {any}
|
|
58
|
+
*/
|
|
59
|
+
static properties = {
|
|
60
|
+
preferredCurrencies: { type: Array },
|
|
61
|
+
allowedCurrencies: { type: Array },
|
|
62
|
+
__dropdownSlot: { type: String },
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
static localizeNamespaces = [
|
|
66
|
+
{ 'lion-input-amount-dropdown': localizeNamespaceLoader },
|
|
67
|
+
...super.localizeNamespaces,
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
refs = {
|
|
71
|
+
/** @type {DropdownRef} */
|
|
72
|
+
dropdown: /** @type {DropdownRef} */ (createRef()),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* This method provides a TemplateData object to be fed to pure template functions, a.k.a.
|
|
77
|
+
* Pure Templates™. The goal is to totally decouple presentation from logic here, so that
|
|
78
|
+
* Subclassers can override all content without having to loose private info contained
|
|
79
|
+
* within the template function that was overridden.
|
|
80
|
+
*
|
|
81
|
+
* Subclassers would need to make sure all the contents of the TemplateData object are implemented
|
|
82
|
+
* by making sure they are coupled to the right 'ref' ([data-ref=dropdown] in this example),
|
|
83
|
+
* with the help of lit's spread operator directive.
|
|
84
|
+
* To enhance this process, the TemplateData object is completely typed. Ideally, this would be
|
|
85
|
+
* enhanced by providing linters that make sure all of their required members are implemented by
|
|
86
|
+
* a Subclasser.
|
|
87
|
+
* When a Subclasser wants to add more data, this can be done via:
|
|
88
|
+
* @example
|
|
89
|
+
* ```js
|
|
90
|
+
* get _templateDataDropdown() {
|
|
91
|
+
* return {
|
|
92
|
+
* ...super._templateDataDropdown,
|
|
93
|
+
* myExtraData: { x: 1, y: 2 },
|
|
94
|
+
* }
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
* @overridable
|
|
98
|
+
* @type {TemplateDataForDropdownInputAmount}
|
|
99
|
+
*/
|
|
100
|
+
get _templateDataDropdown() {
|
|
101
|
+
const refs = {
|
|
102
|
+
dropdown: {
|
|
103
|
+
ref: this.refs.dropdown,
|
|
104
|
+
props: {
|
|
105
|
+
style: `height: 100%;`,
|
|
106
|
+
},
|
|
107
|
+
listeners: {
|
|
108
|
+
change: this._onDropdownValueChange,
|
|
109
|
+
'model-value-changed': this._onDropdownValueChange,
|
|
110
|
+
},
|
|
111
|
+
labels: {
|
|
112
|
+
selectCurrency: this._localizeManager.msg('lion-input-amount-dropdown:selectCurrency'),
|
|
113
|
+
allCurrencies:
|
|
114
|
+
this._allCurrenciesLabel ||
|
|
115
|
+
this._localizeManager.msg('lion-input-amount-dropdown:allCurrencies'),
|
|
116
|
+
preferredCurrencies:
|
|
117
|
+
this._preferredCurrenciesLabel ||
|
|
118
|
+
this._localizeManager.msg('lion-input-amount-dropdown:suggestedCurrencies'),
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
input: this._inputNode,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
refs,
|
|
126
|
+
data: {
|
|
127
|
+
// @ts-expect-error - cannot cast string to CurrencyCode outside a TS file
|
|
128
|
+
currency: this.currency,
|
|
129
|
+
regionMetaList: this.__regionMetaList,
|
|
130
|
+
regionMetaListPreferred: this.__regionMetaListPreferred,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @returns {string}
|
|
137
|
+
*/
|
|
138
|
+
get _dropdownSlot() {
|
|
139
|
+
return /** @type {string} */ this.__dropdownSlot;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
set _dropdownSlot(position) {
|
|
143
|
+
if (position !== 'suffix' && position !== 'prefix') {
|
|
144
|
+
throw new Error('Only the suffix and prefix slots are valid positions for the dropdown.');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.__dropdownSlot = position;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static templates = {
|
|
151
|
+
dropdown: (/** @type {TemplateDataForDropdownInputAmount} */ templateDataForDropdown) => {
|
|
152
|
+
const { refs, data } = templateDataForDropdown;
|
|
153
|
+
const renderOption = (/** @type {RegionMeta} */ regionMeta) =>
|
|
154
|
+
html`${this.templates.dropdownOption(templateDataForDropdown, regionMeta)} `;
|
|
155
|
+
|
|
156
|
+
// TODO: once spread directive available, use it per ref
|
|
157
|
+
return html`
|
|
158
|
+
<select
|
|
159
|
+
${ref(refs?.dropdown?.ref)}
|
|
160
|
+
aria-label="${refs?.dropdown?.labels?.selectCurrency}"
|
|
161
|
+
@change="${refs?.dropdown?.listeners?.change}"
|
|
162
|
+
style="${refs?.dropdown?.props?.style}"
|
|
163
|
+
>
|
|
164
|
+
${data?.regionMetaListPreferred?.length
|
|
165
|
+
? html`
|
|
166
|
+
<optgroup label="${refs?.dropdown?.labels?.preferredCurrencies}">
|
|
167
|
+
${data.regionMetaListPreferred.map(renderOption)}
|
|
168
|
+
</optgroup>
|
|
169
|
+
<optgroup label="${refs?.dropdown?.labels?.allCurrencies}">
|
|
170
|
+
${data?.regionMetaList?.map(renderOption)}
|
|
171
|
+
</optgroup>
|
|
172
|
+
`
|
|
173
|
+
: html` ${data?.regionMetaList?.map(renderOption)}`}
|
|
174
|
+
</select>
|
|
175
|
+
`;
|
|
176
|
+
},
|
|
177
|
+
/**
|
|
178
|
+
* @param {TemplateDataForDropdownInputAmount} templateDataForDropdown
|
|
179
|
+
* @param {RegionMeta} contextData
|
|
180
|
+
*/
|
|
181
|
+
// eslint-disable-next-line class-methods-use-this
|
|
182
|
+
dropdownOption: (
|
|
183
|
+
templateDataForDropdown,
|
|
184
|
+
{ currencyCode, nameForLocale, currencySymbol },
|
|
185
|
+
) => html`
|
|
186
|
+
<option
|
|
187
|
+
value="${currencyCode}"
|
|
188
|
+
aria-label="${ifDefined(
|
|
189
|
+
nameForLocale && currencySymbol ? `${nameForLocale}, ${currencySymbol}` : '',
|
|
190
|
+
)}"
|
|
191
|
+
>
|
|
192
|
+
${currencyCode} (${currencySymbol})
|
|
193
|
+
</option>
|
|
194
|
+
`,
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* @configure LitElement
|
|
199
|
+
* @enhance LionInputAmountDropdown
|
|
200
|
+
*/
|
|
201
|
+
static styles = [
|
|
202
|
+
super.styles,
|
|
203
|
+
css`
|
|
204
|
+
/**
|
|
205
|
+
* We need to align the height of the dropdown with the height of the text field.
|
|
206
|
+
* We target the HTMLDivElement (render wrapper from SlotMixin) here. Its child,
|
|
207
|
+
* [data-ref=dropdown], receives a 100% height as well via inline styles (since we
|
|
208
|
+
* can't target from shadow styles).
|
|
209
|
+
*/
|
|
210
|
+
::slotted([slot='prefix']),
|
|
211
|
+
::slotted([slot='suffix']) {
|
|
212
|
+
height: 100%;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* visually hiding the 'after' slot, leaving it as sr-only (screen-reader only)
|
|
217
|
+
* source: https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html
|
|
218
|
+
*/
|
|
219
|
+
::slotted([slot='after']:not(:focus):not(:active)) {
|
|
220
|
+
clip: rect(0 0 0 0);
|
|
221
|
+
clip-path: inset(50%);
|
|
222
|
+
height: 1px;
|
|
223
|
+
overflow: hidden;
|
|
224
|
+
position: absolute;
|
|
225
|
+
white-space: nowrap;
|
|
226
|
+
width: 1px;
|
|
227
|
+
}
|
|
228
|
+
`,
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @configure SlotMixin
|
|
233
|
+
*/
|
|
234
|
+
get slots() {
|
|
235
|
+
return {
|
|
236
|
+
...super.slots,
|
|
237
|
+
[this._dropdownSlot]: () => {
|
|
238
|
+
const ctor = /** @type {typeof LionInputAmountDropdown} */ (this.constructor);
|
|
239
|
+
const { templates } = ctor;
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
template: templates.dropdown(this._templateDataDropdown),
|
|
243
|
+
renderAsDirectHostChild: true,
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* @configure LocalizeMixin
|
|
251
|
+
*/
|
|
252
|
+
onLocaleUpdated() {
|
|
253
|
+
super.onLocaleUpdated();
|
|
254
|
+
|
|
255
|
+
const localeSplitted = this._localizeManager.locale.split('-');
|
|
256
|
+
/**
|
|
257
|
+
* @protected
|
|
258
|
+
* @type {RegionCode}
|
|
259
|
+
*/
|
|
260
|
+
this._langIso = /** @type {RegionCode} */ (
|
|
261
|
+
localeSplitted[localeSplitted.length - 1].toUpperCase()
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
this.__namesForLocale = new Intl.DisplayNames([this._langIso], {
|
|
265
|
+
type: 'currency',
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
this.__calculateActiveCurrency();
|
|
269
|
+
this.__createCurrencyMeta();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @lifecycle platform
|
|
274
|
+
*/
|
|
275
|
+
constructor() {
|
|
276
|
+
super();
|
|
277
|
+
|
|
278
|
+
this.parser = parseAmount;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* @param {import("../types/index.js").AmountDropdownModelValue} modelValue
|
|
282
|
+
* @param {import('../../localize/types/LocalizeMixinTypes.js').FormatNumberOptions} [givenOptions] Locale Options
|
|
283
|
+
*/
|
|
284
|
+
this.formatter = (modelValue, givenOptions) => formatAmount(modelValue, givenOptions, this);
|
|
285
|
+
this.serializer = serializer;
|
|
286
|
+
this.deserializer = deserializer;
|
|
287
|
+
|
|
288
|
+
this.defaultValidators = [new CurrencyAndAmount()];
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Slot position to render the dropdown in
|
|
292
|
+
* @type {string}
|
|
293
|
+
*/
|
|
294
|
+
this.__dropdownSlot = 'prefix';
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Regions that will be shown on top of the dropdown
|
|
298
|
+
* @type {CurrencyCode[]}
|
|
299
|
+
*/
|
|
300
|
+
this.preferredCurrencies = [];
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Regions that are allowed to be selected in the dropdown.
|
|
304
|
+
* @type {CurrencyCode[]}
|
|
305
|
+
*/
|
|
306
|
+
this.allowedCurrencies = [];
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Group label for all countries, when preferredCountries are shown
|
|
310
|
+
* @protected
|
|
311
|
+
*/
|
|
312
|
+
this._allCurrenciesLabel = '';
|
|
313
|
+
/**
|
|
314
|
+
* Group label for preferred countries, when preferredCountries are shown
|
|
315
|
+
* @protected
|
|
316
|
+
*/
|
|
317
|
+
this._preferredCurrenciesLabel = '';
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Contains everything needed for rendering region options:
|
|
321
|
+
* region code, currency code, display name according to locale, display name
|
|
322
|
+
* @private
|
|
323
|
+
* @type {RegionMeta[]}
|
|
324
|
+
*/
|
|
325
|
+
this.__regionMetaList = [];
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* A filtered `this.__regionMetaList`, containing all regions provided in `preferredCurrencies`
|
|
329
|
+
* @private
|
|
330
|
+
* @type {RegionMeta[]}
|
|
331
|
+
*/
|
|
332
|
+
this.__regionMetaListPreferred = [];
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* @protected
|
|
336
|
+
* @type {EventListener}
|
|
337
|
+
*/
|
|
338
|
+
this._onDropdownValueChange = this._onDropdownValueChange.bind(this);
|
|
339
|
+
/**
|
|
340
|
+
* @private
|
|
341
|
+
* @type {EventListener}
|
|
342
|
+
*/
|
|
343
|
+
this.__syncCurrencyWithDropdown = this.__syncCurrencyWithDropdown.bind(this);
|
|
344
|
+
|
|
345
|
+
this._currencyUtil = currencyUtil;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @lifecycle LitElement
|
|
350
|
+
* @param {import('lit-element').PropertyValues } changedProperties
|
|
351
|
+
*/
|
|
352
|
+
willUpdate(changedProperties) {
|
|
353
|
+
super.willUpdate(changedProperties);
|
|
354
|
+
|
|
355
|
+
if (changedProperties.has('allowedCurrencies')) {
|
|
356
|
+
this.__createCurrencyMeta();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @param {import('lit-element').PropertyValues } changedProperties
|
|
362
|
+
*/
|
|
363
|
+
updated(changedProperties) {
|
|
364
|
+
super.updated(changedProperties);
|
|
365
|
+
|
|
366
|
+
this.__syncCurrencyWithDropdown();
|
|
367
|
+
|
|
368
|
+
if (changedProperties.has('disabled') || changedProperties.has('readOnly')) {
|
|
369
|
+
if (this.disabled || this.readOnly) {
|
|
370
|
+
this.refs.dropdown?.value?.setAttribute('disabled', '');
|
|
371
|
+
} else {
|
|
372
|
+
this.refs.dropdown?.value?.removeAttribute('disabled');
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (changedProperties.has('allowedCurrencies') && this.allowedCurrencies.length > 0) {
|
|
377
|
+
this.__calculateActiveCurrency();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* @lifecycle LitElement
|
|
383
|
+
* @param {import('lit-element').PropertyValues } changedProperties
|
|
384
|
+
*/
|
|
385
|
+
firstUpdated(changedProperties) {
|
|
386
|
+
super.firstUpdated?.(changedProperties);
|
|
387
|
+
this._initModelValueBasedOnDropdown();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @protected
|
|
392
|
+
*/
|
|
393
|
+
_initModelValueBasedOnDropdown() {
|
|
394
|
+
if (!this._initialModelValue && !this.dirty) {
|
|
395
|
+
this.__initializedCurrencyCode = this.currency;
|
|
396
|
+
this._initialModelValue = { currency: this.currency };
|
|
397
|
+
this.modelValue = this._initialModelValue;
|
|
398
|
+
this.initInteractionState();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Used for Required validation and computation of interaction states.
|
|
404
|
+
* We need to override this, because we prefill the input with the currency code, but for proper UX,
|
|
405
|
+
* we don't consider this as having interaction state `prefilled`
|
|
406
|
+
* @param {string} modelValue
|
|
407
|
+
* @return {boolean}
|
|
408
|
+
* @protected
|
|
409
|
+
*/
|
|
410
|
+
_isEmpty(modelValue = this.modelValue) {
|
|
411
|
+
return super._isEmpty(modelValue) || this.currency === this.__initializedCurrencyCode;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* @protected
|
|
416
|
+
* @param {OnDropdownChangeEvent} event
|
|
417
|
+
*/
|
|
418
|
+
_onDropdownValueChange(event) {
|
|
419
|
+
const isInitializing = event.detail?.initialize;
|
|
420
|
+
const dropdownElement = event.target;
|
|
421
|
+
const dropdownValue = /** @type {RegionCode} */ (
|
|
422
|
+
dropdownElement.modelValue || dropdownElement.value
|
|
423
|
+
);
|
|
424
|
+
if (isInitializing || this.currency === dropdownValue) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const prevCurrency = this.currency;
|
|
429
|
+
|
|
430
|
+
/** @type {RegionCode | string} */
|
|
431
|
+
this.currency = dropdownValue;
|
|
432
|
+
|
|
433
|
+
if (prevCurrency !== this.currency && !this.focused) {
|
|
434
|
+
if (!this.value) {
|
|
435
|
+
this.modelValue = { currency: this.currency, amount: this.value };
|
|
436
|
+
} else {
|
|
437
|
+
/** @type {AmountFormatOptions} */
|
|
438
|
+
(this.formatOptions).currency = this.currency;
|
|
439
|
+
this.modelValue = this._callParser(this.value);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* @private
|
|
446
|
+
*/
|
|
447
|
+
__syncCurrencyWithDropdown(currencyCode = this.currency) {
|
|
448
|
+
const dropdownElement = this.refs.dropdown?.value;
|
|
449
|
+
if (!dropdownElement || !currencyCode) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if ('modelValue' in dropdownElement) {
|
|
454
|
+
const dropdownCurrencyCode = dropdownElement.modelValue;
|
|
455
|
+
if (dropdownCurrencyCode === currencyCode) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
/** @type {* & FormatHost} */ (dropdownElement).modelValue = currencyCode;
|
|
459
|
+
} else {
|
|
460
|
+
const dropdownCurrencyCode = dropdownElement.value;
|
|
461
|
+
if (dropdownCurrencyCode === currencyCode) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
/** @type {HTMLSelectElement} */ (dropdownElement).value = currencyCode;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Prepares data for options, like "Greece (Ελλάδα)", where "Greece" is `nameForLocale` and
|
|
470
|
+
* "Ελλάδα" `nameForRegion`.
|
|
471
|
+
* This should be run on change of:
|
|
472
|
+
* - allowedCurrencies
|
|
473
|
+
* - locale
|
|
474
|
+
* @private
|
|
475
|
+
*/
|
|
476
|
+
__createCurrencyMeta() {
|
|
477
|
+
if (!this._allowedOrAllCurrencies?.length || !this.__namesForLocale) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
this.__regionMetaList = [];
|
|
482
|
+
this.__regionMetaListPreferred = [];
|
|
483
|
+
|
|
484
|
+
this._allowedOrAllCurrencies.forEach(currencyCode => {
|
|
485
|
+
const destinationList = this.preferredCurrencies.includes(currencyCode)
|
|
486
|
+
? this.__regionMetaListPreferred
|
|
487
|
+
: this.__regionMetaList;
|
|
488
|
+
|
|
489
|
+
destinationList.push({
|
|
490
|
+
currencyCode,
|
|
491
|
+
nameForLocale: this.__namesForLocale?.of(currencyCode),
|
|
492
|
+
currencySymbol: this._currencyUtil.getCurrencySymbol(currencyCode, this._langIso ?? ''),
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Usually, we don't use composition in regular LionFields (non choice-groups). Here we use a LionSelect(Rich) inside.
|
|
499
|
+
* We don't want to repropagate any children, since an Application Developer is not concerned with these internals (see repropate logic in FormControlMixin)
|
|
500
|
+
* Also, we don't want to give (wrong) info to InteractionStateMixin, that will set the wrong interaction states based on child info.
|
|
501
|
+
* TODO: Make "this._repropagationRole !== 'child'" the default for FormControlMixin
|
|
502
|
+
* (so that FormControls used within are never repropagated for LionFields)
|
|
503
|
+
* @protected
|
|
504
|
+
* @configure FormControlMixin: don't repropagate any children
|
|
505
|
+
*/
|
|
506
|
+
// eslint-disable-next-line class-methods-use-this
|
|
507
|
+
_repropagationCondition() {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
__calculateActiveCurrency() {
|
|
512
|
+
// 1. Get the currency from pre-configured allowed currencies (if one entry)
|
|
513
|
+
if (this.allowedCurrencies?.length === 1) {
|
|
514
|
+
[this.currency] = this.allowedCurrencies;
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// 2. Try to get the currency from user input
|
|
519
|
+
if (this.modelValue?.currency && this.allowedCurrencies?.includes(this.modelValue?.currency)) {
|
|
520
|
+
this.currency = this.modelValue.currency;
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// 3. Try to get the currency from the preferred currencies
|
|
525
|
+
if (this.preferredCurrencies?.length > 0) {
|
|
526
|
+
[this.currency] = this.preferredCurrencies;
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// 4. Try to get the currency from locale
|
|
531
|
+
if (
|
|
532
|
+
this._langIso &&
|
|
533
|
+
this._currencyUtil?.countryToCurrencyMap.has(this._langIso) &&
|
|
534
|
+
this._allowedOrAllCurrencies.includes(
|
|
535
|
+
// @ts-expect-error - Set.get always returns a CurrencyCode.
|
|
536
|
+
this._currencyUtil?.countryToCurrencyMap.get(this._langIso),
|
|
537
|
+
)
|
|
538
|
+
) {
|
|
539
|
+
this.currency = this._currencyUtil?.countryToCurrencyMap.get(this._langIso);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// 5. Not derivable
|
|
544
|
+
this.currency = undefined;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Used for rendering the region/currency list
|
|
549
|
+
* @property _allowedOrAllRegions
|
|
550
|
+
* @type {CurrencyCode[]}
|
|
551
|
+
*/
|
|
552
|
+
get _allowedOrAllCurrencies() {
|
|
553
|
+
return this.allowedCurrencies?.length
|
|
554
|
+
? this.allowedCurrencies
|
|
555
|
+
: Array.from(this._currencyUtil?.allCurrencies) || [];
|
|
556
|
+
}
|
|
557
|
+
}
|