@intl-tel-input/angular 29.1.2 → 29.2.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.
@@ -21,6 +21,7 @@ declare class IntlTelInput implements AfterViewInit, OnDestroy, OnChanges, Contr
21
21
  allowedNumberTypes?: AllOptions["allowedNumberTypes"];
22
22
  allowNumberExtensions?: AllOptions["allowNumberExtensions"];
23
23
  allowPhonewords?: AllOptions["allowPhonewords"];
24
+ classNames?: AllOptions["classNames"];
24
25
  containerClass?: AllOptions["containerClass"];
25
26
  countryNameLocale?: AllOptions["countryNameLocale"];
26
27
  countryNameOverrides?: AllOptions["countryNameOverrides"];
@@ -68,6 +69,7 @@ declare class IntlTelInput implements AfterViewInit, OnDestroy, OnChanges, Contr
68
69
  private lastEmittedErrorCode?;
69
70
  private pendingWriteValue?;
70
71
  private pendingUpdate;
72
+ private isWritingModelValue;
71
73
  private onChange;
72
74
  private onTouched;
73
75
  private onValidatorChange;
@@ -77,6 +79,7 @@ declare class IntlTelInput implements AfterViewInit, OnDestroy, OnChanges, Contr
77
79
  ngAfterViewInit(): void;
78
80
  private buildInitOptions;
79
81
  ngOnChanges(changes: SimpleChanges): void;
82
+ private setNumberFromModel;
80
83
  handleInput: () => void;
81
84
  handleBlur(event: FocusEvent): void;
82
85
  handleFocus(event: FocusEvent): void;
@@ -104,6 +107,6 @@ declare class IntlTelInput implements AfterViewInit, OnDestroy, OnChanges, Contr
104
107
  validate(_control: AbstractControl): ValidationErrors | null;
105
108
  registerOnValidatorChange(fn: () => void): void;
106
109
  static ɵfac: i0.ɵɵFactoryDeclaration<IntlTelInput, never>;
