@jacksonavila/phone-lib 2.0.8 → 2.0.10

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
@@ -13,6 +13,7 @@ Librería JavaScript moderna para integrar fácilmente un input de teléfono con
13
13
 
14
14
  - ✅ **Country dropdown** / **Dropdown de países** showing name, ISO2 code, dial code, and flag
15
15
  - ✅ **Tel input** / **Input tipo tel** with automatic formatting based on selected country
16
+ - ✅ **Numbers only input** / **Input solo números** - automatically filters non-numeric characters
16
17
  - ✅ **Phone number validation** / **Validación de números** using `libphonenumber-js`
17
18
  - ✅ **Complete public API** / **API pública completa** with methods to get number information
18
19
  - ✅ **Vanilla JavaScript and React support** / **Soporte para Vanilla JavaScript y React**
@@ -61,7 +62,7 @@ You can use PhoneLib directly from CDN without npm / Puedes usar PhoneLib direct
61
62
  <!DOCTYPE html>
62
63
  <html>
63
64
  <head>
64
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.7/phone-lib.css">
65
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.10/phone-lib.css">
65
66
  </head>
66
67
  <body>
67
68
  <div id="phone-container"></div>
@@ -69,7 +70,7 @@ You can use PhoneLib directly from CDN without npm / Puedes usar PhoneLib direct
69
70
  <script type="importmap">
70
71
  {
71
72
  "imports": {
72
- "@jacksonavila/phone-lib": "https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.7/phone-lib.js",
73
+ "@jacksonavila/phone-lib": "https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.10/phone-lib.js",
73
74
  "libphonenumber-js": "https://esm.sh/libphonenumber-js@1.11.0"
74
75
  }
75
76
  }
@@ -89,8 +90,8 @@ You can use PhoneLib directly from CDN without npm / Puedes usar PhoneLib direct
89
90
  ```
90
91
 
91
92
  **CDN URLs / URLs de CDN:**
92
- - **jsDelivr:** `https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.7/`
93
- - **unpkg:** `https://unpkg.com/@jacksonavila/phone-lib@2.0.7/`
93
+ - **jsDelivr:** `https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.10/`
94
+ - **unpkg:** `https://unpkg.com/@jacksonavila/phone-lib@2.0.10/`
94
95
 
95
96
  ### Method 2: Script Tag (All Browsers) / Método 2: Script Tag (Todos los Navegadores)
96
97
 
@@ -98,12 +99,12 @@ You can use PhoneLib directly from CDN without npm / Puedes usar PhoneLib direct
98
99
  <!DOCTYPE html>
99
100
  <html>
100
101
  <head>
101
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.7/phone-lib.css">
102
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.10/phone-lib.css">
102
103
  </head>
103
104
  <body>
104
105
  <div id="phone-container"></div>
105
106
 
106
- <script src="https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.7/phone-lib.cdn.js"></script>
107
+ <script src="https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.10/phone-lib.cdn.js"></script>
107
108
 
108
109
  <script>
109
110
  let phoneLib = null;
@@ -196,6 +197,7 @@ import '@jacksonavila/phone-lib/css';
196
197
 
