@sb1/indeks-web 0.17.0 → 0.18.1

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/dist/npm/index.js CHANGED
@@ -287,7 +287,7 @@ var COLLATOR = new Intl.Collator("no", {
287
287
  sensitivity: "base",
288
288
  usage: "search"
289
289
  });
290
- var IS_TOUCH = typeof navigator !== "undefined" && /iphone|ipad|ipod|android/i.test(navigator.userAgent);
290
+ var IS_TOUCH$1 = typeof navigator !== "undefined" && /iphone|ipad|ipod|android/i.test(navigator.userAgent);
291
291
  var IxCombobox = class extends HTMLElement {
292
292
  constructor(..._args) {
293
293
  super(..._args);
@@ -355,6 +355,9 @@ var IxCombobox = class extends HTMLElement {
355
355
  this._arrowHint = null;
356
356
  this._results = null;
357
357
  }
358
+ focus(options) {
359
+ this._input?.focus(options);
360
+ }
358
361
  attributeChangedCallback(name, _oldValue, newValue) {
359
362
  if (!this.isConnected) return;
360
363
  if (name === "disabled" || name === "readonly") this._syncControlState();
@@ -423,7 +426,7 @@ var IxCombobox = class extends HTMLElement {
423
426
  if (this._toggle) {
424
427
  this._toggle.type = "button";
425
428
  this._toggle.setAttribute("tabindex", "-1");
426
- if (IS_TOUCH) this._toggle.setAttribute("aria-hidden", "true");
429
+ if (IS_TOUCH$1) this._toggle.setAttribute("aria-hidden", "true");
427
430
  }
428
431
  if (this._noHits) {
429
432
  this._noHitsText = (this.getAttribute("data-no-hits-text") || this._noHits.textContent || "").trim();
@@ -757,6 +760,7 @@ var IxCombobox = class extends HTMLElement {
757
760
  }
758
761
  }
759
762
  _emitChange() {
763
+ this._select?.dispatchEvent(new Event("change", { bubbles: true }));
760
764
  this.dispatchEvent(new CustomEvent("change", { bubbles: true }));
761
765
  }
762
766
  _syncControlState() {
@@ -898,13 +902,18 @@ function compileTokens(pattern) {
898
902
  /**
899
903
  * Lager en formatter fra en pattern-streng.
900
904
  *
901
- * `format` fyller slots med matchende tegn fra den rå verdien og setter inn
902
- * separatorer først når neste slot faktisk fylles (ingen dinglende separator på
903
- * slutten mens man skriver). Tegn som ikke passer neste slot — enten fordi
904
- * brukeren skrev flere tegn enn patternet har plasser, eller skrev noe som ikke
905
- * hører hjemme (bokstav i et sifferfelt) — droppes IKKE: den ledende delen som
906
- * passer formateres, og resten legges uformatert på til slutt i samme
907
- * rekkefølge brukeren skrev. Alt brukeren skriver forblir synlig.
905
+ * `format` fyller slots med matchende tegn fra den rå verdien og setter inn en
906
+ * separator så snart gruppen foran er fylt OG resten av verdien er konsumert —
907
+ * separatoren dukker altså opp med én gang gruppen er komplett ("24" → "24.",
908
+ * "2412" → "24.12."), ikke først når neste siffer skrives. En separator legges
909
+ * aldri til for en halvfylt gruppe, og heller ikke når vi stoppet fordi neste
910
+ * tegn ikke passet sloten (da ville vi doblet separatoren ved innliming av
911
+ * allerede formatert tekst). Tegn som ikke passer neste slot — enten fordi
912
+ * brukeren skrev flere tegn enn
913
+ * patternet har plasser, eller skrev noe som ikke hører hjemme (bokstav i et
914
+ * sifferfelt) — droppes IKKE: den ledende delen som passer formateres, og resten
915
+ * legges uformatert på til slutt i samme rekkefølge brukeren skrev. Alt brukeren
916
+ * skriver forblir synlig.
908
917
  *
909
918
  * `parse` er tapsfri: den fjerner kun separator-tegnene patternet definerer
910
919
  * (literal-tokens) og beholder alt annet i rekkefølge — ingen cap på antall
@@ -935,11 +944,74 @@ function createPatternFormatter(pattern) {
935
944
  pendingLiterals = "";
936
945
  i++;
937
946
  }
938
- return out + chars.slice(i).join("");
947
+ const trailing = i >= chars.length ? pendingLiterals : "";
948
+ return out + trailing + chars.slice(i).join("");
939
949
  }
940
950
  };
941
951
  }
942
952
  /**
953
+ * Kanonisk dato-parser — den ENE sannheten for hvordan en dato-streng tolkes.
954
+ * Både ix-field sin synlige formatering og IxDateField sin ISO-utleding går
955
+ * gjennom denne, så de aldri kan drifte fra hverandre.
956
+ *
957
+ * Komplett dato → nullutfylte deler; ufullstendig/tvetydig → `null`.
958
+ *
959
+ * - Med punktum: nøyaktig 3 rent-numeriske segmenter, dag/måned 1-2 sifre,
960
+ * år 2 eller 4 sifre. Dag/måned nullutfylles; 2-sifret år utvides til `20xx`
961
+ * (`"26"` → `"2026"`). `"1.1.2026"` og `"1.1.26"` → begge 01/01/2026.
962
+ * - Uten punktum: nøyaktig 8 sifre tolkes som ddmmåååå (`"01012026"`). Annen
963
+ * lengde uten skilletegn (f.eks. `"112026"`) er tvetydig — vi kan ikke vite
964
+ * hvor grensene går uten punktum — og regnes ufullstendig → `null`.
965
+ * - Bokstaver, ekstra punktum eller feil segmentantall → `null`.
966
+ */
967
+ function parseDate(display) {
968
+ const trimmed = display.trim();
969
+ if (trimmed.includes(".")) {
970
+ const segments = trimmed.split(".");
971
+ if (segments.length !== 3) return null;
972
+ const [day, month, year] = segments;
973
+ if (!/^\d{1,2}$/.test(day) || !/^\d{1,2}$/.test(month) || !/^(\d{2}|\d{4})$/.test(year)) return null;
974
+ const fullYear = year.length === 2 ? `20${year}` : year;
975
+ return {
976
+ day: day.padStart(2, "0"),
977
+ month: month.padStart(2, "0"),
978
+ year: fullYear
979
+ };
980
+ }
981
+ if (/^\d{8}$/.test(trimmed)) return {
982
+ day: trimmed.slice(0, 2),
983
+ month: trimmed.slice(2, 4),
984
+ year: trimmed.slice(4, 8)
985
+ };
986
+ return null;
987
+ }
988
+ /** Komplett dato → visning `dd.mm.åååå` (nullutfylt); ellers strengen verbatim. */
989
+ function normalizeDateDisplay(display) {
990
+ const parts = parseDate(display);
991
+ return parts ? `${parts.day}.${parts.month}.${parts.year}` : display;
992
+ }
993
+ /** Komplett dato → ISO `åååå-mm-dd`; ellers tom streng. */
994
+ function isoFromDate(display) {
995
+ const parts = parseDate(display);
996
+ return parts ? `${parts.year}-${parts.month}-${parts.day}` : "";
997
+ }
998
+ /**
999
+ * Formatter for datofelt. `parse` er identitet — punktumet er en meningsfull
1000
+ * tastet grense, ikke en separator å fjerne (trivielt tapsfri). `format`
1001
+ * nullutfyller til `dd.mm.åååå` når datoen er komplett, ellers vises alt verbatim.
1002
+ *
1003
+ * Invariant-unntak: `parse(format(raw)) === raw` gjelder IKKE her, fordi
1004
+ * nullutfylling (`"1"` → `"01"`) ikke er reversibel. Blur-syklusen krever i
1005
+ * stedet at `format` er idempotent på allerede-normaliserte verdier —
1006
+ * `format("01.01.2026") === "01.01.2026"` — som holder.
1007
+ */
1008
+ function createDateFormatter() {
1009
+ return {
1010
+ parse: (display) => display,
1011
+ format: (raw) => normalizeDateDisplay(raw)
1012
+ };
1013
+ }
1014
+ /**
943
1015
  * Beløp er ikke en fast-lengde pattern-maske: lengden varierer og tusenskillet
944
1016
  * forskyver seg mens man skriver. Derfor en egen formatter.
945
1017
  *
@@ -1001,6 +1073,19 @@ function registerFormat(name, formatter) {
1001
1073
  function resolveFormat(name) {
1002
1074
  return registry.get(name);
1003
1075
  }
1076
+ /**
1077
+ * Navnene på de innebygde formatvariantene. Eksporteres slik at typelaget
1078
+ * (indeks-react dupliserer denne listen for autocomplete på `format`) kan holdes
1079
+ * i synk via en sync-test — React importerer ikke web-runtime ellers.
1080
+ */
1081
+ var BUILTIN_FORMAT_NAMES = [
1082
+ "phone",
1083
+ "amount",
1084
+ "account",
1085
+ "orgnr",
1086
+ "ssn",
1087
+ "date"
1088
+ ];
1004
1089
  registerFormat("phone", {
1005
1090
  ...createPatternFormatter("000 00 000"),
1006
1091
  live: true
@@ -1009,10 +1094,7 @@ registerFormat("ssn", {
1009
1094
  ...createPatternFormatter("000000 00000"),
1010
1095
  live: true
1011
1096
  });
1012
- registerFormat("date", {
1013
- ...createPatternFormatter("00.00.0000"),
1014
- live: true
1015
- });
1097
+ registerFormat("date", createDateFormatter());
1016
1098
  registerFormat("account", {
1017
1099
  ...createPatternFormatter("0000 00 00000"),
1018
1100
  live: true
@@ -1029,6 +1111,216 @@ registerFormat("amount", {
1029
1111
  live: true
1030
1112
  });
1031
1113
  //#endregion
1114
+ //#region lib/components/date-field/IxDateField.ts
1115
+ /**
1116
+ * IxDateField — datofelt: formatert tekst-input (dd.mm.åååå) + native OS-kalender.
1117
+ *
1118
+ * ## Designfilosofi
1119
+ *
1120
+ * Hybrid av to mønstre. `ix-date-field` ligger NØSTET inne i `ix-field` (som
1121
+ * `ix-combobox`), og `ix-field` gjør ARIA-limet: kobler label↔input,
1122
+ * aria-describedby, feilmelding, OG formateringen (`data-format="date"` →
1123
+ * normaliserer til dd.mm.åååå på blur; brukeren kan taste `1.1.2026` like gjerne
1124
+ * som `01.01.2026`). `ix-date-field` dupliserer ingenting av dette. Dens ENESTE
1125
+ * jobb er å gjøre feltet til et datofelt:
1126
+ *
1127
+ * 1. GENERERE to instrumentelle elementer inn i `.ix-text-field` (idempotent):
1128
+ * - `.ix-date-field__toggle` kalenderknapp (CSS gir ikonet)
1129
+ * - `.ix-date-field__native` overlagt gjennomsiktig `<input type="date">`
1130
+ * som bærer ISO-verdien, gir min/max-validering og åpner OS-kalenderen
1131
+ * 2. To-veis synk mellom den synlige dd.mm.åååå-inputen og den native ISO-inputen.
1132
+ * 3. Speile min/max/disabled/readonly ned til den native inputen.
1133
+ * 4. Knapp-klikk → `native.showPicker?.()`; tap/klikk treffer uansett den
1134
+ * gjennomsiktige native-inputen som ligger over knappen (mobil).
1135
+ *
1136
+ * ## Verdi-kontrakt
1137
+ *
1138
+ * Utad er verdien ISO (`åååå-mm-dd`): den native inputen bærer den, `change`
1139
+ * emitter den, og `name` settes på den native inputen så form-innsending gir ISO.
1140
+ * Den synlige inputen viser `dd.mm.åååå` og har bevisst INGEN `name` (ville
1141
+ * kollidert med ix-field sin formaterings-speiling ved innsending).
1142
+ *
1143
+ * ## Delelementer (light DOM)
1144
+ * - .ix-text-field > input → synlig dd.mm.åååå-input (forfatteren)
1145
+ * - .ix-date-field__toggle → kalenderknapp (WC genererer)
1146
+ * - .ix-date-field__native (input[date]) → skjult ISO-input (WC genererer)
1147
+ *
1148
+ * @example
1149
+ * <ix-field>
1150
+ * <label>Fødselsdato</label>
1151
+ * <span data-field="description">dd.mm.åååå</span>
1152
+ * <ix-date-field data-open-label="Åpne kalender" name="fodt">
1153
+ * <div class="ix-text-field">
1154
+ * <input inputmode="numeric" data-format="date" />
1155
+ * </div>
1156
+ * </ix-date-field>
1157
+ * <span data-field="error"></span>
1158
+ * </ix-field>
1159
+ */
1160
+ var IS_TOUCH = typeof navigator !== "undefined" && /iphone|ipad|ipod|android/i.test(navigator.userAgent);
1161
+ function digitsFromIso(iso) {
1162
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
1163
+ if (!match) return "";
1164
+ const [, year, month, day] = match;
1165
+ return `${day}${month}${year}`;
1166
+ }
1167
+ function displayFromDigits(digits) {
1168
+ return `${digits.slice(0, 2)}.${digits.slice(2, 4)}.${digits.slice(4, 8)}`;
1169
+ }
1170
+ var IxDateField = class extends HTMLElement {
1171
+ constructor(..._args) {
1172
+ super(..._args);
1173
+ _defineProperty(this, "_input", null);
1174
+ _defineProperty(this, "_toggle", null);
1175
+ _defineProperty(this, "_native", null);
1176
+ _defineProperty(this, "_syncing", false);
1177
+ _defineProperty(this, "_lastEmitted", "");
1178
+ _defineProperty(this, "_onVisibleInput", null);
1179
+ _defineProperty(this, "_onNativeInput", null);
1180
+ _defineProperty(this, "_onToggleClick", null);
1181
+ }
1182
+ static get observedAttributes() {
1183
+ return [
1184
+ "min",
1185
+ "max",
1186
+ "disabled",
1187
+ "readonly",
1188
+ "value",
1189
+ "name",
1190
+ "data-open-label"
1191
+ ];
1192
+ }
1193
+ connectedCallback() {
1194
+ this._wire();
1195
+ }
1196
+ disconnectedCallback() {
1197
+ if (this._input && this._onVisibleInput) this._input.removeEventListener("input", this._onVisibleInput);
1198
+ if (this._native && this._onNativeInput) {
1199
+ this._native.removeEventListener("input", this._onNativeInput);
1200
+ this._native.removeEventListener("change", this._onNativeInput);
1201
+ }
1202
+ if (this._toggle && this._onToggleClick) this._toggle.removeEventListener("click", this._onToggleClick);
1203
+ this._onVisibleInput = null;
1204
+ this._onNativeInput = null;
1205
+ this._onToggleClick = null;
1206
+ this._input = null;
1207
+ this._toggle = null;
1208
+ this._native = null;
1209
+ }
1210
+ attributeChangedCallback(name, _oldValue, newValue) {
1211
+ if (!this.isConnected) return;
1212
+ if (name === "min" || name === "max" || name === "disabled" || name === "readonly") this._syncControlState();
1213
+ else if (name === "name") {
1214
+ if (this._native) this._native.name = newValue ?? "";
1215
+ } else if (name === "data-open-label") this._syncToggleLabel();
1216
+ else if (name === "value") this._seedFromIso(newValue ?? "");
1217
+ }
1218
+ _wire() {
1219
+ const field = this.querySelector(".ix-text-field");
1220
+ if (!field) return;
1221
+ this._input = field.querySelector("input:not(.ix-date-field__native)");
1222
+ if (!this._input) return;
1223
+ this._native = field.querySelector("input.ix-date-field__native");
1224
+ if (!this._native) {
1225
+ const native = document.createElement("input");
1226
+ native.type = "date";
1227
+ native.className = "ix-date-field__native";
1228
+ native.tabIndex = -1;
1229
+ native.setAttribute("aria-hidden", "true");
1230
+ field.appendChild(native);
1231
+ this._native = native;
1232
+ }
1233
+ this._toggle = field.querySelector("button.ix-date-field__toggle");
1234
+ if (!this._toggle) {
1235
+ const toggle = document.createElement("button");
1236
+ toggle.type = "button";
1237
+ toggle.className = "ix-date-field__toggle";
1238
+ field.insertBefore(toggle, this._native);
1239
+ this._toggle = toggle;
1240
+ }
1241
+ const hostName = this.getAttribute("name");
1242
+ const visibleName = this._input.getAttribute("name");
1243
+ const name = hostName ?? visibleName ?? "";
1244
+ if (name) this._native.name = name;
1245
+ if (visibleName) this._input.removeAttribute("name");
1246
+ this.toggleAttribute("data-touch", IS_TOUCH);
1247
+ this._syncToggleLabel();
1248
+ this._syncControlState();
1249
+ this._onVisibleInput = () => this._handleVisibleInput();
1250
+ this._input.addEventListener("input", this._onVisibleInput);
1251
+ this._onNativeInput = (e) => this._handleNativeInput(e);
1252
+ this._native.addEventListener("input", this._onNativeInput);
1253
+ this._native.addEventListener("change", this._onNativeInput);
1254
+ this._onToggleClick = () => this._openPicker();
1255
+ this._toggle.addEventListener("click", this._onToggleClick);
1256
+ const initialIso = this.getAttribute("value");
1257
+ if (initialIso) this._seedFromIso(initialIso);
1258
+ else if (this._input.value) {
1259
+ this._syncing = true;
1260
+ this._native.value = isoFromDate(this._input.value);
1261
+ this._syncing = false;
1262
+ }
1263
+ this._lastEmitted = this._native.value;
1264
+ }
1265
+ _handleVisibleInput() {
1266
+ if (this._syncing || !this._input || !this._native) return;
1267
+ this._syncing = true;
1268
+ this._native.value = isoFromDate(this._input.value);
1269
+ this._syncing = false;
1270
+ this._emitIfChanged();
1271
+ }
1272
+ _handleNativeInput(e) {
1273
+ e.stopPropagation();
1274
+ if (this._syncing || !this._input || !this._native) return;
1275
+ const digits = digitsFromIso(this._native.value);
1276
+ this._syncing = true;
1277
+ this._input.value = digits ? displayFromDigits(digits) : "";
1278
+ this._input.dispatchEvent(new Event("input", { bubbles: true }));
1279
+ this._syncing = false;
1280
+ this._emitIfChanged();
1281
+ }
1282
+ _emitIfChanged() {
1283
+ const iso = this._native?.value ?? "";
1284
+ if (iso === this._lastEmitted) return;
1285
+ this._lastEmitted = iso;
1286
+ this.dispatchEvent(new CustomEvent("change", { bubbles: true }));
1287
+ }
1288
+ _seedFromIso(iso) {
1289
+ if (!this._native || !this._input) return;
1290
+ this._syncing = true;
1291
+ this._native.value = iso;
1292
+ const digits = digitsFromIso(this._native.value);
1293
+ this._input.value = digits ? displayFromDigits(digits) : "";
1294
+ this._syncing = false;
1295
+ this._emitIfChanged();
1296
+ }
1297
+ _openPicker() {
1298
+ try {
1299
+ this._native?.showPicker();
1300
+ } catch {}
1301
+ }
1302
+ _syncControlState() {
1303
+ if (!this._native) return;
1304
+ this._native.min = this.getAttribute("min") ?? "";
1305
+ this._native.max = this.getAttribute("max") ?? "";
1306
+ this._native.disabled = this.hasAttribute("disabled");
1307
+ this._native.readOnly = this.hasAttribute("readonly");
1308
+ }
1309
+ _syncToggleLabel() {
1310
+ if (!this._toggle) return;
1311
+ const label = this.getAttribute("data-open-label");
1312
+ if (label) this._toggle.setAttribute("aria-label", label);
1313
+ else this._toggle.removeAttribute("aria-label");
1314
+ if (IS_TOUCH) {
1315
+ this._toggle.setAttribute("aria-hidden", "true");
1316
+ this._toggle.tabIndex = -1;
1317
+ } else {
1318
+ this._toggle.removeAttribute("aria-hidden");
1319
+ this._toggle.tabIndex = 0;
1320
+ }
1321
+ }
1322
+ };
1323
+ //#endregion
1032
1324
  //#region lib/components/field/IxField.ts
1033
1325
  /**
1034
1326
  * IxField — tilgjengelighetslim mellom label, input og hjelpetekster.
@@ -1336,7 +1628,7 @@ var IxField = class extends HTMLElement {
1336
1628
  listeners.push([type, fn]);
1337
1629
  };
1338
1630
  if (formatter.live) {
1339
- on("input", () => this._maskInPlace());
1631
+ on("input", (e) => this._maskInPlace(e));
1340
1632
  control.value = formatter.format(initialRaw);
1341
1633
  } else {
1342
1634
  on("focus", () => {
@@ -1360,7 +1652,7 @@ var IxField = class extends HTMLElement {
1360
1652
  this._activeControl = null;
1361
1653
  };
1362
1654
  }
1363
- _maskInPlace() {
1655
+ _maskInPlace(event) {
1364
1656
  const formatter = this._activeFormatter;
1365
1657
  const control = this._activeControl;
1366
1658
  if (!formatter || !control) return;
@@ -1375,6 +1667,7 @@ var IxField = class extends HTMLElement {
1375
1667
  newCaret = i;
1376
1668
  break;
1377
1669
  }
1670
+ if (!(event?.inputType?.startsWith("delete") ?? false)) while (newCaret < next.length && !formatter.parse(next[newCaret])) newCaret++;
1378
1671
  if (next !== display) control.value = next;
1379
1672
  control.setSelectionRange(newCaret, newCaret);
1380
1673
  if (this._formatMirror) this._formatMirror.value = raw;
@@ -1641,6 +1934,669 @@ var IxIcon = class extends HTMLElement {
1641
1934
  }
1642
1935
  };
1643
1936
  //#endregion
1937
+ //#region lib/components/phone-number-field/countries.ts
1938
+ var NORDIC = /* @__PURE__ */ new Set([
1939
+ "SE",
1940
+ "DK",
1941
+ "FI",
1942
+ "IS"
1943
+ ]);
1944
+ var COUNTRIES = [
1945
+ {
1946
+ iso: "NO",
1947
+ code: "47",
1948
+ name: {
1949
+ nb: "Norge",
1950
+ nn: "Noreg",
1951
+ en: "Norway"
1952
+ }
1953
+ },
1954
+ {
1955
+ iso: "SE",
1956
+ code: "46",
1957
+ name: {
1958
+ nb: "Sverige",
1959
+ nn: "Sverige",
1960
+ en: "Sweden"
1961
+ }
1962
+ },
1963
+ {
1964
+ iso: "DK",
1965
+ code: "45",
1966
+ name: {
1967
+ nb: "Danmark",
1968
+ nn: "Danmark",
1969
+ en: "Denmark"
1970
+ }
1971
+ },
1972
+ {
1973
+ iso: "FI",
1974
+ code: "358",
1975
+ name: {
1976
+ nb: "Finland",
1977
+ nn: "Finland",
1978
+ en: "Finland"
1979
+ }
1980
+ },
1981
+ {
1982
+ iso: "IS",
1983
+ code: "354",
1984
+ name: {
1985
+ nb: "Island",
1986
+ nn: "Island",
1987
+ en: "Iceland"
1988
+ }
1989
+ },
1990
+ {
1991
+ iso: "BE",
1992
+ code: "32",
1993
+ name: {
1994
+ nb: "Belgia",
1995
+ nn: "Belgia",
1996
+ en: "Belgium"
1997
+ }
1998
+ },
1999
+ {
2000
+ iso: "EE",
2001
+ code: "372",
2002
+ name: {
2003
+ nb: "Estland",
2004
+ nn: "Estland",
2005
+ en: "Estonia"
2006
+ }
2007
+ },
2008
+ {
2009
+ iso: "FR",
2010
+ code: "33",
2011
+ name: {
2012
+ nb: "Frankrike",
2013
+ nn: "Frankrike",
2014
+ en: "France"
2015
+ }
2016
+ },
2017
+ {
2018
+ iso: "GR",
2019
+ code: "30",
2020
+ name: {
2021
+ nb: "Hellas",
2022
+ nn: "Hellas",
2023
+ en: "Greece"
2024
+ }
2025
+ },
2026
+ {
2027
+ iso: "IN",
2028
+ code: "91",
2029
+ name: {
2030
+ nb: "India",
2031
+ nn: "India",
2032
+ en: "India"
2033
+ }
2034
+ },
2035
+ {
2036
+ iso: "IE",
2037
+ code: "353",
2038
+ name: {
2039
+ nb: "Irland",
2040
+ nn: "Irland",
2041
+ en: "Ireland"
2042
+ }
2043
+ },
2044
+ {
2045
+ iso: "IT",
2046
+ code: "39",
2047
+ name: {
2048
+ nb: "Italia",
2049
+ nn: "Italia",
2050
+ en: "Italy"
2051
+ }
2052
+ },
2053
+ {
2054
+ iso: "HR",
2055
+ code: "385",
2056
+ name: {
2057
+ nb: "Kroatia",
2058
+ nn: "Kroatia",
2059
+ en: "Croatia"
2060
+ }
2061
+ },
2062
+ {
2063
+ iso: "LV",
2064
+ code: "371",
2065
+ name: {
2066
+ nb: "Latvia",
2067
+ nn: "Latvia",
2068
+ en: "Latvia"
2069
+ }
2070
+ },
2071
+ {
2072
+ iso: "LT",
2073
+ code: "370",
2074
+ name: {
2075
+ nb: "Litauen",
2076
+ nn: "Litauen",
2077
+ en: "Lithuania"
2078
+ }
2079
+ },
2080
+ {
2081
+ iso: "NL",
2082
+ code: "31",
2083
+ name: {
2084
+ nb: "Nederland",
2085
+ nn: "Nederland",
2086
+ en: "Netherlands"
2087
+ }
2088
+ },
2089
+ {
2090
+ iso: "PL",
2091
+ code: "48",
2092
+ name: {
2093
+ nb: "Polen",
2094
+ nn: "Polen",
2095
+ en: "Poland"
2096
+ }
2097
+ },
2098
+ {
2099
+ iso: "PT",
2100
+ code: "351",
2101
+ name: {
2102
+ nb: "Portugal",
2103
+ nn: "Portugal",
2104
+ en: "Portugal"
2105
+ }
2106
+ },
2107
+ {
2108
+ iso: "RO",
2109
+ code: "40",
2110
+ name: {
2111
+ nb: "Romania",
2112
+ nn: "Romania",
2113
+ en: "Romania"
2114
+ }
2115
+ },
2116
+ {
2117
+ iso: "RU",
2118
+ code: "7",
2119
+ name: {
2120
+ nb: "Russland",
2121
+ nn: "Russland",
2122
+ en: "Russia"
2123
+ }
2124
+ },
2125
+ {
2126
+ iso: "ES",
2127
+ code: "34",
2128
+ name: {
2129
+ nb: "Spania",
2130
+ nn: "Spania",
2131
+ en: "Spain"
2132
+ }
2133
+ },
2134
+ {
2135
+ iso: "GB",
2136
+ code: "44",
2137
+ name: {
2138
+ nb: "Storbritannia",
2139
+ nn: "Storbritannia",
2140
+ en: "United Kingdom"
2141
+ }
2142
+ },
2143
+ {
2144
+ iso: "CH",
2145
+ code: "41",
2146
+ name: {
2147
+ nb: "Sveits",
2148
+ nn: "Sveits",
2149
+ en: "Switzerland"
2150
+ }
2151
+ },
2152
+ {
2153
+ iso: "DE",
2154
+ code: "49",
2155
+ name: {
2156
+ nb: "Tyskland",
2157
+ nn: "Tyskland",
2158
+ en: "Germany"
2159
+ }
2160
+ },
2161
+ {
2162
+ iso: "US",
2163
+ code: "1",
2164
+ name: {
2165
+ nb: "USA",
2166
+ nn: "USA",
2167
+ en: "United States"
2168
+ }
2169
+ },
2170
+ {
2171
+ iso: "AT",
2172
+ code: "43",
2173
+ name: {
2174
+ nb: "Østerrike",
2175
+ nn: "Austerrike",
2176
+ en: "Austria"
2177
+ }
2178
+ },
2179
+ {
2180
+ iso: "TR",
2181
+ code: "90",
2182
+ name: {
2183
+ nb: "Tyrkia",
2184
+ nn: "Tyrkia",
2185
+ en: "Turkey"
2186
+ }
2187
+ },
2188
+ {
2189
+ iso: "UA",
2190
+ code: "380",
2191
+ name: {
2192
+ nb: "Ukraina",
2193
+ nn: "Ukraina",
2194
+ en: "Ukraine"
2195
+ }
2196
+ },
2197
+ {
2198
+ iso: "CN",
2199
+ code: "86",
2200
+ name: {
2201
+ nb: "Kina",
2202
+ nn: "Kina",
2203
+ en: "China"
2204
+ }
2205
+ },
2206
+ {
2207
+ iso: "JP",
2208
+ code: "81",
2209
+ name: {
2210
+ nb: "Japan",
2211
+ nn: "Japan",
2212
+ en: "Japan"
2213
+ }
2214
+ },
2215
+ {
2216
+ iso: "BR",
2217
+ code: "55",
2218
+ name: {
2219
+ nb: "Brasil",
2220
+ nn: "Brasil",
2221
+ en: "Brazil"
2222
+ }
2223
+ },
2224
+ {
2225
+ iso: "AU",
2226
+ code: "61",
2227
+ name: {
2228
+ nb: "Australia",
2229
+ nn: "Australia",
2230
+ en: "Australia"
2231
+ }
2232
+ }
2233
+ ];
2234
+ /**
2235
+ * Bygger standard landliste for en gitt `locale`. Rekkefølge: Norge først,
2236
+ * deretter Norden (Sverige, Danmark, Finland, Island), deretter resten
2237
+ * alfabetisk etter lokalisert landnavn (locale-bevisst `Intl.Collator`, som
2238
+ * håndterer æ/ø/å).
2239
+ *
2240
+ * @default locale = 'nb'
2241
+ */
2242
+ function getDefaultCountries(locale = "nb") {
2243
+ const collator = new Intl.Collator(locale);
2244
+ const toOption = (c) => ({
2245
+ value: c.code,
2246
+ label: `+${c.code}`,
2247
+ description: c.name[locale]
2248
+ });
2249
+ const norway = COUNTRIES.filter((c) => c.iso === "NO");
2250
+ const nordics = COUNTRIES.filter((c) => NORDIC.has(c.iso)).sort((a, b) => collator.compare(a.name[locale], b.name[locale]));
2251
+ const rest = COUNTRIES.filter((c) => c.iso !== "NO" && !NORDIC.has(c.iso)).sort((a, b) => collator.compare(a.name[locale], b.name[locale]));
2252
+ return [
2253
+ ...norway,
2254
+ ...nordics,
2255
+ ...rest
2256
+ ].map(toOption);
2257
+ }
2258
+ /** Normaliserer en `data-locale`-attributt til en gyldig `CountryLocale`. */
2259
+ function normalizeLocale(value) {
2260
+ return value === "nn" || value === "en" ? value : "nb";
2261
+ }
2262
+ //#endregion
2263
+ //#region lib/components/phone-number-field/IxPhoneNumberField.ts
2264
+ /**
2265
+ * IxPhoneNumberField — telefonnummer med landvelger.
2266
+ *
2267
+ * ## Designfilosofi
2268
+ *
2269
+ * Komponenten er en sammensatt gruppe: den binder sammen en <ix-combobox>
2270
+ * (landkode) og et <ix-field>-tekstfelt (nummer) under én felles label og én
2271
+ * felles valideringsmelding. Den reimplementerer verken combobox- eller
2272
+ * formaterings-oppførsel — <ix-combobox> og <ix-field> gjør fortsatt sin egen
2273
+ * interne ARIA-kabling.
2274
+ *
2275
+ * HTML/WC er primærkilden: forfatteren skriver minimal markup (en TOM
2276
+ * <ix-combobox> holder), og komponenten fyller landlista, setter forhåndsvalg
2277
+ * og stamper nummer-feltets standardattributter. React er et tynt lag som kun
2278
+ * videresender config som data-*-attributter.
2279
+ *
2280
+ * Speiler <ix-checkbox-group>/<ix-radio-group>-mønsteret, med disse forskjellene:
2281
+ * de to «kontrollene» er en <ix-combobox> og et nummer-<input> (ikke en liste av
2282
+ * like radio/checkbox), og disabled/readonly/required propageres til begge disse.
2283
+ *
2284
+ * ## Hva komponenten gjør
2285
+ *
2286
+ * 1. Setter role="group" på host slik at skjermlesere annonserer gruppen og
2287
+ * leser legend-teksten som gruppens tilgjengelige navn. (Det finnes ingen
2288
+ * egen rolle for et sammensatt felt — "group" er korrekt.)
2289
+ *
2290
+ * 2. Kobler legend via aria-labelledby — genererer ID på [data-field="legend"]
2291
+ * om nødvendig og peker host til den.
2292
+ *
2293
+ * 3. Setter aria-describedby på host med IDer til description og error
2294
+ * (description alltid først). Hopper over elementer som mangler.
2295
+ *
2296
+ * 4. Setter aria-live="polite" på [data-field="error"] og observerer
2297
+ * textContent-endringer: ikke-tomt innhold → aria-invalid="true" på host;
2298
+ * tomt → aria-invalid fjernes. aria-invalid settes kun på host — det er
2299
+ * gruppens validitet som telles.
2300
+ *
2301
+ * 5. Propagerer disabled/readonly/required til begge kontrollene. Egne
2302
+ * (per-kontroll) verdier bevares via WeakMaps slik at group-toggle ikke
2303
+ * overstyrer dem.
2304
+ *
2305
+ * 6. Fyller landvelgeren: injiserer .ix-combobox__option-markup i den tomme
2306
+ * <ix-combobox>-lista fra en innebygd, lokaliserbar landliste (eller en
2307
+ * egendefinert `data-countries`-JSON), og setter aria-selected på
2308
+ * forhåndsvalgt land (data-country-code / data-default-country-code).
2309
+ *
2310
+ * 7. Stamper nummer-feltets standardattributter (type=tel, inputmode=numeric,
2311
+ * autocomplete=tel-national, data-format=phone) om de mangler.
2312
+ *
2313
+ * 8. Utleder data-state (error/readonly/disabled) for CSS.
2314
+ *
2315
+ * 9. Lytter på childList slik at kontroller som React rendrer inn etter mount
2316
+ * (conditional rendering) fanges opp og re-wires.
2317
+ *
2318
+ * ## Ingen Shadow DOM
2319
+ *
2320
+ * Komponenten bruker light DOM slik at CSS fra @sb1/indeks-css når alle
2321
+ * delelementer direkte, og aria-koblingene (aria-labelledby/-describedby) ikke
2322
+ * krysser en shadow-grense.
2323
+ *
2324
+ * @example
2325
+ * <ix-phone-number-field data-locale="nb" data-default-country-code="47">
2326
+ * <span data-field="legend">Mobilnummer</span>
2327
+ * <span data-field="description">Vi bruker det kun til SMS-varsling</span>
2328
+ * <div data-field="items">
2329
+ * <ix-combobox data-field="country" class="ix-combobox" data-no-hits-text="Ingen treff">
2330
+ * <div class="ix-text-field">
2331
+ * <input class="ix-text-field__input" aria-label="Landkode" />
2332
+ * <button type="button" class="ix-combobox__toggle" aria-label="Vis land"></button>
2333
+ * </div>
2334
+ * <div class="ix-combobox__listbox" hidden></div>
2335
+ * </ix-combobox>
2336
+ * <ix-field data-field="number">
2337
+ * <div class="ix-text-field">
2338
+ * <input aria-label="Telefonnummer" />
2339
+ * </div>
2340
+ * </ix-field>
2341
+ * </div>
2342
+ * <span data-field="error"></span>
2343
+ * </ix-phone-number-field>
2344
+ */
2345
+ var phoneNumberFieldCounter = 0;
2346
+ var IxPhoneNumberField = class extends HTMLElement {
2347
+ constructor(..._args) {
2348
+ super(..._args);
2349
+ _defineProperty(this, "_errorObserver", null);
2350
+ _defineProperty(this, "_childObserver", null);
2351
+ _defineProperty(this, "_instanceId", 0);
2352
+ _defineProperty(this, "_filling", false);
2353
+ _defineProperty(this, "_ownDisabled", /* @__PURE__ */ new WeakMap());
2354
+ _defineProperty(this, "_ownReadonly", /* @__PURE__ */ new WeakMap());
2355
+ _defineProperty(this, "_ownRequired", /* @__PURE__ */ new WeakMap());
2356
+ }
2357
+ static get observedAttributes() {
2358
+ return [
2359
+ "disabled",
2360
+ "readonly",
2361
+ "required",
2362
+ "data-locale",
2363
+ "data-countries",
2364
+ "data-country-code",
2365
+ "data-default-country-code"
2366
+ ];
2367
+ }
2368
+ connectedCallback() {
2369
+ this._wire();
2370
+ }
2371
+ disconnectedCallback() {
2372
+ this._errorObserver?.disconnect();
2373
+ this._errorObserver = null;
2374
+ this._childObserver?.disconnect();
2375
+ this._childObserver = null;
2376
+ }
2377
+ attributeChangedCallback(attr, _oldValue, _newValue) {
2378
+ if (!this.isConnected) return;
2379
+ if (attr === "disabled" || attr === "readonly") this._syncHostStateToControls();
2380
+ else if (attr === "required") this._syncRequired();
2381
+ else if (attr === "data-locale" || attr === "data-countries") this._refillCombobox();
2382
+ else if (attr === "data-country-code" || attr === "data-default-country-code") this._applyPreselection();
2383
+ }
2384
+ _wire() {
2385
+ this.setAttribute("role", "group");
2386
+ this._instanceId = ++phoneNumberFieldCounter;
2387
+ const legend = this.querySelector(":scope > [data-field=\"legend\"]");
2388
+ if (legend) {
2389
+ if (!legend.id) legend.id = `ix-phone-number-field-legend-${this._instanceId}`;
2390
+ this.setAttribute("aria-labelledby", legend.id);
2391
+ }
2392
+ this._wireDescribedBy();
2393
+ this._fillCombobox();
2394
+ this._stampNumberDefaults();
2395
+ this._syncHostStateToControls();
2396
+ this._syncRequired();
2397
+ this._syncDataState();
2398
+ this._childObserver = new MutationObserver((mutations) => {
2399
+ if (this._filling) return;
2400
+ if (mutations.some((m) => Array.from(m.addedNodes).some((n) => this._containsControl(n)) || Array.from(m.removedNodes).some((n) => this._containsControl(n)))) {
2401
+ this._fillCombobox();
2402
+ this._stampNumberDefaults();
2403
+ this._syncHostStateToControls();
2404
+ this._syncRequired();
2405
+ }
2406
+ if (mutations.some((m) => Array.from(m.addedNodes).some((n) => this._containsGroupField(n)) || Array.from(m.removedNodes).some((n) => this._containsGroupField(n)))) this._wireDescribedBy();
2407
+ });
2408
+ this._childObserver.observe(this, {
2409
+ childList: true,
2410
+ subtree: true
2411
+ });
2412
+ }
2413
+ _wireDescribedBy() {
2414
+ const description = this.querySelector(":scope > [data-field=\"description\"]");
2415
+ const error = this.querySelector(":scope > [data-field=\"error\"]");
2416
+ const describedBy = [];
2417
+ if (description) {
2418
+ if (!description.id) description.id = `ix-phone-number-field-desc-${this._instanceId}`;
2419
+ describedBy.push(description.id);
2420
+ }
2421
+ this._errorObserver?.disconnect();
2422
+ this._errorObserver = null;
2423
+ if (error) {
2424
+ if (!error.id) error.id = `ix-phone-number-field-error-${this._instanceId}`;
2425
+ describedBy.push(error.id);
2426
+ error.setAttribute("aria-live", "polite");
2427
+ this._errorObserver = new MutationObserver(() => {
2428
+ this._syncInvalid(error);
2429
+ });
2430
+ this._errorObserver.observe(error, {
2431
+ childList: true,
2432
+ characterData: true,
2433
+ subtree: true
2434
+ });
2435
+ this._syncInvalid(error);
2436
+ } else this.removeAttribute("aria-invalid");
2437
+ if (describedBy.length > 0) this.setAttribute("aria-describedby", describedBy.join(" "));
2438
+ else this.removeAttribute("aria-describedby");
2439
+ }
2440
+ _containsControl(node) {
2441
+ if (node.nodeType !== Node.ELEMENT_NODE) return false;
2442
+ const el = node;
2443
+ if (el.matches?.("ix-combobox, input")) return true;
2444
+ return !!el.querySelector?.("ix-combobox, input");
2445
+ }
2446
+ _containsGroupField(node) {
2447
+ if (node.nodeType !== Node.ELEMENT_NODE) return false;
2448
+ const el = node;
2449
+ const selector = "[data-field=\"description\"], [data-field=\"error\"]";
2450
+ if (el.matches?.(selector)) return true;
2451
+ return !!el.querySelector?.(selector);
2452
+ }
2453
+ _combobox() {
2454
+ return this.querySelector("ix-combobox");
2455
+ }
2456
+ _numberInput() {
2457
+ const numberField = this.querySelector("[data-field=\"number\"]");
2458
+ if (numberField) return numberField.querySelector("input");
2459
+ const combobox = this._combobox();
2460
+ return Array.from(this.querySelectorAll("input")).find((input) => !combobox?.contains(input)) ?? null;
2461
+ }
2462
+ _syncInvalid(error) {
2463
+ if (!!error.textContent?.trim()) this.setAttribute("aria-invalid", "true");
2464
+ else this.removeAttribute("aria-invalid");
2465
+ this._syncDataState();
2466
+ }
2467
+ _syncDataState() {
2468
+ const state = !!this.querySelector(":scope > [data-field=\"error\"]")?.textContent?.trim() ? "error" : this.hasAttribute("readonly") ? "readonly" : this.hasAttribute("disabled") ? "disabled" : null;
2469
+ if (state) this.setAttribute("data-state", state);
2470
+ else this.removeAttribute("data-state");
2471
+ }
2472
+ _syncHostStateToControls() {
2473
+ const isDisabled = this.hasAttribute("disabled");
2474
+ const isReadonly = this.hasAttribute("readonly");
2475
+ const combobox = this._combobox();
2476
+ if (combobox) this._applyState(combobox, isDisabled, isReadonly, "attribute");
2477
+ const input = this._numberInput();
2478
+ if (input) this._applyState(input, isDisabled, isReadonly, "property");
2479
+ this._syncDataState();
2480
+ }
2481
+ _syncRequired() {
2482
+ const on = this.hasAttribute("required");
2483
+ const comboboxInput = this._combobox()?.querySelector("input") ?? null;
2484
+ if (comboboxInput) this._applyFlag(comboboxInput, "required", on, this._ownRequired, "property");
2485
+ const numberInput = this._numberInput();
2486
+ if (numberInput) this._applyFlag(numberInput, "required", on, this._ownRequired, "property");
2487
+ }
2488
+ _applyState(control, isDisabled, isReadonly, mode) {
2489
+ this._applyFlag(control, "disabled", isDisabled, this._ownDisabled, mode);
2490
+ this._applyFlag(control, "readonly", isReadonly, this._ownReadonly, mode);
2491
+ }
2492
+ _applyFlag(control, flag, groupOn, ownStore, mode) {
2493
+ const prop = flag === "readonly" ? "readOnly" : flag;
2494
+ const read = () => mode === "attribute" ? control.hasAttribute(flag) : !!control[prop];
2495
+ const write = (value) => {
2496
+ if (mode === "attribute") if (value) control.setAttribute(flag, "");
2497
+ else control.removeAttribute(flag);
2498
+ else control[prop] = value;
2499
+ };
2500
+ if (groupOn) {
2501
+ if (!ownStore.has(control)) ownStore.set(control, read());
2502
+ write(true);
2503
+ } else if (ownStore.has(control)) {
2504
+ write(ownStore.get(control));
2505
+ ownStore.delete(control);
2506
+ }
2507
+ }
2508
+ _fillCombobox() {
2509
+ if (this._filling) return;
2510
+ const listbox = this._listbox();
2511
+ if (!listbox) return;
2512
+ if (listbox.querySelector(".ix-combobox__option")) return;
2513
+ const options = this._resolveCountryOptions();
2514
+ if (options.length === 0) return;
2515
+ const preselect = this._preselectValue();
2516
+ this._filling = true;
2517
+ try {
2518
+ const frag = document.createDocumentFragment();
2519
+ for (const opt of options) frag.appendChild(this._buildOption(opt, opt.value === preselect));
2520
+ listbox.setAttribute("data-ix-injected", "");
2521
+ listbox.appendChild(frag);
2522
+ } finally {
2523
+ this._filling = false;
2524
+ }
2525
+ }
2526
+ _refillCombobox() {
2527
+ const listbox = this._listbox();
2528
+ if (!listbox || !listbox.hasAttribute("data-ix-injected")) {
2529
+ this._fillCombobox();
2530
+ return;
2531
+ }
2532
+ this._filling = true;
2533
+ try {
2534
+ for (const opt of Array.from(listbox.querySelectorAll(".ix-combobox__option"))) opt.remove();
2535
+ } finally {
2536
+ this._filling = false;
2537
+ }
2538
+ listbox.removeAttribute("data-ix-injected");
2539
+ this._fillCombobox();
2540
+ }
2541
+ _applyPreselection() {
2542
+ const listbox = this._listbox();
2543
+ if (!listbox) return;
2544
+ const preselect = this._preselectValue();
2545
+ this._filling = true;
2546
+ try {
2547
+ for (const opt of Array.from(listbox.querySelectorAll(".ix-combobox__option"))) if (preselect !== null && opt.getAttribute("data-value") === preselect) opt.setAttribute("aria-selected", "true");
2548
+ else opt.removeAttribute("aria-selected");
2549
+ } finally {
2550
+ this._filling = false;
2551
+ }
2552
+ }
2553
+ _buildOption(opt, selected) {
2554
+ const el = document.createElement("div");
2555
+ el.className = "ix-combobox__option";
2556
+ el.setAttribute("data-value", opt.value);
2557
+ if (selected) el.setAttribute("aria-selected", "true");
2558
+ const check = document.createElement("span");
2559
+ check.className = "ix-combobox__option-check";
2560
+ check.setAttribute("aria-hidden", "true");
2561
+ el.appendChild(check);
2562
+ const label = document.createElement("span");
2563
+ label.className = "ix-combobox__option-label";
2564
+ label.textContent = opt.label;
2565
+ el.appendChild(label);
2566
+ if (opt.description) {
2567
+ const desc = document.createElement("span");
2568
+ desc.className = "ix-combobox__option-description";
2569
+ desc.textContent = opt.description;
2570
+ el.appendChild(desc);
2571
+ }
2572
+ return el;
2573
+ }
2574
+ _preselectValue() {
2575
+ return this.getAttribute("data-country-code") ?? this.getAttribute("data-default-country-code");
2576
+ }
2577
+ _resolveCountryOptions() {
2578
+ const raw = this.getAttribute("data-countries");
2579
+ if (raw) try {
2580
+ const parsed = JSON.parse(raw);
2581
+ if (Array.isArray(parsed) && parsed.every((o) => o && typeof o.value === "string" && typeof o.label === "string")) return parsed;
2582
+ } catch {}
2583
+ return getDefaultCountries(normalizeLocale(this.getAttribute("data-locale")));
2584
+ }
2585
+ _listbox() {
2586
+ return this._combobox()?.querySelector(".ix-combobox__listbox") ?? null;
2587
+ }
2588
+ _stampNumberDefaults() {
2589
+ const input = this._numberInput();
2590
+ if (!input) return;
2591
+ for (const [attr, value] of Object.entries({
2592
+ type: "tel",
2593
+ inputmode: "numeric",
2594
+ autocomplete: "tel-national",
2595
+ "data-format": "phone"
2596
+ })) if (!input.hasAttribute(attr)) input.setAttribute(attr, value);
2597
+ }
2598
+ };
2599
+ //#endregion
1644
2600
  //#region lib/utils/platform.ts
1645
2601
  /**
1646
2602
  * Plattform-deteksjon for web components.
@@ -2338,6 +3294,22 @@ var IxTabPanel = class extends HTMLElement {
2338
3294
  }
2339
3295
  };
2340
3296
  //#endregion
3297
+ //#region lib/utils/cn.ts
3298
+ function cn(...inputs) {
3299
+ const klasser = [];
3300
+ for (const input of inputs) {
3301
+ if (!input) continue;
3302
+ if (typeof input === "string" || typeof input === "number") klasser.push(String(input));
3303
+ else if (Array.isArray(input)) {
3304
+ const nøstet = cn(...input);
3305
+ if (nøstet) klasser.push(nøstet);
3306
+ } else if (typeof input === "object") {
3307
+ for (const nøkkel in input) if (input[nøkkel]) klasser.push(nøkkel);
3308
+ }
3309
+ }
3310
+ return klasser.join(" ");
3311
+ }
3312
+ //#endregion
2341
3313
  //#region lib/modal/modal.ts
2342
3314
  var ATTR_OPEN = "data-modal-open";
2343
3315
  var ATTR_CLOSE = "data-modal-close";
@@ -2530,10 +3502,13 @@ customElements.define("ix-icon", IxIcon);
2530
3502
  customElements.define("ix-radio-group", IxRadioGroup);
2531
3503
  customElements.define("ix-checkbox-group", IxCheckboxGroup);
2532
3504
  customElements.define("ix-combobox", IxCombobox);
3505
+ customElements.define("ix-date-field", IxDateField);
3506
+ customElements.define("ix-phone-number-field", IxPhoneNumberField);
2533
3507
  customElements.define("ix-progress-bar", IxProgressBar);
2534
3508
  customElements.define("ix-tabs", IxTabs);
2535
3509
  customElements.define("ix-tab-list", IxTabList);
2536
3510
  customElements.define("ix-tab", IxTab);
2537
3511
  customElements.define("ix-tab-panel", IxTabPanel);
3512
+ globalThis.cn = cn;
2538
3513
  //#endregion
2539
- export { COMMON_ICON_NAMES, IxCheckboxGroup, IxCombobox, IxField, IxIcon, IxProgressBar, IxRadioGroup, IxTab, IxTabList, IxTabPanel, IxTabs, amountFormatterForLocale, createAmountFormatter, createPatternFormatter, registerFormat, resolveFormat };
3514
+ export { BUILTIN_FORMAT_NAMES, COMMON_ICON_NAMES, IxCheckboxGroup, IxCombobox, IxDateField, IxField, IxIcon, IxPhoneNumberField, IxProgressBar, IxRadioGroup, IxTab, IxTabList, IxTabPanel, IxTabs, amountFormatterForLocale, cn, createAmountFormatter, createPatternFormatter, registerFormat, resolveFormat };