107
- static ɵcmp: i0.ɵɵComponentDeclaration<IntlTelInput, "intl-tel-input", never, { "initialValue": { "alias": "initialValue"; "required": false; }; "usePreciseValidation": { "alias": "usePreciseValidation"; "required": false; }; "inputAttributes": { "alias": "inputAttributes"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "countrySelectorMode": { "alias": "countrySelectorMode"; "required": false; }; "allowedNumberTypes": { "alias": "allowedNumberTypes"; "required": false; }; "allowNumberExtensions": { "alias": "allowNumberExtensions"; "required": false; }; "allowPhonewords": { "alias": "allowPhonewords"; "required": false; }; "containerClass": { "alias": "containerClass"; "required": false; }; "countryNameLocale": { "alias": "countryNameLocale"; "required": false; }; "countryNameOverrides": { "alias": "countryNameOverrides"; "required": false; }; "countryOrder": { "alias": "countryOrder"; "required": false; }; "countrySearch": { "alias": "countrySearch"; "required": false; }; "customPlaceholder": { "alias": "customPlaceholder"; "required": false; }; "dropdownAlwaysOpen": { "alias": "dropdownAlwaysOpen"; "required": false; }; "dropdownParent": { "alias": "dropdownParent"; "required": false; }; "excludeCountries": { "alias": "excludeCountries"; "required": false; }; "matchDropdownWidth": { "alias": "matchDropdownWidth"; "required": false; }; "formatAsYouType": { "alias": "formatAsYouType"; "required": false; }; "hiddenInputs": { "alias": "hiddenInputs"; "required": false; }; "uiTranslations": { "alias": "uiTranslations"; "required": false; }; "initialCountry": { "alias": "initialCountry"; "required": false; }; "initialCountryLookup": { "alias": "initialCountryLookup"; "required": false; }; "loadUtils": { "alias": "loadUtils"; "required": false; }; "numberDisplayFormat": { "alias": "numberDisplayFormat"; "required": false; }; "onlyCountries": { "alias": "onlyCountries"; "required": false; }; "placeholderNumberPolicy": { "alias": "placeholderNumberPolicy"; "required": false; }; "placeholderNumberType": { "alias": "placeholderNumberType"; "required": false; }; "searchInputClass": { "alias": "searchInputClass"; "required": false; }; "separateDialCode": { "alias": "separateDialCode"; "required": false; }; "showFlags": { "alias": "showFlags"; "required": false; }; "strictMode": { "alias": "strictMode"; "required": false; }; "strictRejectAnimation": { "alias": "strictRejectAnimation"; "required": false; }; }, { "numberChange": "numberChange"; "countryChange": "countryChange"; "validityChange": "validityChange"; "errorCodeChange": "errorCodeChange"; "openCountrySelector": "openCountrySelector"; "closeCountrySelector": "closeCountrySelector"; "strictReject": "strictReject"; "blur": "blur"; "focus": "focus"; "keydown": "keydown"; "keyup": "keyup"; "paste": "paste"; "click": "click"; }, never, never, true, never>;
110
+ static ɵcmp: i0.ɵɵComponentDeclaration<IntlTelInput, "intl-tel-input", never, { "initialValue": { "alias": "initialValue"; "required": false; }; "usePreciseValidation": { "alias": "usePreciseValidation"; "required": false; }; "inputAttributes": { "alias": "inputAttributes"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "countrySelectorMode": { "alias": "countrySelectorMode"; "required": false; }; "allowedNumberTypes": { "alias": "allowedNumberTypes"; "required": false; }; "allowNumberExtensions": { "alias": "allowNumberExtensions"; "required": false; }; "allowPhonewords": { "alias": "allowPhonewords"; "required": false; }; "classNames": { "alias": "classNames"; "required": false; }; "containerClass": { "alias": "containerClass"; "required": false; }; "countryNameLocale": { "alias": "countryNameLocale"; "required": false; }; "countryNameOverrides": { "alias": "countryNameOverrides"; "required": false; }; "countryOrder": { "alias": "countryOrder"; "required": false; }; "countrySearch": { "alias": "countrySearch"; "required": false; }; "customPlaceholder": { "alias": "customPlaceholder"; "required": false; }; "dropdownAlwaysOpen": { "alias": "dropdownAlwaysOpen"; "required": false; }; "dropdownParent": { "alias": "dropdownParent"; "required": false; }; "excludeCountries": { "alias": "excludeCountries"; "required": false; }; "matchDropdownWidth": { "alias": "matchDropdownWidth"; "required": false; }; "formatAsYouType": { "alias": "formatAsYouType"; "required": false; }; "hiddenInputs": { "alias": "hiddenInputs"; "required": false; }; "uiTranslations": { "alias": "uiTranslations"; "required": false; }; "initialCountry": { "alias": "initialCountry"; "required": false; }; "initialCountryLookup": { "alias": "initialCountryLookup"; "required": false; }; "loadUtils": { "alias": "loadUtils"; "required": false; }; "numberDisplayFormat": { "alias": "numberDisplayFormat"; "required": false; }; "onlyCountries": { "alias": "onlyCountries"; "required": false; }; "placeholderNumberPolicy": { "alias": "placeholderNumberPolicy"; "required": false; }; "placeholderNumberType": { "alias": "placeholderNumberType"; "required": false; }; "searchInputClass": { "alias": "searchInputClass"; "required": false; }; "separateDialCode": { "alias": "separateDialCode"; "required": false; }; "showFlags": { "alias": "showFlags"; "required": false; }; "strictMode": { "alias": "strictMode"; "required": false; }; "strictRejectAnimation": { "alias": "strictRejectAnimation"; "required": false; }; }, { "numberChange": "numberChange"; "countryChange": "countryChange"; "validityChange": "validityChange"; "errorCodeChange": "errorCodeChange"; "openCountrySelector": "openCountrySelector"; "closeCountrySelector": "closeCountrySelector"; "strictReject": "strictReject"; "blur": "blur"; "focus": "focus"; "keydown": "keydown"; "keyup": "keyup"; "paste": "paste"; "click": "click"; }, never, never, true, never>;
108
111
  }
109
112
  export default IntlTelInput;
@@ -13,8 +13,8 @@ var rawCountryData = [
13
13
  // Åland Islands (AKA Aland Islands)
14
14
  "358",
15
15
  1,
16
- ["18", "4"],
17
- // (4 is a mobile range shared with FI)
16
+ ["18", "4", "50"],
17
+ // (4 and 50 are mobile ranges shared with FI)
18
18
  // NOTE: https://en.wikipedia.org/wiki/Telephone%20numbers%20in%20%C3%85land says some 4XXX ranges (e.g. 4570) are specific to AX, but LPN doesn't respect this (https://libphonenumber.appspot.com/phonenumberparser?number=%2B3584570123456 says region=FI) so we won't either. Also it's too much of a maintenance burden to keep track of. Keep the 4 area code range here so that if the user selects AX and types this kind of number, we wont change the flag to FI. Whereas if they type a FI-only range then we will.
19
19
  "0"
20
20
  ],
@@ -561,8 +561,8 @@ var rawCountryData = [
561
561
  // Finland
562
562
  "358",
563
563
  0,
564
- ["4"],
565
- // (mobile range shared with AX)
564
+ ["4", "50"],
565
+ // (mobile ranges shared with AX)
566
566
  "0"
567
567
  ],
568
568
  [
@@ -669,7 +669,8 @@ var rawCountryData = [
669
669
  // Guernsey
670
670
  "44",
671
671
  1,
672
- ["1481", "7781", "7839", "7911"],
672
+ //* Only 79111 and 79117 belong to GG - the rest of 7911 is GB (e.g. 79110).
673
+ ["1481", "7781", "7839", "79111", "79117"],
673
674
  "0"
674
675
  ],
675
676
  [
@@ -800,7 +801,8 @@ var rawCountryData = [
800
801
  // Jersey
801
802
  "44",
802
803
  3,
803
- ["1534", "7509", "7700", "7797", "7829", "7937"],
804
+ //* Only 77003/77007/77008 belong to JE - the rest of 7700 is GB (e.g. 77001).
805
+ ["1534", "7509", "77003", "77007", "77008", "7797", "7829", "7937"],
804
806
  "0"
805
807
  ],
806
808
  [
@@ -995,7 +997,7 @@ var rawCountryData = [
995
997
  // Mayotte
996
998
  "262",
997
999
  1,
998
- ["269", "639"],
1000
+ ["2689", "269", "639", "7093"],
999
1001
  "0"
1000
1002
  ],
1001
1003
  [
@@ -1759,6 +1761,29 @@ var EVENTS = {
1759
1761
  // used for synthetic input trigger
1760
1762
  STRICT_REJECT: "strict:reject"
1761
1763
  };
1764
+ var ITI_SLOTS = [
1765
+ "container",
1766
+ "input",
1767
+ "countryContainer",
1768
+ "selectedCountry",
1769
+ "selectedCountryPrimary",
1770
+ "selectedFlag",
1771
+ "arrow",
1772
+ "selectedDialCode",
1773
+ "countrySelector",
1774
+ "countrySelectorContainer",
1775
+ "searchWrapper",
1776
+ "searchIcon",
1777
+ "searchInput",
1778
+ "searchClear",
1779
+ "countryList",
1780
+ "countryListItem",
1781
+ "countryListItemFlag",
1782
+ "countryName",
1783
+ "dialCode",
1784
+ "countryCheck",
1785
+ "noResults"
1786
+ ];
1762
1787
  var CLASSES = {
1763
1788
  HIDE: "iti__hide",
1764
1789
  V_HIDE: "iti__v-hide",
@@ -1935,6 +1960,8 @@ var defaults = {
1935
1960
  allowNumberExtensions: false,
1936
1961
  // Allow alphanumeric "phonewords" (e.g. +1 800 FLOWERS) as valid numbers
1937
1962
  allowPhonewords: false,
1963
+ //* Add custom classes to the elements we generate, keyed by slot name e.g. { selectedCountry: "rounded-l-lg" }.
1964
+ classNames: {},
1938
1965
  //* Add a custom class to the (injected) container element.
1939
1966
  containerClass: "",
1940
1967
  //* Locale for localising country names via Intl.DisplayNames.
@@ -1997,6 +2024,7 @@ var isElLike = (val) => {
1997
2024
  return v.nodeType === 1 && typeof v.tagName === "string" && typeof v.appendChild === "function";
1998
2025
  };
1999
2026
  var placeholderPolicySet = new Set(Object.values(PLACEHOLDER_POLICY));
2027
+ var slotSet = new Set(ITI_SLOTS);
2000
2028
  var warn = (message) => {
2001
2029
  console.warn(`[intl-tel-input] ${message}`);
2002
2030
  };
@@ -2097,6 +2125,26 @@ var validateOptions = (customOptions) => {
2097
2125
  }
2098
2126
  validatedOptions[key] = value;
2099
2127
  break;
2128
+ case "classNames": {
2129
+ if (!isPlainObject(value)) {
2130
+ warnOption("classNames", "an object", value);
2131
+ break;
2132
+ }
2133
+ const validSlots = {};
2134
+ for (const [slot, slotValue] of Object.entries(value)) {
2135
+ if (!slotSet.has(slot)) {
2136
+ warn(
2137
+ `Unknown slot '${slot}' in 'classNames'. Valid slots: ${ITI_SLOTS.join(", ")}. Skipping.`
2138
+ );
2139
+ } else if (typeof slotValue !== "string") {
2140
+ warnOption(`classNames.${slot}`, "a string", slotValue);
2141
+ } else {
2142
+ validSlots[slot] = slotValue.trim().replace(/\s+/g, " ");
2143
+ }
2144
+ }
2145
+ validatedOptions[key] = validSlots;
2146
+ break;
2147
+ }
2100
2148
  case "countryOrder": {
2101
2149
  if (value === null) {
2102
2150
  validatedOptions[key] = value;
@@ -2509,11 +2557,18 @@ var UI = class {
2509
2557
  );
2510
2558
  }
2511
2559
  }
2560
+ //* Append any consumer-supplied classes (via the classNames option) for the given slot to our own classes for that element.
2561
+ #withSlotClass(slot, ourClasses) {
2562
+ const custom = this.#options.classNames[slot];
2563
+ return custom ? `${ourClasses} ${custom}` : ourClasses;
2564
+ }
2512
2565
  //* Generate all of the markup for the core library: the selected country overlay, and the country selector.
2513
2566
  buildMarkup(countries, searchTokens) {
2514
2567
  this.#countries = countries;
2515
2568
  this.#searchTokens = searchTokens;
2516
- this.telInputEl.classList.add("iti__tel-input");
2569
+ this.telInputEl.classList.add(
2570
+ ...this.#withSlotClass("input", "iti__tel-input").split(" ")
2571
+ );
2517
2572
  if (!this.telInputEl.hasAttribute("type")) {
2518
2573
  this.telInputEl.setAttribute("type", "tel");
2519
2574
  }
@@ -2541,7 +2596,9 @@ var UI = class {
2541
2596
  "iti--inline-country-selector": countrySelectorMode !== COUNTRY_SELECTOR_MODE.FULLSCREEN,
2542
2597
  [containerClass]: Boolean(containerClass)
2543
2598
  });
2544
- const wrapper = createEl("div", { class: parentClasses });
2599
+ const wrapper = createEl("div", {
2600
+ class: this.#withSlotClass("container", parentClasses)
2601
+ });
2545
2602
  if (this.#isRTL) {
2546
2603
  wrapper.setAttribute("dir", "ltr");
2547
2604
  }
@@ -2557,7 +2614,12 @@ var UI = class {
2557
2614
  this.#countryContainerEl = createEl(
2558
2615
  "div",
2559
2616
  // visibly hidden until we measure its width to set the input padding correctly
2560
- { class: `iti__country-container ${CLASSES.V_HIDE}` },
2617
+ {
2618
+ class: this.#withSlotClass(
2619
+ "countryContainer",
2620
+ `iti__country-container ${CLASSES.V_HIDE}`
2621
+ )
2622
+ },
2561
2623
  wrapper
2562
2624
  );
2563
2625
  if (enableCountrySelector) {
@@ -2565,7 +2627,7 @@ var UI = class {
2565
2627
  "button",
2566
2628
  {
2567
2629
  type: "button",
2568
- class: "iti__selected-country",
2630
+ class: this.#withSlotClass("selectedCountry", "iti__selected-country"),
2569
2631
  [ARIA.EXPANDED]: "false",
2570
2632
  [ARIA.LABEL]: this.#options.uiTranslations.noCountrySelected,
2571
2633
  [ARIA.HASPOPUP]: "dialog",
@@ -2579,31 +2641,44 @@ var UI = class {
2579
2641
  } else {
2580
2642
  this.#selectedCountryEl = createEl(
2581
2643
  "div",
2582
- { class: "iti__selected-country" },
2644
+ { class: this.#withSlotClass("selectedCountry", "iti__selected-country") },
2583
2645
  this.#countryContainerEl
2584
2646
  );
2585
2647
  }
2586
2648
  const selectedCountryPrimary = createEl(
2587
2649
  "div",
2588
- { class: "iti__selected-country-primary" },
2650
+ {
2651
+ class: this.#withSlotClass(
2652
+ "selectedCountryPrimary",
2653
+ "iti__selected-country-primary"
2654
+ )
2655
+ },
2589
2656
  this.#selectedCountryEl
2590
2657
  );
2591
2658
  this.#selectedFlagEl = createEl(
2592
2659
  "div",
2593
- { class: CLASSES.FLAG },
2660
+ { class: this.#withSlotClass("selectedFlag", CLASSES.FLAG) },
2594
2661
  selectedCountryPrimary
2595
2662
  );
2596
2663
  if (enableCountrySelector) {
2597
2664
  this.#arrowEl = createEl(
2598
2665
  "div",
2599
- { class: "iti__arrow", [ARIA.HIDDEN]: "true" },
2666
+ {
2667
+ class: this.#withSlotClass("arrow", "iti__arrow"),
2668
+ [ARIA.HIDDEN]: "true"
2669
+ },
2600
2670
  selectedCountryPrimary
2601
2671
  );
2602
2672
  }
2603
2673
  if (separateDialCode) {
2604
2674
  this.#selectedDialCodeEl = createEl(
2605
2675
  "div",
2606
- { class: "iti__selected-dial-code" },
2676
+ {
2677
+ class: this.#withSlotClass(
2678
+ "selectedDialCode",
2679
+ "iti__selected-dial-code"
2680
+ )
2681
+ },
2607
2682
  this.#selectedCountryEl
2608
2683
  );
2609
2684
  }
@@ -2634,7 +2709,10 @@ var UI = class {
2634
2709
  const extraClasses = matchDropdownWidth ? "" : "iti--flexible-dropdown-width";
2635
2710
  this.#countrySelectorEl = createEl("div", {
2636
2711
  id: `iti-${this.#id}__country-selector`,
2637
- class: `iti__country-selector ${CLASSES.HIDE} ${extraClasses}`,
2712
+ class: this.#withSlotClass(
2713
+ "countrySelector",
2714
+ `iti__country-selector ${CLASSES.HIDE} ${extraClasses}`
2715
+ ),
2638
2716
  role: "dialog",
2639
2717
  [ARIA.MODAL]: "true"
2640
2718
  });
@@ -2647,7 +2725,7 @@ var UI = class {
2647
2725
  this.#countryListEl = createEl(
2648
2726
  "ul",
2649
2727
  {
2650
- class: "iti__country-list",
2728
+ class: this.#withSlotClass("countryList", "iti__country-list"),
2651
2729
  id: `iti-${this.#id}__country-listbox`,
2652
2730
  role: "listbox",
2653
2731
  [ARIA.LABEL]: uiTranslations.countryListAriaLabel
@@ -2666,7 +2744,9 @@ var UI = class {
2666
2744
  "iti--inline-country-selector": !isFullscreen,
2667
2745
  [containerClass]: Boolean(containerClass)
2668
2746
  });
2669
- this.#detachedCountrySelectorEl = createEl("div", { class: wrapperClasses });
2747
+ this.#detachedCountrySelectorEl = createEl("div", {
2748
+ class: this.#withSlotClass("countrySelectorContainer", wrapperClasses)
2749
+ });
2670
2750
  this.#detachedCountrySelectorEl.appendChild(this.#countrySelectorEl);
2671
2751
  } else {
2672
2752
  this.#countryContainerEl.appendChild(this.#countrySelectorEl);
@@ -2687,13 +2767,13 @@ var UI = class {
2687
2767
  const { uiTranslations, searchInputClass } = this.#options;
2688
2768
  const searchWrapper = createEl(
2689
2769
  "div",
2690
- { class: "iti__search-input-wrapper" },
2770
+ { class: this.#withSlotClass("searchWrapper", "iti__search-input-wrapper") },
2691
2771
  this.#countrySelectorEl
2692
2772
  );
2693
2773
  this.#searchIconEl = createEl(
2694
2774
  "span",
2695
2775
  {
2696
- class: "iti__search-icon",
2776
+ class: this.#withSlotClass("searchIcon", "iti__search-icon"),
2697
2777
  [ARIA.HIDDEN]: "true"
2698
2778
  },
2699
2779
  searchWrapper
@@ -2705,7 +2785,10 @@ var UI = class {
2705
2785
  id: `iti-${this.#id}__search-input`,
2706
2786
  // Chrome says inputs need either a name or an id
2707
2787
  type: "search",
2708
- class: `iti__search-input ${searchInputClass}`,
2788
+ class: this.#withSlotClass(
2789
+ "searchInput",
2790
+ `iti__search-input ${searchInputClass}`
2791
+ ),
2709
2792
  placeholder: uiTranslations.searchPlaceholder,
2710
2793
  // role=combobox + aria-autocomplete=list + aria-activedescendant allows maintaining focus on the search input while allowing users to navigate search results with up/down keyboard keys
2711
2794
  role: "combobox",
@@ -2721,7 +2804,10 @@ var UI = class {
2721
2804
  "button",
2722
2805
  {
2723
2806
  type: "button",
2724
- class: `iti__search-clear ${CLASSES.HIDE}`,
2807
+ class: this.#withSlotClass(
2808
+ "searchClear",
2809
+ `iti__search-clear ${CLASSES.HIDE}`
2810
+ ),
2725
2811
  [ARIA.LABEL]: uiTranslations.clearSearchAriaLabel,
2726
2812
  tabindex: "-1"
2727
2813
  },
@@ -2736,7 +2822,7 @@ var UI = class {
2736
2822
  this.#noResultsMessageEl = createEl(
2737
2823
  "div",
2738
2824
  {
2739
- class: `iti__no-results ${CLASSES.HIDE}`,
2825
+ class: this.#withSlotClass("noResults", `iti__no-results ${CLASSES.HIDE}`),
2740
2826
  [ARIA.HIDDEN]: "true"
2741
2827
  // all a11y messaging happens in this.#searchResultsLiveRegionEl
2742
2828
  },
@@ -2790,11 +2876,9 @@ var UI = class {
2790
2876
  //* For each country: add a country list item <li> to the countryList <ul> container.
2791
2877
  #appendListItems() {
2792
2878
  const frag = document.createDocumentFragment();
2879
+ const liClass = this.#withSlotClass("countryListItem", CLASSES.COUNTRY_ITEM);
2793
2880
  for (let i = 0; i < this.#countries.length; i++) {
2794
2881
  const c = this.#countries[i];
2795
- const liClass = buildClassNames({
2796
- [CLASSES.COUNTRY_ITEM]: true
2797
- });
2798
2882
  const listItem = createEl("li", {
2799
2883
  id: `iti-${this.#id}__item-${c.iso2}`,
2800
2884
  class: liClass,
@@ -2806,11 +2890,28 @@ var UI = class {
2806
2890
  listItem.dataset[DATA_KEYS.ISO2] = c.iso2;
2807
2891
  this.#listItemByIso2.set(c.iso2, listItem);
2808
2892
  if (this.#options.showFlags) {
2809
- createEl("div", { class: `${CLASSES.FLAG} iti__${c.iso2}` }, listItem);
2893
+ createEl(
2894
+ "div",
2895
+ {
2896
+ class: this.#withSlotClass(
2897
+ "countryListItemFlag",
2898
+ `${CLASSES.FLAG} iti__${c.iso2}`
2899
+ )
2900
+ },
2901
+ listItem
2902
+ );
2810
2903
  }
2811
- const nameEl = createEl("span", { class: "iti__country-name" }, listItem);
2904
+ const nameEl = createEl(
2905
+ "span",
2906
+ { class: this.#withSlotClass("countryName", "iti__country-name") },
2907
+ listItem
2908
+ );
2812
2909
  nameEl.textContent = `${c.name} `;
2813
- const dialEl = createEl("span", { class: "iti__dial-code" }, nameEl);
2910
+ const dialEl = createEl(
2911
+ "span",
2912
+ { class: this.#withSlotClass("dialCode", "iti__dial-code") },
2913
+ nameEl
2914
+ );
2814
2915
  if (this.#isRTL) {
2815
2916
  dialEl.setAttribute("dir", "ltr");
2816
2917
  }
@@ -3257,7 +3358,10 @@ var UI = class {
3257
3358
  newListItem.setAttribute(ARIA.SELECTED, "true");
3258
3359
  const checkIcon = createEl(
3259
3360
  "span",
3260
- { class: "iti__country-check", [ARIA.HIDDEN]: "true" },
3361
+ {
3362
+ class: this.#withSlotClass("countryCheck", "iti__country-check"),
3363
+ [ARIA.HIDDEN]: "true"
3364
+ },
3261
3365
  newListItem
3262
3366
  );
3263
3367
  checkIcon.appendChild(buildCheckIcon());
@@ -3444,7 +3548,10 @@ var UI = class {
3444
3548
  this.#updateSelectedListItem(iso2);
3445
3549
  }
3446
3550
  if (this.#selectedCountryEl) {
3447
- const flagClass = iso2 && showFlags ? `${CLASSES.FLAG} iti__${iso2}` : `${CLASSES.FLAG} ${CLASSES.GLOBE}`;
3551
+ const flagClass = this.#withSlotClass(
3552
+ "selectedFlag",
3553
+ iso2 && showFlags ? `${CLASSES.FLAG} iti__${iso2}` : `${CLASSES.FLAG} ${CLASSES.GLOBE}`
3554
+ );
3448
3555
  let ariaLabel, title;
3449
3556
  let flagContent = null;
3450
3557
  if (iso2) {
@@ -4841,7 +4948,7 @@ var intlTelInput = Object.assign(
4841
4948
  attachUtils,
4842
4949
  startedLoadingUtils: false,
4843
4950
  startedLoadingAutoCountry: false,
4844
- version: "29.1.2",
4951
+ version: "29.2.0",
4845
4952
  NUMBER_FORMAT,
4846
4953
  NUMBER_TYPE,
4847
4954
  VALIDATION_ERROR,
@@ -4872,6 +4979,7 @@ var IntlTelInput = class _IntlTelInput {
4872
4979
  allowedNumberTypes;
4873
4980
  allowNumberExtensions;
4874
4981
  allowPhonewords;
4982
+ classNames;
4875
4983
  containerClass;
4876
4984
  countryNameLocale;
4877
4985
  countryNameOverrides;
@@ -4922,6 +5030,10 @@ var IntlTelInput = class _IntlTelInput {
4922
5030
  pendingWriteValue;
4923
5031
  // if an input event fires before utils has loaded, we defer the update until the promise resolves
4924
5032
  pendingUpdate = false;
5033
+ //* setNumber dispatches a synthetic input event, which handleInput would otherwise treat as a
5034
+ //* user edit and report via onChange - and Angular marks a control dirty on any onChange call.
5035
+ //* Set while we write a value that came FROM the form model, so we can skip that callback.
5036
+ isWritingModelValue = false;
4925
5037
  // eslint-disable-next-line class-methods-use-this
4926
5038
  onChange = () => {
4927
5039
  };
@@ -4937,8 +5049,9 @@ var IntlTelInput = class _IntlTelInput {
4937
5049
  this.strictReject.emit(e.detail);
4938
5050
  };
4939
5051
  ngAfterViewInit() {
5052
+ const classesBeforeInit = new Set(this.inputRef.nativeElement.classList);
4940
5053
  this.iti = intlTelInput_default(this.inputRef.nativeElement, this.buildInitOptions());
4941
- this.libraryInputClasses = this.inputRef.nativeElement.className;
5054
+ this.libraryInputClasses = Array.from(this.inputRef.nativeElement.classList).filter((className) => !classesBeforeInit.has(className)).join(" ");
4942
5055
  this.inputRef.nativeElement.addEventListener("input", this.handleInput);
4943
5056
  this.inputRef.nativeElement.addEventListener("open:countryselector", this.handleOpenCountrySelector);
4944
5057
  this.inputRef.nativeElement.addEventListener("close:countryselector", this.handleCloseCountrySelector);
@@ -4955,10 +5068,10 @@ var IntlTelInput = class _IntlTelInput {
4955
5068
  return;
4956
5069
  }
4957
5070
  if (this.pendingWriteValue !== void 0) {
4958
- this.iti.setNumber(this.pendingWriteValue);
5071
+ this.setNumberFromModel(this.pendingWriteValue);
4959
5072
  this.pendingWriteValue = void 0;
4960
5073
  } else if (this.initialValue) {
4961
- this.iti.setNumber(this.initialValue);
5074
+ this.setNumberFromModel(this.initialValue);
4962
5075
  }
4963
5076
  if (this.pendingUpdate) {
4964
5077
  this.pendingUpdate = false;
@@ -4974,6 +5087,7 @@ var IntlTelInput = class _IntlTelInput {
4974
5087
  allowedNumberTypes: this.allowedNumberTypes,
4975
5088
  allowNumberExtensions: this.allowNumberExtensions,
4976
5089
  allowPhonewords: this.allowPhonewords,
5090
+ classNames: this.classNames,
4977
5091
  containerClass: this.containerClass,
4978
5092
  countryNameLocale: this.countryNameLocale,
4979
5093
  countryNameOverrides: this.countryNameOverrides,
@@ -5013,6 +5127,16 @@ var IntlTelInput = class _IntlTelInput {
5013
5127
  this.applyInputAttrs();
5014
5128
  }
5015
5129
  }
5130
+ //* Write a value that came from the form model (writeValue/initialValue), without reporting it
5131
+ //* back to the model as a user change.
5132
+ setNumberFromModel(value) {
5133
+ this.isWritingModelValue = true;
5134
+ try {
5135
+ this.iti?.setNumber(value);
5136
+ } finally {
5137
+ this.isWritingModelValue = false;
5138
+ }
5139
+ }
5016
5140
  handleInput = () => {
5017
5141
  if (!this.iti) {
5018
5142
  return;
@@ -5027,7 +5151,9 @@ var IntlTelInput = class _IntlTelInput {
5027
5151
  if (num !== this.lastEmittedNumber) {
5028
5152
  this.lastEmittedNumber = num;
5029
5153
  this.numberChange.emit(num);
5030
- this.onChange(num);
5154
+ if (!this.isWritingModelValue) {
5155
+ this.onChange(num);
5156
+ }
5031
5157
  hasChanged = true;
5032
5158
  }
5033
5159
  if (countryIso !== this.lastEmittedCountry) {
@@ -5121,7 +5247,7 @@ var IntlTelInput = class _IntlTelInput {
5121
5247
  if (this.iti) {
5122
5248
  this.iti.promise.then(() => {
5123
5249
  if (this.iti?.isActive()) {
5124
- this.iti.setNumber(next);
5250
+ this.setNumberFromModel(next);
5125
5251
  }
5126
5252
  });
5127
5253
  } else {
@@ -5166,7 +5292,7 @@ var IntlTelInput = class _IntlTelInput {
5166
5292
  let _t;
5167
5293
  i0.\u0275\u0275queryRefresh(_t = i0.\u0275\u0275loadQuery()) && (ctx.inputRef = _t.first);
5168
5294
  }
5169
- }, inputs: { initialValue: "initialValue", usePreciseValidation: "usePreciseValidation", inputAttributes: "inputAttributes", disabled: "disabled", readonly: "readonly", countrySelectorMode: "countrySelectorMode", allowedNumberTypes: "allowedNumberTypes", allowNumberExtensions: "allowNumberExtensions", allowPhonewords: "allowPhonewords", containerClass: "containerClass", countryNameLocale: "countryNameLocale", countryNameOverrides: "countryNameOverrides", countryOrder: "countryOrder", countrySearch: "countrySearch", customPlaceholder: "customPlaceholder", dropdownAlwaysOpen: "dropdownAlwaysOpen", dropdownParent: "dropdownParent", excludeCountries: "excludeCountries", matchDropdownWidth: "matchDropdownWidth", formatAsYouType: "formatAsYouType", hiddenInputs: "hiddenInputs", uiTranslations: "uiTranslations", initialCountry: "initialCountry", initialCountryLookup: "initialCountryLookup", loadUtils: "loadUtils", numberDisplayFormat: "numberDisplayFormat", onlyCountries: "onlyCountries", placeholderNumberPolicy: "placeholderNumberPolicy", placeholderNumberType: "placeholderNumberType", searchInputClass: "searchInputClass", separateDialCode: "separateDialCode", showFlags: "showFlags", strictMode: "strictMode", strictRejectAnimation: "strictRejectAnimation" }, outputs: { numberChange: "numberChange", countryChange: "countryChange", validityChange: "validityChange", errorCodeChange: "errorCodeChange", openCountrySelector: "openCountrySelector", closeCountrySelector: "closeCountrySelector", strictReject: "strictReject", blur: "blur", focus: "focus", keydown: "keydown", keyup: "keyup", paste: "paste", click: "click" }, features: [i0.\u0275\u0275ProvidersFeature([
5295
+ }, inputs: { initialValue: "initialValue", usePreciseValidation: "usePreciseValidation", inputAttributes: "inputAttributes", disabled: "disabled", readonly: "readonly", countrySelectorMode: "countrySelectorMode", allowedNumberTypes: "allowedNumberTypes", allowNumberExtensions: "allowNumberExtensions", allowPhonewords: "allowPhonewords", classNames: "classNames", containerClass: "containerClass", countryNameLocale: "countryNameLocale", countryNameOverrides: "countryNameOverrides", countryOrder: "countryOrder", countrySearch: "countrySearch", customPlaceholder: "customPlaceholder", dropdownAlwaysOpen: "dropdownAlwaysOpen", dropdownParent: "dropdownParent", excludeCountries: "excludeCountries", matchDropdownWidth: "matchDropdownWidth", formatAsYouType: "formatAsYouType", hiddenInputs: "hiddenInputs", uiTranslations: "uiTranslations", initialCountry: "initialCountry", initialCountryLookup: "initialCountryLookup", loadUtils: "loadUtils", numberDisplayFormat: "numberDisplayFormat", onlyCountries: "onlyCountries", placeholderNumberPolicy: "placeholderNumberPolicy", placeholderNumberType: "placeholderNumberType", searchInputClass: "searchInputClass", separateDialCode: "separateDialCode", showFlags: "showFlags", strictMode: "strictMode", strictRejectAnimation: "strictRejectAnimation" }, outputs: { numberChange: "numberChange", countryChange: "countryChange", validityChange: "validityChange", errorCodeChange: "errorCodeChange", openCountrySelector: "openCountrySelector", closeCountrySelector: "closeCountrySelector", strictReject: "strictReject", blur: "blur", focus: "focus", keydown: "keydown", keyup: "keyup", paste: "paste", click: "click" }, features: [i0.\u0275\u0275ProvidersFeature([
5170
5296
  {
5171
5297
  provide: NG_VALUE_ACCESSOR,
5172
5298
  useExisting: forwardRef(() => _IntlTelInput),
@@ -5249,6 +5375,8 @@ var IntlTelInput = class _IntlTelInput {
5249
5375
  type: Input
5250
5376
  }], allowPhonewords: [{
5251
5377
  type: Input
5378
+ }], classNames: [{
5379
+ type: Input
5252
5380
  }], containerClass: [{
5253
5381
  type: Input
5254
5382
  }], countryNameLocale: [{