@jacksonavila/phone-lib 2.0.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 ADDED
@@ -0,0 +1,455 @@
1
+ # PhoneLib 📱
2
+
3
+ Librería JavaScript para integrar fácilmente un input de teléfono con selector de país, banderas y validación de números telefónicos. **Compatible con Vanilla JavaScript y React**.
4
+
5
+ ## Características
6
+
7
+ - ✅ Dropdown con países mostrando nombre, código ISO2, código de marcación y bandera
8
+ - ✅ Input de tipo "tel" con formato automático según el país seleccionado
9
+ - ✅ Validación de números telefónicos usando `libphonenumber-js`
10
+ - ✅ API pública completa con métodos para obtener información del número
11
+ - ✅ Opciones de configuración flexibles
12
+ - ✅ Estilos CSS modernos y responsivos
13
+ - ✅ **Soporte para Vanilla JavaScript y React**
14
+ - ✅ Eventos y callbacks personalizables
15
+ - ✅ Control programático completo
16
+ - ✅ Detección automática de país
17
+ - ✅ Filtrado de países
18
+ - ✅ Modo readonly/disabled
19
+
20
+ ## Instalación
21
+
22
+ ### Desde npm (Después de publicar)
23
+
24
+ ```bash
25
+ npm install @jacksonavila/phone-lib
26
+ ```
27
+
28
+ ### Desarrollo Local
29
+
30
+ ```bash
31
+ npm install
32
+ ```
33
+
34
+ ## 🚀 Cómo Probar la Librería
35
+
36
+ ### Opción Rápida (Recomendada)
37
+
38
+ ```bash
39
+ # 1. Instalar dependencias
40
+ npm install
41
+
42
+ # 2. Iniciar servidor de desarrollo
43
+ npm run serve
44
+ ```
45
+
46
+ Esto abrirá automáticamente tu navegador en `http://localhost:3004` con todos los demos disponibles.
47
+
48
+ ### Archivos de Demo Disponibles
49
+
50
+ Una vez que el servidor esté corriendo, puedes probar:
51
+
52
+ - **`demo.html`** - Ejemplo básico con layout integrado
53
+ - **`demo-separated.html`** - Layout separado
54
+ - **`demo-all-layouts.html`** - Comparación de todos los layouts
55
+ - **`demo-features.html`** - Todas las nuevas características (eventos, control programático, etc.)
56
+ - **`demo-react.html`** - Ejemplo con React
57
+
58
+ ### Otras Opciones de Servidor
59
+
60
+ ```bash
61
+ # Python 3
62
+ python -m http.server 8000
63
+
64
+ # Node.js http-server
65
+ npx http-server . -p 8080 -o
66
+
67
+ # PHP
68
+ php -S localhost:8000
69
+ ```
70
+
71
+ **⚠️ Importante:** No abras los archivos directamente con `file://` - necesitas un servidor HTTP debido a los módulos ES6.
72
+
73
+ 📖 **Para más detalles sobre cómo probar**, consulta [`TESTING.md`](./TESTING.md)
74
+
75
+ ## Uso Básico
76
+
77
+ ### HTML
78
+
79
+ ```html
80
+ <!DOCTYPE html>
81
+ <html>
82
+ <head>
83
+ <link rel="stylesheet" href="phone-lib.css">
84
+ </head>
85
+ <body>
86
+ <div id="phone-container"></div>
87
+
88
+ <script type="module">
89
+ import PhoneLib from './phone-lib.js';
90
+
91
+ const phoneLib = new PhoneLib('#phone-container', {
92
+ initialCountry: 'CO',
93
+ preferredCountries: ['CO', 'US', 'ES'],
94
+ showHint: true
95
+ });
96
+ </script>
97
+ </body>
98
+ </html>
99
+ ```
100
+
101
+ ### Opciones de Configuración
102
+
103
+ ```javascript
104
+ const phoneLib = new PhoneLib(container, {
105
+ initialCountry: 'CO', // País inicial (código ISO2)
106
+ preferredCountries: ['CO', 'US'], // Países que aparecen primero en la lista
107
+ showHint: true, // Mostrar hint de validación
108
+ layout: 'integrated', // 'integrated' o 'separated'
109
+ showDialCode: true // Mostrar código de marcación (true/false)
110
+ });
111
+ ```
112
+
113
+ ### Ejemplos de Layouts
114
+
115
+ **Layout Integrado con código:**
116
+ ```javascript
117
+ const phoneLib = new PhoneLib('#phone-container', {
118
+ layout: 'integrated',
119
+ showDialCode: true // Muestra +57 en el botón
120
+ });
121
+ ```
122
+
123
+ **Layout Integrado sin código:**
124
+ ```javascript
125
+ const phoneLib = new PhoneLib('#phone-container', {
126
+ layout: 'integrated',
127
+ showDialCode: false // Solo muestra la bandera
128
+ });
129
+ ```
130
+
131
+ **Layout Separado con código:**
132
+ ```javascript
133
+ const phoneLib = new PhoneLib('#phone-container', {
134
+ layout: 'separated',
135
+ showDialCode: true // Muestra campo "Código" separado
136
+ });
137
+ ```
138
+
139
+ **Layout Separado sin código:**
140
+ ```javascript
141
+ const phoneLib = new PhoneLib('#phone-container', {
142
+ layout: 'separated',
143
+ showDialCode: false // Solo muestra País y Número
144
+ });
145
+ ```
146
+
147
+ ### Estilos Personalizados
148
+
149
+ Puedes personalizar los estilos usando clases CSS o estilos inline:
150
+
151
+ **Con clases CSS personalizadas:**
152
+ ```javascript
153
+ const phoneLib = new PhoneLib('#phone-container', {
154
+ layout: 'integrated',
155
+ customClasses: {
156
+ wrapper: 'mi-clase-personalizada',
157
+ dropdownButton: 'mi-boton-personalizado',
158
+ input: 'mi-input-personalizado'
159
+ }
160
+ });
161
+ ```
162
+
163
+ **Con estilos inline:**
164
+ ```javascript
165
+ const phoneLib = new PhoneLib('#phone-container', {
166
+ layout: 'integrated',
167
+ customStyles: {
168
+ dropdownButton: {
169
+ backgroundColor: '#4a90e2',
170
+ borderRadius: '20px',
171
+ color: 'white'
172
+ },
173
+ input: {
174
+ borderColor: '#4a90e2',
175
+ fontSize: '18px'
176
+ }
177
+ }
178
+ });
179
+ ```
180
+
181
+ **Combinando ambos métodos:**
182
+ ```javascript
183
+ const phoneLib = new PhoneLib('#phone-container', {
184
+ layout: 'separated',
185
+ customClasses: {
186
+ wrapper: 'mi-tema-personalizado'
187
+ },
188
+ customStyles: {
189
+ row: {
190
+ gap: '20px'
191
+ },
192
+ input: {
193
+ borderColor: '#27ae60'
194
+ }
195
+ }
196
+ });
197
+ ```
198
+
199
+ **Elementos personalizables:**
200
+ - `wrapper` - Contenedor principal
201
+ - `dropdownButton` - Botón del selector de país
202
+ - `input` - Campo de entrada de teléfono
203
+ - `dialCodeInput` - Campo de código (solo layout separado)
204
+ - `row` - Fila del grid (solo layout separado)
205
+
206
+ ## API Pública
207
+
208
+ ### `getCountry()`
209
+ Devuelve el código ISO2 del país seleccionado.
210
+
211
+ ```javascript
212
+ const country = phoneLib.getCountry(); // 'CO'
213
+ ```
214
+
215
+ ### `getDialCode()`
216
+ Devuelve el código de marcación del país seleccionado.
217
+
218
+ ```javascript
219
+ const dialCode = phoneLib.getDialCode(); // '+57'
220
+ ```
221
+
222
+ ### `getRaw()`
223
+ Devuelve el número ingresado sin formato.
224
+
225
+ ```javascript
226
+ const raw = phoneLib.getRaw(); // '3001234567'
227
+ ```
228
+
229
+ ### `getE164()`
230
+ Devuelve el número en formato E.164.
231
+
232
+ ```javascript
233
+ const e164 = phoneLib.getE164(); // '+573001234567'
234
+ ```
235
+
236
+ ### `isValid()`
237
+ Devuelve `true` si el número es válido, `false` en caso contrario.
238
+
239
+ ```javascript
240
+ const isValid = phoneLib.isValid(); // true o false
241
+ ```
242
+
243
+ ### `formatInternational()`
244
+ Devuelve el número en formato internacional.
245
+
246
+ ```javascript
247
+ const international = phoneLib.formatInternational(); // '+57 300 123 4567'
248
+ ```
249
+
250
+ ### `formatNational()`
251
+ Devuelve el número en formato nacional.
252
+
253
+ ```javascript
254
+ const national = phoneLib.formatNational(); // '300 123 4567'
255
+ ```
256
+
257
+ ### `formatRFC3966()`
258
+ Devuelve el número en formato RFC3966.
259
+
260
+ ```javascript
261
+ const rfc3966 = phoneLib.formatRFC3966(); // 'tel:+57-300-123-4567'
262
+ ```
263
+
264
+ ### `getNumberType()`
265
+ Devuelve el tipo de número (MOBILE, FIXED_LINE, etc.).
266
+
267
+ ```javascript
268
+ const type = phoneLib.getNumberType(); // 'MOBILE' o null
269
+ ```
270
+
271
+ ### `getInfo()`
272
+ Devuelve un objeto con toda la información del número.
273
+
274
+ ```javascript
275
+ const info = phoneLib.getInfo();
276
+ // {
277
+ // country: 'CO',
278
+ // dialCode: '+57',
279
+ // raw: '3001234567',
280
+ // e164: '+573001234567',
281
+ // international: '+57 300 123 4567',
282
+ // national: '300 123 4567',
283
+ // rfc3966: 'tel:+57-300-123-4567',
284
+ // isValid: true,
285
+ // type: 'MOBILE',
286
+ // countryName: 'Colombia'
287
+ // }
288
+ ```
289
+
290
+ ### `getCountryMetadata()`
291
+ Devuelve información completa del país seleccionado.
292
+
293
+ ```javascript
294
+ const metadata = phoneLib.getCountryMetadata();
295
+ // {
296
+ // iso2: 'CO',
297
+ // name: 'Colombia',
298
+ // dialCode: '+57',
299
+ // flag: '<img...>'
300
+ // }
301
+ ```
302
+
303
+ ### `setCountry(iso2)`
304
+ Establece el país programáticamente.
305
+
306
+ ```javascript
307
+ phoneLib.setCountry('ES');
308
+ ```
309
+
310
+ ### `setPhoneNumber(number)`
311
+ Establece el número telefónico programáticamente.
312
+
313
+ ```javascript
314
+ phoneLib.setPhoneNumber('+34600123456');
315
+ ```
316
+
317
+ ### `setValue(country, number)`
318
+ Establece país y número juntos.
319
+
320
+ ```javascript
321
+ phoneLib.setValue('ES', '600123456');
322
+ ```
323
+
324
+ ### `enable()` / `disable()`
325
+ Habilita o deshabilita el componente.
326
+
327
+ ```javascript
328
+ phoneLib.enable();
329
+ phoneLib.disable();
330
+ ```
331
+
332
+ ### `reset()`
333
+ Resetea el componente a valores iniciales.
334
+
335
+ ```javascript
336
+ phoneLib.reset();
337
+ ```
338
+
339
+ ### `destroy()`
340
+ Destruye la instancia y limpia recursos.
341
+
342
+ ```javascript
343
+ phoneLib.destroy();
344
+ ```
345
+
346
+ ### `updateOptions(newOptions)`
347
+ Actualiza opciones dinámicamente.
348
+
349
+ ```javascript
350
+ phoneLib.updateOptions({
351
+ preferredCountries: ['CO', 'US', 'ES', 'MX']
352
+ });
353
+ ```
354
+
355
+ ## Ejemplos Completos
356
+
357
+ ### Vanilla JavaScript
358
+ - `demo.html` - Layout integrado con código
359
+ - `demo-separated.html` - Layout separado con código
360
+ - `demo-all-layouts.html` - Comparación de todos los layouts disponibles
361
+ - `demo-features.html` - Demostración de todas las nuevas características
362
+
363
+ ### React
364
+ - `demo-react.html` - Ejemplo básico con React (usando Babel Standalone para demo)
365
+ - `phone-lib-react.jsx` - Componente React completo para usar en tu proyecto
366
+
367
+ Ver estos archivos para ejemplos completos de integración con actualización en tiempo real de la información del número.
368
+
369
+ ## Estructura de Archivos
370
+
371
+ ```
372
+ phone-lib/
373
+ ├── phone-lib.js # Código principal de la librería (Vanilla JS)
374
+ ├── phone-lib.css # Estilos CSS
375
+ ├── phone-lib-react.jsx # Componente React wrapper
376
+ ├── phone-lib-react.js # Versión CommonJS/ESM del wrapper React
377
+ ├── demo.html # Ejemplo básico Vanilla JS
378
+ ├── demo-separated.html # Ejemplo layout separado
379
+ ├── demo-all-layouts.html # Comparación de layouts
380
+ ├── demo-features.html # Nuevas características
381
+ ├── demo-react.html # Ejemplo React (demo)
382
+ ├── package.json # Dependencias
383
+ └── README.md # Documentación
384
+ ```
385
+
386
+ ## Nuevas Características (v2.0)
387
+
388
+ ### Eventos y Callbacks
389
+ ```javascript
390
+ const phoneLib = new PhoneLib('#phone-container', {
391
+ onCountryChange: (country, dialCode, countryName) => {
392
+ console.log('País cambiado:', country, dialCode);
393
+ },
394
+ onPhoneChange: (phoneNumber, isValid, country) => {
395
+ console.log('Número cambiado:', phoneNumber);
396
+ },
397
+ onValidationChange: (isValid, phoneNumber) => {
398
+ console.log('Validación:', isValid);
399
+ },
400
+ onFocus: () => console.log('Input enfocado'),
401
+ onBlur: () => console.log('Input perdió foco')
402
+ });
403
+ ```
404
+
405
+ ### Métodos de Control Programático
406
+ ```javascript
407
+ phoneLib.setCountry('ES'); // Establecer país
408
+ phoneLib.setPhoneNumber('600123456'); // Establecer número
409
+ phoneLib.setValue('ES', '600123456'); // Establecer ambos
410
+ phoneLib.enable(); // Habilitar
411
+ phoneLib.disable(); // Deshabilitar
412
+ phoneLib.reset(); // Resetear
413
+ phoneLib.destroy(); // Destruir instancia
414
+ ```
415
+
416
+ ### Detección Automática de País
417
+ ```javascript
418
+ const phoneLib = new PhoneLib('#phone-container', {
419
+ autoDetectCountry: true // Detecta país automáticamente al ingresar +34...
420
+ });
421
+ ```
422
+
423
+ ### Filtrado de Países
424
+ ```javascript
425
+ const phoneLib = new PhoneLib('#phone-container', {
426
+ onlyCountries: ['CO', 'US', 'ES'], // Solo estos países
427
+ disabledCountries: ['CU', 'KP'], // Deshabilitar estos
428
+ excludeCountries: ['XX'] // Excluir estos
429
+ });
430
+ ```
431
+
432
+ ### Modo Readonly/Disabled
433
+ ```javascript
434
+ const phoneLib = new PhoneLib('#phone-container', {
435
+ readonly: true, // Solo lectura
436
+ disabled: false // Deshabilitado
437
+ });
438
+ ```
439
+
440
+ ### Métodos Adicionales
441
+ ```javascript
442
+ phoneLib.formatNational(); // Formato nacional
443
+ phoneLib.formatRFC3966(); // Formato RFC3966
444
+ phoneLib.getNumberType(); // Tipo (MOBILE, FIXED_LINE, etc.)
445
+ phoneLib.getInfo(); // Objeto completo con toda la info
446
+ phoneLib.getCountryMetadata(); // Metadata del país
447
+ ```
448
+
449
+ ## Dependencias
450
+
451
+ - `libphonenumber-js`: Para validación y formato de números telefónicos
452
+
453
+ ## Licencia
454
+
455
+ MIT
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@jacksonavila/phone-lib",
3
+ "version": "2.0.0",
4
+ "description": "Librería JavaScript para input de teléfono con selector de país y banderas - Compatible con Vanilla JS y React",
5
+ "main": "phone-lib.js",
6
+ "module": "phone-lib.js",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./phone-lib.js",
11
+ "require": "./phone-lib.js"
12
+ },
13
+ "./react": {
14
+ "import": "./phone-lib-react.jsx",
15
+ "require": "./phone-lib-react.js"
16
+ },
17
+ "./css": "./phone-lib.css"
18
+ },
19
+ "scripts": {
20
+ "serve": "npx http-server . -p 3004 -o",
21
+ "prepublishOnly": "echo 'Verificando archivos antes de publicar...' && node --check phone-lib.js"
22
+ },
23
+ "keywords": [
24
+ "phone",
25
+ "telephone",
26
+ "input",
27
+ "country",
28
+ "selector",
29
+ "flags",
30
+ "validation",
31
+ "libphonenumber",
32
+ "react",
33
+ "react-component",
34
+ "vanilla-js",
35
+ "phone-input",
36
+ "country-selector",
37
+ "international-phone"
38
+ ],
39
+ "author": "",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": ""
44
+ },
45
+ "bugs": {
46
+ "url": ""
47
+ },
48
+ "homepage": "",
49
+ "dependencies": {
50
+ "libphonenumber-js": "^1.11.0"
51
+ },
52
+ "peerDependencies": {
53
+ "react": ">=16.8.0",
54
+ "react-dom": ">=16.8.0"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "react": {
58
+ "optional": true
59
+ },
60
+ "react-dom": {
61
+ "optional": true
62
+ }
63
+ },
64
+ "files": [
65
+ "phone-lib.js",
66
+ "phone-lib.css",
67
+ "phone-lib-react.jsx",
68
+ "phone-lib-react.js",
69
+ "README.md"
70
+ ],
71
+ "engines": {
72
+ "node": ">=14.0.0"
73
+ }
74
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Versión CommonJS/ESM del wrapper React para PhoneLib
3
+ * Para usar con import/require estándar
4
+ */
5
+
6
+ import PhoneLib from './phone-lib.js';
7
+ import './phone-lib.css';
8
+
9
+ /**
10
+ * Hook de React para PhoneLib
11
+ * @param {Object} options - Opciones de configuración
12
+ * @param {string} options.containerId - ID del contenedor (opcional)
13
+ * @param {string} options.initialCountry - País inicial (default: 'US')
14
+ * @param {Array} options.preferredCountries - Países preferidos
15
+ * @param {boolean} options.showHint - Mostrar hint de validación
16
+ * @param {string} options.layout - 'integrated' o 'separated'
17
+ * @param {boolean} options.showDialCode - Mostrar código de marcación
18
+ * @param {Object} options.customClasses - Clases CSS personalizadas
19
+ * @param {Object} options.customStyles - Estilos inline personalizados
20
+ * @param {Function} options.onCountryChange - Callback cuando cambia el país
21
+ * @param {Function} options.onPhoneChange - Callback cuando cambia el número
22
+ * @param {Function} options.onValidationChange - Callback cuando cambia la validación
23
+ * @returns {Object} - Objeto con ref del contenedor y métodos de PhoneLib
24
+ */
25
+ export function usePhoneLib(options = {}) {
26
+ const containerRef = React.useRef(null);
27
+ const phoneLibRef = React.useRef(null);
28
+
29
+ React.useEffect(() => {
30
+ if (!containerRef.current) return;
31
+
32
+ phoneLibRef.current = new PhoneLib(containerRef.current, options);
33
+
34
+ return () => {
35
+ if (phoneLibRef.current) {
36
+ phoneLibRef.current.destroy();
37
+ phoneLibRef.current = null;
38
+ }
39
+ };
40
+ }, []);
41
+
42
+ React.useEffect(() => {
43
+ if (!phoneLibRef.current) return;
44
+ phoneLibRef.current.updateOptions(options);
45
+ }, [options]);
46
+
47
+ return {
48
+ containerRef,
49
+ phoneLib: phoneLibRef.current,
50
+ // Métodos de acceso rápido
51
+ getCountry: () => phoneLibRef.current?.getCountry(),
52
+ getDialCode: () => phoneLibRef.current?.getDialCode(),
53
+ getRaw: () => phoneLibRef.current?.getRaw(),
54
+ getE164: () => phoneLibRef.current?.getE164(),
55
+ isValid: () => phoneLibRef.current?.isValid(),
56
+ getInfo: () => phoneLibRef.current?.getInfo(),
57
+ setCountry: (iso2) => phoneLibRef.current?.setCountry(iso2),
58
+ setPhoneNumber: (number) => phoneLibRef.current?.setPhoneNumber(number),
59
+ reset: () => phoneLibRef.current?.reset()
60
+ };
61
+ }
62
+
63
+ export default PhoneLib;