lite-phone-input 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -97,6 +97,36 @@ Same API as React. Uses `preact/hooks` directly — no `preact/compat` required.
97
97
  </script>
98
98
  ```
99
99
 
100
+ ## Geo IP Lookup
101
+
102
+ Auto-detect the user's country at mount time using any geo-IP service. The lookup runs once, and the result is ignored if the user has already interacted with the widget.
103
+
104
+ ```js
105
+ const phone = PhoneInput.mount(document.getElementById('phone'), {
106
+ defaultCountry: 'US',
107
+ geoIpLookup: (callback) => {
108
+ // Cloudflare (free, no API key)
109
+ fetch('/cdn-cgi/trace')
110
+ .then(res => res.text())
111
+ .then(text => {
112
+ const match = text.match(/loc=(\w+)/);
113
+ callback(match ? match[1] : null);
114
+ })
115
+ .catch(() => callback(null));
116
+ },
117
+ });
118
+ ```
119
+
120
+ ```js
121
+ // Alternative: ipapi.co
122
+ geoIpLookup: (callback) => {
123
+ fetch('https://ipapi.co/json/')
124
+ .then(res => res.json())
125
+ .then(data => callback(data.country_code))
126
+ .catch(() => callback(null));
127
+ }
128
+ ```
129
+
100
130
  ## Documentation
101
131
 
102
132
  | Guide | Description |
@@ -65,6 +65,7 @@ interface PhoneInputOptions {
65
65
  initialValue?: string;
66
66
  containerClass?: string;
67
67
  dropdownContainer?: HTMLElement;
68
+ geoIpLookup?: (callback: (countryCode: string | null) => void) => void;
68
69
  onChange?: (e164: string, country: Country, validation: ValidationResult) => void;
69
70
  onCountryChange?: (country: Country) => void;
70
71
  onValidationChange?: (validation: ValidationResult) => void;
@@ -65,6 +65,7 @@ interface PhoneInputOptions {
65
65
  initialValue?: string;
66
66
  containerClass?: string;
67
67
  dropdownContainer?: HTMLElement;
68
+ geoIpLookup?: (callback: (countryCode: string | null) => void) => void;
68
69
  onChange?: (e164: string, country: Country, validation: ValidationResult) => void;
69
70
  onCountryChange?: (country: Country) => void;
70
71
  onValidationChange?: (validation: ValidationResult) => void;
@@ -3301,6 +3301,7 @@ var PhoneInput = class _PhoneInput {
3301
3301
  constructor(el, options) {
3302
3302
  this.nationalDigits = "";
3303
3303
  this.displayInternational = false;
3304
+ this.userHasInteracted = false;
3304
3305
  this.lastValidation = null;
3305
3306
  this.dropdown = null;
3306
3307
  this.dialCodeEl = null;
@@ -3318,6 +3319,7 @@ var PhoneInput = class _PhoneInput {
3318
3319
  if (this.opts.initialValue) {
3319
3320
  this.setValueInternal(this.opts.initialValue, true);
3320
3321
  }
3322
+ this.invokeGeoIpLookup();
3321
3323
  }
3322
3324
  }
3323
3325
  get isNationalInput() {
@@ -3355,8 +3357,9 @@ var PhoneInput = class _PhoneInput {
3355
3357
  return validatePhone(this.nationalDigits, this.selectedCountry);
3356
3358
  }
3357
3359
  setOptions(opts) {
3360
+ const { geoIpLookup: _, ...rest } = opts;
3358
3361
  const prev = { ...this.opts };
3359
- Object.assign(this.opts, opts);
3362
+ Object.assign(this.opts, rest);
3360
3363
  if (opts.allowedCountries !== void 0 || opts.excludedCountries !== void 0) {
3361
3364
  this.countries = this.filterCountries(getAllCountries());
3362
3365
  if (!this.countries.find((c) => c.code === this.selectedCountry.code)) {
@@ -3398,6 +3401,16 @@ var PhoneInput = class _PhoneInput {
3398
3401
  }
3399
3402
  this.el.innerHTML = "";
3400
3403
  }
3404
+ invokeGeoIpLookup() {
3405
+ const lookup = this.opts.geoIpLookup;
3406
+ if (!lookup) return;
3407
+ this.opts.geoIpLookup = void 0;
3408
+ const signal = this.ac.signal;
3409
+ lookup((countryCode) => {
3410
+ if (signal.aborted || this.userHasInteracted || !countryCode) return;
3411
+ this.setCountry(countryCode);
3412
+ });
3413
+ }
3401
3414
  // --- DOM Construction ---
3402
3415
  buildDOM() {
3403
3416
  this.containerEl = document.createElement("div");
@@ -3497,6 +3510,7 @@ var PhoneInput = class _PhoneInput {
3497
3510
  this.inputEl.addEventListener("paste", (e) => this.handlePaste(e), { signal });
3498
3511
  }
3499
3512
  handleInput(e) {
3513
+ this.userHasInteracted = true;
3500
3514
  if (this.opts.strict !== false && e.data === "+" && this.isNationalInput) {
3501
3515
  this.inputEl.value = this.inputEl.value.replace("+", "");
3502
3516
  }
@@ -3562,6 +3576,7 @@ var PhoneInput = class _PhoneInput {
3562
3576
  }
3563
3577
  }
3564
3578
  handlePaste(e) {
3579
+ this.userHasInteracted = true;
3565
3580
  e.preventDefault();
3566
3581
  const text = e.clipboardData?.getData("text") ?? "";
3567
3582
  if (!text) return;
@@ -3600,6 +3615,7 @@ var PhoneInput = class _PhoneInput {
3600
3615
  // --- Dropdown ---
3601
3616
  openDropdown() {
3602
3617
  if (this.dropdown) return;
3618
+ this.userHasInteracted = true;
3603
3619
  this.dropdown = new Dropdown({
3604
3620
  countries: this.countries,
3605
3621
  preferredCountries: this.opts.preferredCountries ?? [],
@@ -3857,6 +3873,7 @@ var WIDGET_KEYS = /* @__PURE__ */ new Set([
3857
3873
  "initialValue",
3858
3874
  "containerClass",
3859
3875
  "dropdownContainer",
3876
+ "geoIpLookup",
3860
3877
  "onChange",
3861
3878
  "onCountryChange",
3862
3879
  "onValidationChange",
@@ -3914,6 +3931,7 @@ var PhoneInput2 = (0, import_compat.forwardRef)(
3914
3931
  hiddenInput: p.hiddenInput,
3915
3932
  containerClass: p.containerClass,
3916
3933
  dropdownContainer: p.dropdownContainer,
3934
+ geoIpLookup: p.geoIpLookup,
3917
3935
  initialValue: p.initialValue,
3918
3936
  inputAttributes,
3919
3937
  onChange: (e164, country, validation) => propsRef.current.onChange?.(e164, country, validation),