197
198
  const phoneLib = new PhoneLib('#phone-container', {
198
199
  initialCountry: 'CO',
200
+ initialPhoneNumber: '+573001234567', // Optional: set initial phone number / Opcional: establecer número inicial
199
201
  preferredCountries: ['CO', 'US', 'ES'],
200
202
  showHint: true,
201
203
  layout: 'integrated',
@@ -453,6 +455,54 @@ function ContactForm() {
453
455
  }
454
456
  ```
455
457
 
458
+ #### With Initial Phone Number (React Forms) / Con Número Inicial (Formularios React)
459
+
460
+ **Important / Importante:** Use `initialPhoneNumber` to set an initial value and ensure the phone number persists when the parent component re-renders / Usa `initialPhoneNumber` para establecer un valor inicial y asegurar que el número persista cuando el componente padre se re-renderiza:
461
+
462
+ ```jsx
463
+ import React, { useState } from 'react';
464
+ import PhoneLibReact from '@jacksonavila/phone-lib/react';
465
+ import '@jacksonavila/phone-lib/css';
466
+
467
+ function EditContactForm({ contact }) {
468
+ const [name, setName] = useState(contact?.name || '');
469
+ const [email, setEmail] = useState(contact?.email || '');
470
+
471
+ return (
472
+ <form>
473
+ <input
474
+ value={name}
475
+ onChange={(e) => setName(e.target.value)}
476
+ placeholder="Name / Nombre"
477
+ />
478
+ <input
479
+ value={email}
480
+ onChange={(e) => setEmail(e.target.value)}
481
+ placeholder="Email"
482
+ />
483
+
484
+ {/* The phone number value persists even when name/email change /
485
+ El valor del teléfono persiste incluso cuando cambian name/email */}
486
+ <PhoneLibReact
487
+ initialCountry={contact?.country || 'US'}
488
+ initialPhoneNumber={contact?.phone || null}
489
+ onPhoneChange={(phone, isValid) => {
490
+ console.log('Phone / Teléfono:', phone);
491
+ }}
492
+ />
493
+
494
+ <button type="submit">Save / Guardar</button>
495
+ </form>
496
+ );
497
+ }
498
+ ```
499
+
500
+ **💡 Why `initialPhoneNumber` is important / Por qué `initialPhoneNumber` es importante:**
501
+
502
+ - **Prevents data loss / Previene pérdida de datos:** Without `initialPhoneNumber`, the phone value can be lost when the parent component re-renders (e.g., when other form fields change) / Sin `initialPhoneNumber`, el valor del teléfono puede perderse cuando el componente padre se re-renderiza (ej: cuando otros campos del formulario cambian)
503
+ - **Form editing / Edición de formularios:** Essential when editing existing data - the phone number is preserved during re-renders / Esencial al editar datos existentes - el número de teléfono se preserva durante re-renders
504
+ - **Controlled components / Componentes controlados:** Works seamlessly with React's controlled component pattern / Funciona perfectamente con el patrón de componentes controlados de React
505
+
456
506
  ---
457
507
 
458
508
  ## 🎨 Available Layouts / Layouts Disponibles
@@ -512,6 +562,7 @@ const phoneLib = new PhoneLib('#container', {
512
562
  ```javascript
513
563
  {
514
564
  initialCountry: 'CO', // Initial country / País inicial (ISO2)
565
+ initialPhoneNumber: '+573001234567', // Initial phone number (optional) / Número inicial (opcional)
515
566
  preferredCountries: ['CO', 'US'], // Countries that appear first / Países que aparecen primero
516
567
  showHint: true, // Show validation hint / Mostrar hint de validación
517
568
  layout: 'integrated', // 'integrated' or 'separated' / 'integrated' o 'separated'
@@ -519,10 +570,22 @@ const phoneLib = new PhoneLib('#container', {
519
570
  }
520
571
  ```
521
572
 
573
+ **📝 Note about `initialPhoneNumber` / Nota sobre `initialPhoneNumber`:**
574
+
575
+ - **Vanilla JS:** Use `initialPhoneNumber` in the constructor options to set an initial phone number value / Usa `initialPhoneNumber` en las opciones del constructor para establecer un valor inicial del teléfono
576
+ - **React:** Use the `initialPhoneNumber` prop to set an initial value. This prop also ensures the phone number value persists when the parent component re-renders / Usa la prop `initialPhoneNumber` para establecer un valor inicial. Esta prop también asegura que el valor del teléfono persista cuando el componente padre se re-renderiza
577
+ - The value is preserved during re-renders, preventing data loss in React forms / El valor se preserva durante re-renders, previniendo pérdida de datos en formularios React
578
+
522
579
  ### Advanced Options / Opciones Avanzadas
523
580
 
524
581
  ```javascript
525
582
  {
583
+ // Initial values / Valores iniciales
584
+ initialCountry: 'CO', // Initial country (ISO2) / País inicial (ISO2)
585
+ initialPhoneNumber: '+573001234567', // Initial phone number (optional) / Número inicial (opcional)
586
+ // In React: ensures value persists during re-renders /
587
+ // En React: asegura que el valor persista durante re-renders
588
+
526
589
  // Detection and validation / Detección y validación
527
590
  autoDetectCountry: true, // Auto-detect country / Detectar país automáticamente
528
591
  validateOnInput: false, // Validate while typing / Validar mientras se escribe
@@ -878,6 +941,7 @@ const phoneLib = new PhoneLib('#container', {
878
941
  | Prop | Type | Default | Description / Descripción |
879
942
  |------|------|---------|--------------------------|
880
943
  | `initialCountry` | string | `'US'` | Initial country (ISO2) / País inicial (ISO2) |
944
+ | `initialPhoneNumber` | string \| null | `null` | Initial phone number value. **Important:** This prop ensures the phone value persists during parent component re-renders in React / Valor inicial del teléfono. **Importante:** Esta prop asegura que el valor del teléfono persista durante re-renders del componente padre en React |
881
945
  | `preferredCountries` | array | `[]` | Preferred countries / Países preferidos |
882
946
  | `showHint` | boolean | `true` | Show validation hint / Mostrar hint de validación |
883
947
  | `layout` | string | `'integrated'` | `'integrated'` or `'separated'` / `'integrated'` o `'separated'` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacksonavila/phone-lib",
3
- "version": "2.0.8",
3
+ "version": "2.0.10",
4
4
  "description": "Librería JavaScript para input de teléfono con selector de país y banderas - Compatible con Vanilla JS y React",
5
5
  "main": "phone-lib.js",
6
6
  "module": "phone-lib.js",
@@ -28,6 +28,7 @@ const PhoneLibReact = forwardRef((props, ref) => {
28
28
  containerId,
29
29
  containerClassName,
30
30
  initialCountry = 'US',
31
+ initialPhoneNumber = null,
31
32
  preferredCountries = [],
32
33
  showHint = true,
33
34
  layout = 'integrated',
@@ -67,6 +68,7 @@ const PhoneLibReact = forwardRef((props, ref) => {
67
68
  // Inicializar PhoneLib
68
69
  phoneLibRef.current = new PhoneLib(containerRef.current, {
69
70
  initialCountry,
71
+ initialPhoneNumber,
70
72
  preferredCountries,
71
73
  showHint,
72
74
  layout,
@@ -93,6 +95,11 @@ const PhoneLibReact = forwardRef((props, ref) => {
93
95
  onBlur
94
96
  });
95
97
 
98
+ // Si hay un número inicial, establecerlo después de la inicialización
99
+ if (initialPhoneNumber) {
100
+ phoneLibRef.current.setPhoneNumber(initialPhoneNumber);
101
+ }
102
+
96
103
  // Cleanup al desmontar
97
104
  return () => {
98
105
  if (phoneLibRef.current) {
@@ -108,6 +115,7 @@ const PhoneLibReact = forwardRef((props, ref) => {
108
115
 
109
116
  phoneLibRef.current.updateOptions({
110
117
  initialCountry,
118
+ initialPhoneNumber,
111
119
  preferredCountries,
112
120
  showHint,
113
121
  layout,
@@ -130,6 +138,7 @@ const PhoneLibReact = forwardRef((props, ref) => {
130
138
  });
131
139
  }, [
132
140
  initialCountry,
141
+ initialPhoneNumber,
133
142
  preferredCountries,
134
143
  showHint,
135
144
  layout,
package/phone-lib.cdn.js CHANGED
@@ -4,8 +4,8 @@
4
4
  * Carga libphonenumber-js dinámicamente y expone PhoneLib globalmente
5
5
  *
6
6
  * Uso:
7
- * <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.8/phone-lib.css">
8
- * <script src="https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.8/phone-lib.cdn.js"></script>
7
+ * <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.10/phone-lib.css">
8
+ * <script src="https://cdn.jsdelivr.net/npm/@jacksonavila/phone-lib@2.0.10/phone-lib.cdn.js"></script>
9
9
  * <script>
10
10
  * document.addEventListener('phoneLibReady', () => {
11
11
  * const phoneLib = new PhoneLib('#container', {...});
@@ -17,7 +17,7 @@
17
17
  'use strict';
18
18
 
19
19
  // Versión del paquete (actualizar cuando se publique nueva versión)
20
- const PACKAGE_VERSION = '2.0.8';
20
+ const PACKAGE_VERSION = '2.0.10';
21
21
  const PACKAGE_NAME = '@jacksonavila/phone-lib';
22
22
 
23
23
  // URLs de CDN
package/phone-lib.js CHANGED
@@ -76,7 +76,7 @@ class PhoneLib {
76
76
 
77
77
  // Estado interno
78
78
  this.selectedCountry = this.options.initialCountry;
79
- this.phoneNumber = '';
79
+ this.phoneNumber = options.initialPhoneNumber || '';
80
80
  this.isValid = false;
81
81
  this.isDisabled = this.options.disabled;
82
82
  this.isReadonly = this.options.readonly;
@@ -543,7 +543,13 @@ class PhoneLib {
543
543
  init() {
544
544
  this.render();
545
545
  this.attachEventListeners();
546
- this.updatePhoneNumber();
546
+
547
+ // Si hay un número inicial, establecerlo después de renderizar
548
+ if (this.phoneNumber) {
549
+ this.setPhoneNumber(this.phoneNumber);
550
+ } else {
551
+ this.updatePhoneNumber();
552
+ }
547
553
 
548
554
  // Aplicar estados iniciales
549
555
  if (this.isDisabled) {
@@ -895,11 +901,65 @@ class PhoneLib {
895
901
 
896
902
  // Input de teléfono
897
903
  if (this.phoneInput) {
904
+ // Handler para prevenir entrada de caracteres no numéricos
905
+ this._boundHandlers.phoneKeyDown = (e) => {
906
+ if (this.isDisabled || this.isReadonly) return;
907
+
908
+ // Permitir teclas especiales (Backspace, Delete, Arrow keys, Tab, etc.)
909
+ const allowedKeys = [
910
+ 'Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',
911
+ 'Home', 'End', 'Tab', 'Escape', 'Enter'
912
+ ];
913
+
914
+ // Permitir Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X
915
+ if (e.ctrlKey || e.metaKey) {
916
+ if (['a', 'c', 'v', 'x'].includes(e.key.toLowerCase())) {
917
+ return; // Permitir
918
+ }
919
+ }
920
+
921
+ // Si es una tecla permitida, dejar pasar
922
+ if (allowedKeys.includes(e.key)) {
923
+ return;
924
+ }
925
+
926
+ // Permitir números (0-9)
927
+ if (e.key >= '0' && e.key <= '9') {
928
+ return;
929
+ }
930
+
931
+ // Permitir + solo al inicio del input
932
+ if (e.key === '+') {
933
+ const currentValue = e.target.value;
934
+ const selectionStart = e.target.selectionStart;
935
+ // Solo permitir + si está al inicio o si no hay ningún carácter antes de la posición del cursor
936
+ if (selectionStart === 0 || currentValue.substring(0, selectionStart).trim() === '') {
937
+ return;
938
+ }
939
+ }
940
+
941
+ // Si llegamos aquí, prevenir la entrada
942
+ e.preventDefault();
943
+ };
944
+
898
945
  this._boundHandlers.phoneInput = (e) => {
899
946
  if (this.isDisabled || this.isReadonly) return;
900
947
 
901
948
  // Obtener el valor actual del input
902
- const inputValue = e.target.value;
949
+ let inputValue = e.target.value;
950
+
951
+ // Filtrar caracteres no numéricos (permitir números y +)
952
+ // Remover cualquier carácter que no sea número o +
953
+ inputValue = inputValue.replace(/[^0-9+]/g, '');
954
+
955
+ // Si el valor cambió después de filtrar, actualizar el input
956
+ if (inputValue !== e.target.value) {
957
+ const cursorPosition = e.target.selectionStart;
958
+ e.target.value = inputValue;
959
+ // Restaurar posición del cursor (ajustada por caracteres removidos)
960
+ const newPosition = Math.min(cursorPosition, inputValue.length);
961
+ e.target.setSelectionRange(newPosition, newPosition);
962
+ }
903
963
 
904
964
  // Procesar el input
905
965
  this.handlePhoneInput(inputValue);
@@ -921,6 +981,7 @@ class PhoneLib {
921
981
  this.emitEvent('blur', { phoneNumber: this.phoneNumber, isValid });
922
982
  };
923
983
 
984
+ this.phoneInput.addEventListener('keydown', this._boundHandlers.phoneKeyDown);
924
985
  this.phoneInput.addEventListener('input', this._boundHandlers.phoneInput);
925
986
  this.phoneInput.addEventListener('focus', this._boundHandlers.phoneFocus);
926
987
  this.phoneInput.addEventListener('blur', this._boundHandlers.phoneBlur);
@@ -954,6 +1015,9 @@ class PhoneLib {
954
1015
 
955
1016
  // Remover listeners del input
956
1017
  if (this.phoneInput && this._boundHandlers) {
1018
+ if (this._boundHandlers.phoneKeyDown) {
1019
+ this.phoneInput.removeEventListener('keydown', this._boundHandlers.phoneKeyDown);
1020
+ }
957
1021
  if (this._boundHandlers.phoneInput) {
958
1022
  this.phoneInput.removeEventListener('input', this._boundHandlers.phoneInput);
959
1023
  }
@@ -1501,9 +1565,12 @@ class PhoneLib {
1501
1565
  return;
1502
1566
  }
1503
1567
 
1504
- this.phoneNumber = number;
1568
+ // Filtrar caracteres no numéricos (permitir números y +)
1569
+ const filteredNumber = typeof number === 'string' ? number.replace(/[^0-9+]/g, '') : (number || '').toString().replace(/[^0-9+]/g, '');
1570
+
1571
+ this.phoneNumber = filteredNumber;
1505
1572
  if (this.phoneInput) {
1506
- this.phoneInput.value = number;
1573
+ this.phoneInput.value = filteredNumber;
1507
1574
  }
1508
1575
  this.updatePhoneNumber();
1509
1576
 
@@ -1624,6 +1691,10 @@ class PhoneLib {
1624
1691
  * Actualiza opciones dinámicamente
1625
1692
  */
1626
1693
  updateOptions(newOptions) {
1694
+ // Preservar el valor actual del teléfono y país antes de cualquier cambio
1695
+ const savedPhoneNumber = this.phoneNumber || '';
1696
+ const savedCountry = this.selectedCountry || this.options.initialCountry || 'US';
1697
+
1627
1698
  // Actualizar opciones
1628
1699
  this.options = { ...this.options, ...newOptions };
1629
1700
 
@@ -1646,10 +1717,28 @@ class PhoneLib {
1646
1717
  this.setCountry(newOptions.initialCountry);
1647
1718
  }
1648
1719
 
1720
+ // Si se proporciona un número inicial, usarlo
1721
+ if (newOptions.initialPhoneNumber !== undefined) {
1722
+ this.phoneNumber = newOptions.initialPhoneNumber;
1723
+ }
1724
+
1649
1725
  // Re-renderizar si cambió layout u opciones visuales importantes
1650
1726
  if (newOptions.layout || newOptions.showDialCode !== undefined || newOptions.customClasses || newOptions.customStyles || newOptions.arrowIcon !== undefined) {
1651
1727
  this.render();
1652
1728
  this.attachEventListeners();
1729
+
1730
+ // Restaurar el valor del teléfono después de re-renderizar
1731
+ // Si no se proporcionó un nuevo initialPhoneNumber, restaurar el valor guardado
1732
+ if (newOptions.initialPhoneNumber === undefined && savedPhoneNumber) {
1733
+ this.setPhoneNumber(savedPhoneNumber);
1734
+ } else if (newOptions.initialPhoneNumber !== undefined) {
1735
+ this.setPhoneNumber(newOptions.initialPhoneNumber);
1736
+ }
1737
+
1738
+ // Asegurar que el país también esté correcto
1739
+ if (this.selectedCountry !== savedCountry && newOptions.initialCountry === undefined) {
1740
+ this.setCountry(savedCountry);
1741
+ }
1653
1742
  }
1654
1743
  }
1655
1744
  }