From 2627124c97ae4575dcf9f4ee299a11a5c11eb3eb Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 12 Sep 2025 05:18:33 +0000
Subject: [PATCH 01/14] Initial plan
From 2055e698be0f8a00ab38718ae31c01f81ebc2172 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 12 Sep 2025 05:22:30 +0000
Subject: [PATCH 02/14] Initial exploration and planning for phone-intl feature
Co-authored-by: micahmills <2531805+micahmills@users.noreply.github.com>
---
package-lock.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index bb7575bd..7f2a11dd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@disciple.tools/web-components",
- "version": "0.7.9",
+ "version": "0.7.14",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@disciple.tools/web-components",
- "version": "0.7.9",
+ "version": "0.7.14",
"license": "GPL-2.0-or-later",
"dependencies": {
"@lit/localize": "^0.12.1",
From 8abb26b17876a95e7f71119604088bc1e9264708 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 12 Sep 2025 05:31:43 +0000
Subject: [PATCH 03/14] Implement phone-intl feature for dt-multi-text
component
Co-authored-by: micahmills <2531805+micahmills@users.noreply.github.com>
---
package-lock.json | 26 +++
package.json | 2 +
.../form/dt-multi-text/dt-multi-text.js | 214 ++++++++++++++++++
.../dt-multi-text/dt-multi-text.stories.js | 28 ++-
.../form/dt-multi-text/dt-multi-text.test.js | 40 ++++
5 files changed, 309 insertions(+), 1 deletion(-)
diff --git a/package-lock.json b/package-lock.json
index 7f2a11dd..ba6490b8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,8 @@
"license": "GPL-2.0-or-later",
"dependencies": {
"@lit/localize": "^0.12.1",
+ "country-list-with-dial-code-and-flag": "^5.1.1",
+ "i18n-iso-countries": "^7.14.0",
"iconify-icon": "^1.0.2",
"lit": "^3.1.4"
},
@@ -6167,6 +6169,12 @@
"url": "https://opencollective.com/core-js"
}
},
+ "node_modules/country-list-with-dial-code-and-flag": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/country-list-with-dial-code-and-flag/-/country-list-with-dial-code-and-flag-5.1.1.tgz",
+ "integrity": "sha512-dj93ztplLEV2svD4+oOKTnR+8+cQSNf8kB051w+kYIoxcW6K0BPLgWYqwJjEpvoee60FTBufmi7jvcLPEgfpjg==",
+ "license": "ISC"
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -6421,6 +6429,12 @@
"integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==",
"dev": true
},
+ "node_modules/diacritics": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
+ "integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==",
+ "license": "MIT"
+ },
"node_modules/diff": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
@@ -8136,6 +8150,18 @@
"url": "https://github.com/sponsors/typicode"
}
},
+ "node_modules/i18n-iso-countries": {
+ "version": "7.14.0",
+ "resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.14.0.tgz",
+ "integrity": "sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==",
+ "license": "MIT",
+ "dependencies": {
+ "diacritics": "1.3.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/iconify-icon": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-1.0.2.tgz",
diff --git a/package.json b/package.json
index ff773651..ae10fecf 100644
--- a/package.json
+++ b/package.json
@@ -42,6 +42,8 @@
},
"dependencies": {
"@lit/localize": "^0.12.1",
+ "country-list-with-dial-code-and-flag": "^5.1.1",
+ "i18n-iso-countries": "^7.14.0",
"iconify-icon": "^1.0.2",
"lit": "^3.1.4"
},
diff --git a/src/components/form/dt-multi-text/dt-multi-text.js b/src/components/form/dt-multi-text/dt-multi-text.js
index aee869ad..494b1373 100644
--- a/src/components/form/dt-multi-text/dt-multi-text.js
+++ b/src/components/form/dt-multi-text/dt-multi-text.js
@@ -4,6 +4,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { when } from 'lit/directives/when.js';
import { DtText } from '../dt-text/dt-text.js';
import '../../icons/dt-icon.js';
+import CountryList from 'country-list-with-dial-code-and-flag';
/**
* Field to edit multiple text values with ability to add/remove values.
@@ -144,6 +145,43 @@ export class DtMultiText extends DtText {
.field-container:has(.btn-remove) ~ .icon-overlay {
inset-inline-end: 5.5rem;
}
+
+ /* Phone-intl specific styles */
+ .phone-intl-container {
+ display: flex;
+ flex-direction: row;
+ gap: 0.25rem;
+ }
+ .phone-intl-container .country-select {
+ flex-shrink: 0;
+ min-width: 120px;
+ padding: var(--dt-form-padding, 0.5333333333rem);
+ border: 1px solid var(--dt-multi-text-border-color, #fefefe);
+ border-radius: var(--dt-multi-text-border-radius, 0);
+ background-color: var(--dt-multi-text-background-color, #fefefe);
+ box-shadow: var(
+ --dt-multi-text-box-shadow,
+ var(
+ --dt-form-input-box-shadow,
+ inset 0 1px 2px hsl(0deg 0% 4% / 10%)
+ )
+ );
+ font-family: inherit;
+ font-size: 1rem;
+ color: var(--dt-form-text-color, #000);
+ }
+ .phone-intl-container .country-select:disabled {
+ background-color: var(
+ --dt-text-disabled-background-color,
+ var(--dt-form-disabled-background-color, #e6e6e6)
+ );
+ cursor: not-allowed;
+ }
+ .phone-intl-container input[data-type="phone"] {
+ flex-grow: 1;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
`,
];
}
@@ -158,6 +196,58 @@ export class DtMultiText extends DtText {
};
}
+ constructor() {
+ super();
+ this._countries = CountryList.getAll();
+ }
+
+ _getCountryOptions() {
+ return this._countries.map(country => {
+ const data = country.data;
+ return {
+ code: data.code,
+ name: data.name,
+ dialCode: data.dial_code,
+ flag: data.flag,
+ };
+ });
+ }
+
+ _parsePhoneValue(value) {
+ if (!value) return { countryCode: 'US', phoneNumber: '' };
+
+ // Try to extract country code and phone number from the value
+ // Expected format: "+1 555-123-4567" or similar
+ const match = value.match(/^(\+\d{1,4})\s*(.*)$/);
+ if (match) {
+ const dialCode = match[1];
+ const phoneNumber = match[2];
+
+ // Find countries by dial code
+ const matchingCountries = this._countries.filter(c => c.data.dial_code === dialCode);
+
+ // Prefer the country marked as preferred, otherwise take the first one
+ let country = matchingCountries.find(c => c.data.preferred);
+ if (!country && matchingCountries.length > 0) {
+ country = matchingCountries[0];
+ }
+
+ return {
+ countryCode: country ? country.data.code : 'US',
+ phoneNumber: phoneNumber,
+ };
+ }
+
+ // If no country code detected, assume it's just a phone number
+ return { countryCode: 'US', phoneNumber: value };
+ }
+
+ _formatPhoneValue(countryCode, phoneNumber) {
+ const country = this._countries.find(c => c.data.code === countryCode);
+ const dialCode = country ? country.data.dial_code : '+1';
+ return phoneNumber ? `${dialCode} ${phoneNumber}` : '';
+ }
+
updated(changedProperties) {
// if length of value was changed, focus the last element (which is new)
if (changedProperties.has('value')) {
@@ -253,6 +343,10 @@ export class DtMultiText extends DtText {
}
_inputFieldTemplate(item, itemCount) {
+ if (this.type === 'phone-intl') {
+ return this._phoneIntlFieldTemplate(item, itemCount);
+ }
+
return html`
+ `;
+ }
+
// rendering the input at 0 index
_renderInputFields() {
if (!this.value || !this.value.length) {
@@ -333,6 +491,62 @@ export class DtMultiText extends DtText {
}
}
+ _changeCountry(e) {
+ const key = e?.currentTarget?.dataset?.key;
+ if (key) {
+ const countryCode = e.target.value;
+ const phoneInput = e.target.parentElement.querySelector('input[data-type="phone"]');
+ const phoneNumber = phoneInput ? phoneInput.value : '';
+
+ const newValue = this._formatPhoneValue(countryCode, phoneNumber);
+
+ const event = new CustomEvent('change', {
+ detail: {
+ field: this.name,
+ oldValue: this.value,
+ },
+ });
+
+ // update this item's value in the list
+ this.value = this.value.map(x => ({
+ ...x,
+ value: x.key === key || x.tempKey === key ? newValue : x.value,
+ }));
+ event.detail.newValue = this.value;
+
+ this._setFormValue(this.value);
+ this.dispatchEvent(event);
+ }
+ }
+
+ _changePhone(e) {
+ const key = e?.currentTarget?.dataset?.key;
+ if (key) {
+ const phoneNumber = e.target.value;
+ const countrySelect = e.target.parentElement.querySelector('.country-select');
+ const countryCode = countrySelect ? countrySelect.value : 'US';
+
+ const newValue = this._formatPhoneValue(countryCode, phoneNumber);
+
+ const event = new CustomEvent('change', {
+ detail: {
+ field: this.name,
+ oldValue: this.value,
+ },
+ });
+
+ // update this item's value in the list
+ this.value = this.value.map(x => ({
+ ...x,
+ value: x.key === key || x.tempKey === key ? newValue : x.value,
+ }));
+ event.detail.newValue = this.value;
+
+ this._setFormValue(this.value);
+ this.dispatchEvent(event);
+ }
+ }
+
get classes() {
const classes = {
'text-input': true,
diff --git a/src/components/form/dt-multi-text/dt-multi-text.stories.js b/src/components/form/dt-multi-text/dt-multi-text.stories.js
index 0f28c52a..2198821a 100644
--- a/src/components/form/dt-multi-text/dt-multi-text.stories.js
+++ b/src/components/form/dt-multi-text/dt-multi-text.stories.js
@@ -22,7 +22,7 @@ export default {
placeholder: { control: 'text' },
type: {
control: 'select',
- options: ['text', 'password', 'email', 'number', 'tel', 'url'],
+ options: ['text', 'password', 'email', 'number', 'tel', 'url', 'phone-intl'],
defaultValue: 'text',
},
disabled: { control: 'boolean' },
@@ -196,3 +196,29 @@ LocalizeRTL.args = {
},
],
};
+
+export const PhoneIntl = Template.bind({});
+PhoneIntl.args = {
+ type: 'phone-intl',
+ label: 'Phone Numbers',
+ placeholder: 'Enter phone number',
+ value: [
+ {
+ verified: false,
+ value: '+1 555-123-4567',
+ key: 'phone_1',
+ },
+ {
+ verified: false,
+ value: '+44 20 7946 0958',
+ key: 'phone_2',
+ },
+ ],
+};
+
+export const PhoneIntlEmpty = Template.bind({});
+PhoneIntlEmpty.args = {
+ type: 'phone-intl',
+ label: 'Phone Numbers',
+ placeholder: 'Enter phone number',
+};
diff --git a/src/components/form/dt-multi-text/dt-multi-text.test.js b/src/components/form/dt-multi-text/dt-multi-text.test.js
index 9aa08ea1..310dc74f 100644
--- a/src/components/form/dt-multi-text/dt-multi-text.test.js
+++ b/src/components/form/dt-multi-text/dt-multi-text.test.js
@@ -168,4 +168,44 @@ describe('DtMultiText', () => {
expect(detail.newValue[0].value).to.equal('Value 1')
expect(detail.newValue[0].delete).to.equal(true);
});
+
+ describe('phone-intl type', () => {
+ it('renders country code dropdown for phone-intl type', async () => {
+ const el = await fixture(
+ html``
+ );
+
+ const countrySelect = el.shadowRoot.querySelector('.country-select');
+ expect(countrySelect).to.exist;
+ });
+
+ it('renders phone number input for phone-intl type', async () => {
+ const el = await fixture(
+ html``
+ );
+
+ const phoneInput = el.shadowRoot.querySelector('input[data-type="phone"]');
+ expect(phoneInput).to.exist;
+ });
+
+ it('handles phone-intl value format', async () => {
+ const el = await fixture(
+ html``
+ );
+
+ const countrySelect = el.shadowRoot.querySelector('.country-select');
+ const phoneInput = el.shadowRoot.querySelector('input[data-type="phone"]');
+
+ expect(countrySelect.value).to.equal('US');
+ expect(phoneInput.value).to.equal('555-123-4567');
+ });
+ });
});
From f6ac19fb43c72561e0cb5db93f09ef378a00168e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 12 Sep 2025 08:30:32 +0000
Subject: [PATCH 04/14] Implement improved phone-intl UI design and
localization features
Co-authored-by: micahmills <2531805+micahmills@users.noreply.github.com>
---
.../form/dt-multi-text/dt-multi-text.js | 66 ++++++++++++++++---
.../dt-multi-text/dt-multi-text.stories.js | 21 ++++++
2 files changed, 79 insertions(+), 8 deletions(-)
diff --git a/src/components/form/dt-multi-text/dt-multi-text.js b/src/components/form/dt-multi-text/dt-multi-text.js
index 494b1373..8d596660 100644
--- a/src/components/form/dt-multi-text/dt-multi-text.js
+++ b/src/components/form/dt-multi-text/dt-multi-text.js
@@ -2,9 +2,10 @@ import { html, css } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
import { classMap } from 'lit/directives/class-map.js';
import { when } from 'lit/directives/when.js';
+import CountryList from 'country-list-with-dial-code-and-flag';
+import countries from 'i18n-iso-countries';
import { DtText } from '../dt-text/dt-text.js';
import '../../icons/dt-icon.js';
-import CountryList from 'country-list-with-dial-code-and-flag';
/**
* Field to edit multiple text values with ability to add/remove values.
@@ -150,14 +151,17 @@ export class DtMultiText extends DtText {
.phone-intl-container {
display: flex;
flex-direction: row;
- gap: 0.25rem;
+ align-items: center;
+ gap: 0;
}
.phone-intl-container .country-select {
flex-shrink: 0;
- min-width: 120px;
+ width: 60px;
padding: var(--dt-form-padding, 0.5333333333rem);
border: 1px solid var(--dt-multi-text-border-color, #fefefe);
border-radius: var(--dt-multi-text-border-radius, 0);
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
background-color: var(--dt-multi-text-background-color, #fefefe);
box-shadow: var(
--dt-multi-text-box-shadow,
@@ -169,6 +173,7 @@ export class DtMultiText extends DtText {
font-family: inherit;
font-size: 1rem;
color: var(--dt-form-text-color, #000);
+ text-align: center;
}
.phone-intl-container .country-select:disabled {
background-color: var(
@@ -177,10 +182,22 @@ export class DtMultiText extends DtText {
);
cursor: not-allowed;
}
+ .phone-intl-container .dial-code {
+ flex-shrink: 0;
+ padding: var(--dt-form-padding, 0.5333333333rem);
+ border-top: 1px solid var(--dt-multi-text-border-color, #fefefe);
+ border-bottom: 1px solid var(--dt-multi-text-border-color, #fefefe);
+ background-color: var(--dt-multi-text-background-color, #f8f8f8);
+ font-family: inherit;
+ font-size: 1rem;
+ color: var(--dt-form-text-color, #000);
+ font-weight: 500;
+ }
.phone-intl-container input[data-type="phone"] {
flex-grow: 1;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
+ border-left: none;
}
`,
];
@@ -199,14 +216,37 @@ export class DtMultiText extends DtText {
constructor() {
super();
this._countries = CountryList.getAll();
+
+ // Register some common locales with i18n-iso-countries
+ // We'll register them on-demand in _getCountryOptions to avoid bundling all locales
}
_getCountryOptions() {
return this._countries.map(country => {
- const data = country.data;
+ const { data } = country;
+ // Use localized country names when possible, fallback to default
+ let localizedName = data.name;
+ try {
+ const locale = this.locale || 'en';
+ // Convert locale format: 'en_US' -> 'en', 'fr_FR' -> 'fr', etc.
+ const [isoLanguage] = locale.split('_');
+
+ // Try to get localized name, fallback to English or default
+ const localizedNameResult = countries.getName(data.code, isoLanguage) ||
+ countries.getName(data.code, 'en') ||
+ data.name;
+ if (localizedNameResult) {
+ localizedName = localizedNameResult;
+ }
+ } catch (e) {
+ // Fallback to default name if localization fails
+ // eslint-disable-next-line no-console
+ console.debug('Failed to get localized country name:', e);
+ }
+
return {
code: data.code,
- name: data.name,
+ name: localizedName,
dialCode: data.dial_code,
flag: data.flag,
};
@@ -229,12 +269,12 @@ export class DtMultiText extends DtText {
// Prefer the country marked as preferred, otherwise take the first one
let country = matchingCountries.find(c => c.data.preferred);
if (!country && matchingCountries.length > 0) {
- country = matchingCountries[0];
+ [country] = matchingCountries;
}
return {
countryCode: country ? country.data.code : 'US',
- phoneNumber: phoneNumber,
+ phoneNumber,
};
}
@@ -391,6 +431,7 @@ export class DtMultiText extends DtText {
_phoneIntlFieldTemplate(item, itemCount) {
const parsed = this._parsePhoneValue(item.value);
const countryOptions = this._getCountryOptions();
+ const selectedCountry = countryOptions.find(c => c.code === parsed.countryCode) || countryOptions.find(c => c.code === 'US');
return html`
@@ -407,10 +448,11 @@ export class DtMultiText extends DtText {
value="${country.code}"
?selected=${country.code === parsed.countryCode}
>
- ${country.flag} ${country.dialCode} ${country.name}
+ ${country.flag}
`)}
+
${selectedCountry?.dialCode || '+1'}
c.code === countryCode);
+ if (dialCodeDisplay && selectedCountry) {
+ dialCodeDisplay.textContent = selectedCountry.dialCode;
+ }
+
const newValue = this._formatPhoneValue(countryCode, phoneNumber);
const event = new CustomEvent('change', {
diff --git a/src/components/form/dt-multi-text/dt-multi-text.stories.js b/src/components/form/dt-multi-text/dt-multi-text.stories.js
index 2198821a..244dcc2c 100644
--- a/src/components/form/dt-multi-text/dt-multi-text.stories.js
+++ b/src/components/form/dt-multi-text/dt-multi-text.stories.js
@@ -222,3 +222,24 @@ PhoneIntlEmpty.args = {
label: 'Phone Numbers',
placeholder: 'Enter phone number',
};
+
+export const PhoneIntlLocalized = Template.bind({});
+PhoneIntlLocalized.decorators = [LocaleDecorator, FormDecorator];
+PhoneIntlLocalized.args = {
+ type: 'phone-intl',
+ lang: 'fr',
+ label: 'Numéros de téléphone',
+ placeholder: 'Entrez le numéro de téléphone',
+ value: [
+ {
+ verified: false,
+ value: '+33 1 42 86 83 26',
+ key: 'phone_1',
+ },
+ {
+ verified: false,
+ value: '+49 30 12345678',
+ key: 'phone_2',
+ },
+ ],
+};
From 0eb82d59500bf9e3216f86ba575b96cd83775a04 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 12 Sep 2025 09:03:20 +0000
Subject: [PATCH 05/14] Enhance phone-intl UI with improved dropdown display
and styling alignment
Co-authored-by: micahmills <2531805+micahmills@users.noreply.github.com>
---
phone-intl-test.html | 47 +++++++++++++++++++
.../form/dt-multi-text/dt-multi-text.js | 11 ++++-
2 files changed, 56 insertions(+), 2 deletions(-)
create mode 100644 phone-intl-test.html
diff --git a/phone-intl-test.html b/phone-intl-test.html
new file mode 100644
index 00000000..9290bf0d
--- /dev/null
+++ b/phone-intl-test.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
Phone International Test
+
+
+
+
+
Phone International Component Test
+
+
+
Empty Phone International
+
+
+
+
+
Populated Phone International
+
+
+
+
+
+
French Localized
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/form/dt-multi-text/dt-multi-text.js b/src/components/form/dt-multi-text/dt-multi-text.js
index 8d596660..595f91f1 100644
--- a/src/components/form/dt-multi-text/dt-multi-text.js
+++ b/src/components/form/dt-multi-text/dt-multi-text.js
@@ -156,7 +156,8 @@ export class DtMultiText extends DtText {
}
.phone-intl-container .country-select {
flex-shrink: 0;
- width: 60px;
+ min-width: 60px;
+ width: auto;
padding: var(--dt-form-padding, 0.5333333333rem);
border: 1px solid var(--dt-multi-text-border-color, #fefefe);
border-radius: var(--dt-multi-text-border-radius, 0);
@@ -174,6 +175,8 @@ export class DtMultiText extends DtText {
font-size: 1rem;
color: var(--dt-form-text-color, #000);
text-align: center;
+ height: auto;
+ line-height: 1.5;
}
.phone-intl-container .country-select:disabled {
background-color: var(
@@ -192,12 +195,16 @@ export class DtMultiText extends DtText {
font-size: 1rem;
color: var(--dt-form-text-color, #000);
font-weight: 500;
+ line-height: 1.5;
+ display: flex;
+ align-items: center;
}
.phone-intl-container input[data-type="phone"] {
flex-grow: 1;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-left: none;
+ line-height: 1.5;
}
`,
];
@@ -448,7 +455,7 @@ export class DtMultiText extends DtText {
value="${country.code}"
?selected=${country.code === parsed.countryCode}
>
- ${country.flag}
+ ${country.flag} ${country.name} ${country.dialCode}
`)}
From 4fd2cd3bd9e7a018beab8540a07dc61556839a89 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 12 Sep 2025 10:33:41 +0000
Subject: [PATCH 06/14] Replace HTML select with custom dropdown for phone-intl
component
Co-authored-by: micahmills <2531805+micahmills@users.noreply.github.com>
---
.../form/dt-multi-text/dt-multi-text.js | 182 ++++++++++++++++--
1 file changed, 168 insertions(+), 14 deletions(-)
diff --git a/src/components/form/dt-multi-text/dt-multi-text.js b/src/components/form/dt-multi-text/dt-multi-text.js
index 595f91f1..f15afe2e 100644
--- a/src/components/form/dt-multi-text/dt-multi-text.js
+++ b/src/components/form/dt-multi-text/dt-multi-text.js
@@ -153,8 +153,9 @@ export class DtMultiText extends DtText {
flex-direction: row;
align-items: center;
gap: 0;
+ position: relative;
}
- .phone-intl-container .country-select {
+ .phone-intl-container .country-button {
flex-shrink: 0;
min-width: 60px;
width: auto;
@@ -177,14 +178,65 @@ export class DtMultiText extends DtText {
text-align: center;
height: auto;
line-height: 1.5;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.25rem;
}
- .phone-intl-container .country-select:disabled {
+ .phone-intl-container .country-button:disabled {
background-color: var(
--dt-text-disabled-background-color,
var(--dt-form-disabled-background-color, #e6e6e6)
);
cursor: not-allowed;
}
+ .phone-intl-container .country-button:hover:not(:disabled) {
+ background-color: var(--dt-multi-text-background-color, #f0f0f0);
+ }
+ .phone-intl-container .country-dropdown {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ background-color: var(--dt-multi-text-background-color, #fefefe);
+ border: 1px solid var(--dt-multi-text-border-color, #fefefe);
+ border-radius: var(--dt-multi-text-border-radius, 0);
+ box-shadow: var(
+ --dt-multi-text-box-shadow,
+ var(
+ --dt-form-input-box-shadow,
+ 0 2px 8px hsl(0deg 0% 4% / 20%)
+ )
+ );
+ max-height: 200px;
+ overflow-y: auto;
+ min-width: 250px;
+ display: none;
+ }
+ .phone-intl-container .country-dropdown.open {
+ display: block;
+ }
+ .phone-intl-container .country-option {
+ padding: var(--dt-form-padding, 0.5333333333rem);
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ font-family: inherit;
+ font-size: 1rem;
+ color: var(--dt-form-text-color, #000);
+ border: none;
+ background: none;
+ width: 100%;
+ text-align: left;
+ }
+ .phone-intl-container .country-option:hover {
+ background-color: var(--dt-multi-text-background-color, #f0f0f0);
+ }
+ .phone-intl-container .country-option.selected {
+ background-color: var(--dt-multi-text-background-color, #e6f3ff);
+ }
.phone-intl-container .dial-code {
flex-shrink: 0;
padding: var(--dt-form-padding, 0.5333333333rem);
@@ -217,15 +269,23 @@ export class DtMultiText extends DtText {
type: Array,
reflect: true,
},
+ _openDropdownKey: {
+ type: String,
+ state: true,
+ },
};
}
constructor() {
super();
this._countries = CountryList.getAll();
+ this._openDropdownKey = null;
// Register some common locales with i18n-iso-countries
// We'll register them on-demand in _getCountryOptions to avoid bundling all locales
+
+ // Bind click outside handler
+ this._handleClickOutside = this._handleClickOutside.bind(this);
}
_getCountryOptions() {
@@ -305,6 +365,11 @@ export class DtMultiText extends DtText {
}
}
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ document.removeEventListener('click', this._handleClickOutside);
+ }
+
focusNewItem() {
const items = this.shadowRoot.querySelectorAll('input');
if (items && items.length) {
@@ -439,29 +504,41 @@ export class DtMultiText extends DtText {
const parsed = this._parsePhoneValue(item.value);
const countryOptions = this._getCountryOptions();
const selectedCountry = countryOptions.find(c => c.code === parsed.countryCode) || countryOptions.find(c => c.code === 'US');
+ const dropdownKey = item.key ?? item.tempKey;
+ const isDropdownOpen = this._openDropdownKey === dropdownKey;
return html`
-