@alexochihua/exos-library-components 2.25.18 → 2.25.19

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.
@@ -672,6 +672,57 @@ function calculateColorVariant(primaryColor, variant = null) {
672
672
  function sleep(ms) {
673
673
  return new Promise(resolve => setTimeout(resolve, ms));
674
674
  }
675
+ /**
676
+ * Valida un número de tarjeta usando el Algoritmo de Luhn.
677
+ *
678
+ * @memberof Mixins
679
+ * @param {string|number} cardNumber - El número a validar.
680
+ * @returns {boolean} Verdadero si es válido, falso en caso contrario.
681
+ * @example
682
+ * // Tarjeta de 16 dígitos válida
683
+ * console.log(
684
+ * "validateLuhn:",
685
+ * this.validateLuhn("4532015112830366")
686
+ * );
687
+ * //Como salida: validateLuhn: true
688
+ * @example
689
+ * // Tarjeta de 16 dígitos inválida
690
+ * console.log(
691
+ * "validateLuhn:",
692
+ * this.validateLuhn("4532015112830367")
693
+ * );
694
+ * //Como salida: validateLuhn: false
695
+ * @example
696
+ * // Funciona con espacios en blanco
697
+ * console.log(
698
+ * "validateLuhn:",
699
+ * this.validateLuhn("4532 0151 1283 0366")
700
+ * );
701
+ * //Como salida: validateLuhn: true
702
+ */
703
+ function validateLuhn(cardNumber) {
704
+ // 1. Convertir a string y eliminar espacios en blanco
705
+ let n = cardNumber.toString().replace(/\s+/g, "");
706
+ let suma = 0;
707
+ let debeDoblar = false;
708
+ // 2. Recorrer la cadena de derecha a izquierda
709
+ for (let i = n.length - 1; i >= 0; i--) {
710
+ let digito = parseInt(n.charAt(i), 10);
711
+ // Si la posición es "par" (empezando desde la derecha)
712
+ if (debeDoblar) {
713
+ digito *= 2;
714
+ // Si el resultado es mayor a 9, sumamos los dígitos (o restamos 9)
715
+ if (digito > 9) {
716
+ digito -= 9;
717
+ }
718
+ }
719
+ suma += digito;
720
+ // Alternamos el booleano para el siguiente dígito
721
+ debeDoblar = !debeDoblar;
722
+ }
723
+ // 3. El número es válido si la suma es múltiplo de 10
724
+ return suma % 10 === 0;
725
+ }
675
726
  const methods = {
676
727
  readContentTxt,
677
728
  getTypeFileFromExtension,
@@ -687,7 +738,8 @@ const methods = {
687
738
  calculateVariant1,
688
739
  calculateVariant,
689
740
  calculateColorVariant,
690
- sleep
741
+ sleep,
742
+ validateLuhn
691
743
  };
692
744
  exports["default"] = {
693
745
  methods: Object.assign({}, methods)
@@ -53107,10 +53159,496 @@ if (typeof (EInputvue_type_custom_index_0_blockType_docs_lang_md_default()) ===
53107
53159
  const EInput_exports_ = EInputvue_type_script_setup_true_lang_js;
53108
53160
 
53109
53161
  /* harmony default export */ var EInput = (EInput_exports_);
53162
+ ;// CONCATENATED MODULE: ./src/ui/components/inputMask/mask-presets.js
53163
+ /**
53164
+ * Presets de máscara para EInputMask
53165
+ *
53166
+ * Tipos de presets:
53167
+ * - 'fixed': Patrón estático con posiciones fijas (ej: teléfonos, NSS)
53168
+ * - 'dynamic': Patrón que se genera según la longitud del input (ej: tarjetas de crédito)
53169
+ * - 'numeric': Formateo numérico con separadores de miles, decimales y símbolos (ej: montos, porcentajes)
53170
+ */
53171
+
53172
+
53173
+
53174
+ /**
53175
+ * Preset para tarjetas de crédito
53176
+ * - Muestra los primeros 6 dígitos (BIN)
53177
+ * - Oculta los dígitos intermedios
53178
+ * - Muestra los últimos 4 dígitos
53179
+ * - Soporta tarjetas de 13 a 19 dígitos
53180
+ */
53181
+ const creditCardPreset = {
53182
+ name: 'credit-card',
53183
+ type: 'dynamic',
53184
+ visiblePrefix: 6,
53185
+ visibleSuffix: 4,
53186
+ hiddenChar: '*',
53187
+ separator: ' ',
53188
+ groupSize: 4,
53189
+ minLength: 13,
53190
+ maxLength: 19,
53191
+ allowedChars: /[0-9]/,
53192
+ /**
53193
+ * Genera el patrón dinámico basado en la longitud actual
53194
+ * @param {number} length - Longitud actual del valor
53195
+ * @param {object} options - Opciones de configuración
53196
+ * @returns {string} Patrón generado
53197
+ */
53198
+ generatePattern(length, options = {}) {
53199
+ const visiblePrefix = options.visiblePrefix ?? this.visiblePrefix;
53200
+ const visibleSuffix = options.visibleSuffix ?? this.visibleSuffix;
53201
+ const hiddenChar = options.hiddenChar ?? this.hiddenChar;
53202
+ if (length <= visiblePrefix + visibleSuffix) {
53203
+ // Si la longitud es menor o igual a prefix + suffix, mostrar todo
53204
+ return '#'.repeat(length);
53205
+ }
53206
+ const hiddenCount = length - visiblePrefix - visibleSuffix;
53207
+ return '#'.repeat(visiblePrefix) + hiddenChar.repeat(hiddenCount) + '#'.repeat(visibleSuffix);
53208
+ },
53209
+ /**
53210
+ * Formatea el valor para mostrar con separadores
53211
+ * @param {string} value - Valor sin formato
53212
+ * @param {string} pattern - Patrón a aplicar
53213
+ * @param {object} options - Opciones de configuración
53214
+ * @returns {object} { displayValue, realValue }
53215
+ */
53216
+ format(value, pattern, options = {}) {
53217
+ const separator = options.separator ?? this.separator;
53218
+ const groupSize = options.groupSize ?? this.groupSize;
53219
+ const hiddenChar = options.hiddenChar ?? this.hiddenChar;
53220
+ const cleanValue = value.replace(/[^0-9]/g, '');
53221
+ let displayValue = '';
53222
+ for (let i = 0; i < pattern.length && i < cleanValue.length; i++) {
53223
+ const patternChar = pattern[i];
53224
+ if (patternChar === '#') {
53225
+ displayValue += cleanValue[i];
53226
+ } else {
53227
+ displayValue += hiddenChar;
53228
+ }
53229
+ }
53230
+
53231
+ // Agregar separadores cada groupSize caracteres
53232
+ if (separator && groupSize) {
53233
+ displayValue = displayValue.replace(new RegExp(`(.{${groupSize}})(?=.)`, 'g'), `$1${separator}`);
53234
+ }
53235
+ return {
53236
+ displayValue,
53237
+ realValue: cleanValue
53238
+ };
53239
+ },
53240
+ /**
53241
+ * Valida si el valor es una tarjeta de crédito válida (algoritmo de Luhn)
53242
+ * Utiliza mixins.methods.validateLuhn de @alexochihua/exos-functionality
53243
+ * @param {string} value - Valor a validar
53244
+ * @returns {boolean}
53245
+ */
53246
+ validate(value) {
53247
+ const cleanValue = value.replace(/[^0-9]/g, '');
53248
+ if (cleanValue.length < this.minLength || cleanValue.length > this.maxLength) {
53249
+ return false;
53250
+ }
53251
+
53252
+ // Usar validateLuhn de la librería exos-functionality
53253
+ return mixins_default().methods.validateLuhn(cleanValue);
53254
+ }
53255
+ };
53256
+
53257
+ /**
53258
+ * Preset para teléfonos mexicanos
53259
+ * Formato: (##) ## ## ## ##
53260
+ */
53261
+ const phoneMxPreset = {
53262
+ name: 'phone-mx',
53263
+ type: 'fixed',
53264
+ pattern: '(##) ## ## ## ##',
53265
+ maxLength: 10,
53266
+ allowedChars: /[0-9]/,
53267
+ /**
53268
+ * Formatea el valor según el patrón fijo
53269
+ * @param {string} value - Valor sin formato
53270
+ * @returns {object} { displayValue, realValue }
53271
+ */
53272
+ format(value) {
53273
+ const cleanValue = value.replace(/[^0-9]/g, '').slice(0, this.maxLength);
53274
+ let displayValue = '';
53275
+ let digitIndex = 0;
53276
+ for (let i = 0; i < this.pattern.length && digitIndex < cleanValue.length; i++) {
53277
+ const patternChar = this.pattern[i];
53278
+ if (patternChar === '#') {
53279
+ displayValue += cleanValue[digitIndex];
53280
+ digitIndex++;
53281
+ } else {
53282
+ displayValue += patternChar;
53283
+ }
53284
+ }
53285
+ return {
53286
+ displayValue,
53287
+ realValue: cleanValue
53288
+ };
53289
+ },
53290
+ /**
53291
+ * Valida si el valor es un teléfono mexicano válido
53292
+ * @param {string} value - Valor a validar
53293
+ * @returns {boolean}
53294
+ */
53295
+ validate(value) {
53296
+ const cleanValue = value.replace(/[^0-9]/g, '');
53297
+ return cleanValue.length === this.maxLength;
53298
+ }
53299
+ };
53300
+
53301
+ /**
53302
+ * Preset para NSS (Número de Seguro Social mexicano)
53303
+ * Formato: ###-##-####
53304
+ */
53305
+ const nssPreset = {
53306
+ name: 'nss',
53307
+ type: 'fixed',
53308
+ pattern: '###-##-####',
53309
+ maxLength: 9,
53310
+ allowedChars: /[0-9]/,
53311
+ /**
53312
+ * Formatea el valor según el patrón fijo
53313
+ * @param {string} value - Valor sin formato
53314
+ * @returns {object} { displayValue, realValue }
53315
+ */
53316
+ format(value) {
53317
+ const cleanValue = value.replace(/[^0-9]/g, '').slice(0, this.maxLength);
53318
+ let displayValue = '';
53319
+ let digitIndex = 0;
53320
+ for (let i = 0; i < this.pattern.length && digitIndex < cleanValue.length; i++) {
53321
+ const patternChar = this.pattern[i];
53322
+ if (patternChar === '#') {
53323
+ displayValue += cleanValue[digitIndex];
53324
+ digitIndex++;
53325
+ } else {
53326
+ displayValue += patternChar;
53327
+ }
53328
+ }
53329
+ return {
53330
+ displayValue,
53331
+ realValue: cleanValue
53332
+ };
53333
+ },
53334
+ /**
53335
+ * Valida si el valor es un NSS válido
53336
+ * @param {string} value - Valor a validar
53337
+ * @returns {boolean}
53338
+ */
53339
+ validate(value) {
53340
+ const cleanValue = value.replace(/[^0-9]/g, '');
53341
+ return cleanValue.length === this.maxLength;
53342
+ }
53343
+ };
53344
+
53345
+ /**
53346
+ * Preset para montos/currency
53347
+ * Formato: $1,234,567.89
53348
+ */
53349
+ const currencyPreset = {
53350
+ name: 'currency',
53351
+ type: 'numeric',
53352
+ prefix: '$',
53353
+ suffix: '',
53354
+ decimals: 2,
53355
+ thousandsSeparator: ',',
53356
+ decimalSeparator: '.',
53357
+ allowNegative: false,
53358
+ allowedChars: /[0-9.]/,
53359
+ /**
53360
+ * Formatea el valor como moneda
53361
+ * @param {string} value - Valor sin formato
53362
+ * @param {object} options - Opciones de configuración
53363
+ * @returns {object} { displayValue, realValue }
53364
+ */
53365
+ format(value, options = {}) {
53366
+ const prefix = options.prefix ?? options.currencySymbol ?? this.prefix;
53367
+ const suffix = options.suffix ?? this.suffix;
53368
+ const decimals = options.decimals ?? this.decimals;
53369
+ const thousandsSeparator = options.thousandsSeparator ?? this.thousandsSeparator;
53370
+ const decimalSeparator = options.decimalSeparator ?? this.decimalSeparator;
53371
+
53372
+ // Limpiar el valor, permitiendo solo números y punto decimal
53373
+ let cleanValue = value.replace(/[^0-9.]/g, '');
53374
+
53375
+ // Si el valor está vacío, retornar vacío (no forzar $0)
53376
+ if (!cleanValue || cleanValue === '') {
53377
+ return {
53378
+ displayValue: '',
53379
+ realValue: ''
53380
+ };
53381
+ }
53382
+
53383
+ // Asegurar solo un punto decimal
53384
+ const parts = cleanValue.split('.');
53385
+ if (parts.length > 2) {
53386
+ cleanValue = parts[0] + '.' + parts.slice(1).join('');
53387
+ }
53388
+
53389
+ // Separar parte entera y decimal
53390
+ let [integerPart, decimalPart] = cleanValue.split('.');
53391
+
53392
+ // Remover ceros al inicio solo si hay más dígitos después
53393
+ // Esto preserva "0" cuando es el único dígito (ej: "0" o "0.25")
53394
+ if (integerPart === '') {
53395
+ // Si está vacío y no hay decimales, retornar vacío
53396
+ if (decimalPart === undefined) {
53397
+ return {
53398
+ displayValue: '',
53399
+ realValue: ''
53400
+ };
53401
+ }
53402
+ // Hay decimales, usar '0'
53403
+ integerPart = '0';
53404
+ } else {
53405
+ // Remover ceros extras solo si hay otros dígitos (ej: "00123" → "123", pero "0" → "0")
53406
+ integerPart = integerPart.replace(/^0+(?=\d)/, '');
53407
+ }
53408
+
53409
+ // Limitar decimales
53410
+ if (decimalPart !== undefined) {
53411
+ decimalPart = decimalPart.slice(0, decimals);
53412
+ }
53413
+
53414
+ // Formatear parte entera con separadores de miles
53415
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator);
53416
+
53417
+ // Construir valor de display
53418
+ let displayValue = prefix + formattedInteger;
53419
+ if (decimalPart !== undefined) {
53420
+ displayValue += decimalSeparator + decimalPart;
53421
+ }
53422
+ displayValue += suffix;
53423
+
53424
+ // Construir valor real (número sin formato)
53425
+ let realValue = integerPart;
53426
+ if (decimalPart !== undefined) {
53427
+ realValue += '.' + decimalPart;
53428
+ }
53429
+ return {
53430
+ displayValue,
53431
+ realValue
53432
+ };
53433
+ },
53434
+ /**
53435
+ * Parsea el valor de display a número
53436
+ * @param {string} displayValue - Valor con formato
53437
+ * @param {object} options - Opciones de configuración
53438
+ * @returns {string} Valor numérico
53439
+ */
53440
+ parse(displayValue, options = {}) {
53441
+ const prefix = options.prefix ?? options.currencySymbol ?? this.prefix;
53442
+ const suffix = options.suffix ?? this.suffix;
53443
+ const thousandsSeparator = options.thousandsSeparator ?? this.thousandsSeparator;
53444
+ let value = displayValue;
53445
+
53446
+ // Remover prefijo y sufijo
53447
+ if (prefix && value.startsWith(prefix)) {
53448
+ value = value.slice(prefix.length);
53449
+ }
53450
+ if (suffix && value.endsWith(suffix)) {
53451
+ value = value.slice(0, -suffix.length);
53452
+ }
53453
+
53454
+ // Remover separadores de miles
53455
+ value = value.replace(new RegExp(`\\${thousandsSeparator}`, 'g'), '');
53456
+ return value;
53457
+ },
53458
+ /**
53459
+ * Valida si el valor es un monto válido
53460
+ * @param {string} value - Valor a validar
53461
+ * @returns {boolean}
53462
+ */
53463
+ validate(value) {
53464
+ const cleanValue = value.replace(/[^0-9.]/g, '');
53465
+ return !isNaN(parseFloat(cleanValue)) && isFinite(cleanValue);
53466
+ }
53467
+ };
53468
+
53469
+ /**
53470
+ * Preset para porcentajes
53471
+ * Formato: 12.34%
53472
+ */
53473
+ const percentagePreset = {
53474
+ name: 'percentage',
53475
+ type: 'numeric',
53476
+ prefix: '',
53477
+ suffix: '%',
53478
+ decimals: 2,
53479
+ thousandsSeparator: ',',
53480
+ decimalSeparator: '.',
53481
+ allowNegative: false,
53482
+ maxValue: null,
53483
+ // null = sin límite, o número para limitar (ej: 100)
53484
+ allowedChars: /[0-9.]/,
53485
+ /**
53486
+ * Formatea el valor como porcentaje
53487
+ * @param {string} value - Valor sin formato
53488
+ * @param {object} options - Opciones de configuración
53489
+ * @returns {object} { displayValue, realValue }
53490
+ */
53491
+ format(value, options = {}) {
53492
+ const prefix = options.prefix ?? this.prefix;
53493
+ const suffix = options.suffix ?? this.suffix;
53494
+ const decimals = options.decimals ?? this.decimals;
53495
+ const thousandsSeparator = options.thousandsSeparator ?? this.thousandsSeparator;
53496
+ const decimalSeparator = options.decimalSeparator ?? this.decimalSeparator;
53497
+
53498
+ // Limpiar el valor, permitiendo solo números y punto decimal
53499
+ let cleanValue = value.replace(/[^0-9.]/g, '');
53500
+
53501
+ // Si el valor está vacío, retornar vacío (no forzar 0%)
53502
+ if (!cleanValue || cleanValue === '') {
53503
+ return {
53504
+ displayValue: '',
53505
+ realValue: ''
53506
+ };
53507
+ }
53508
+
53509
+ // Asegurar solo un punto decimal
53510
+ const parts = cleanValue.split('.');
53511
+ if (parts.length > 2) {
53512
+ cleanValue = parts[0] + '.' + parts.slice(1).join('');
53513
+ }
53514
+
53515
+ // Separar parte entera y decimal
53516
+ let [integerPart, decimalPart] = cleanValue.split('.');
53517
+
53518
+ // Remover ceros al inicio solo si hay más dígitos después
53519
+ // Esto preserva "0" cuando es el único dígito (ej: "0" o "0.25")
53520
+ if (integerPart === '') {
53521
+ // Si está vacío y no hay decimales, retornar vacío
53522
+ if (decimalPart === undefined) {
53523
+ return {
53524
+ displayValue: '',
53525
+ realValue: ''
53526
+ };
53527
+ }
53528
+ // Hay decimales, usar '0'
53529
+ integerPart = '0';
53530
+ } else {
53531
+ // Remover ceros extras solo si hay otros dígitos (ej: "00123" → "123", pero "0" → "0")
53532
+ integerPart = integerPart.replace(/^0+(?=\d)/, '');
53533
+ }
53534
+
53535
+ // Limitar decimales
53536
+ if (decimalPart !== undefined) {
53537
+ decimalPart = decimalPart.slice(0, decimals);
53538
+ }
53539
+
53540
+ // Formatear parte entera con separadores de miles (si es necesario)
53541
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator);
53542
+
53543
+ // Construir valor de display
53544
+ let displayValue = prefix + formattedInteger;
53545
+ if (decimalPart !== undefined) {
53546
+ displayValue += decimalSeparator + decimalPart;
53547
+ }
53548
+ displayValue += suffix;
53549
+
53550
+ // Construir valor real (número sin formato)
53551
+ let realValue = integerPart;
53552
+ if (decimalPart !== undefined) {
53553
+ realValue += '.' + decimalPart;
53554
+ }
53555
+ return {
53556
+ displayValue,
53557
+ realValue
53558
+ };
53559
+ },
53560
+ /**
53561
+ * Parsea el valor de display a número
53562
+ * @param {string} displayValue - Valor con formato
53563
+ * @param {object} options - Opciones de configuración
53564
+ * @returns {string} Valor numérico
53565
+ */
53566
+ parse(displayValue, options = {}) {
53567
+ const prefix = options.prefix ?? this.prefix;
53568
+ const suffix = options.suffix ?? this.suffix;
53569
+ const thousandsSeparator = options.thousandsSeparator ?? this.thousandsSeparator;
53570
+ let value = displayValue;
53571
+
53572
+ // Remover prefijo y sufijo
53573
+ if (prefix && value.startsWith(prefix)) {
53574
+ value = value.slice(prefix.length);
53575
+ }
53576
+ if (suffix && value.endsWith(suffix)) {
53577
+ value = value.slice(0, -suffix.length);
53578
+ }
53579
+
53580
+ // Remover separadores de miles
53581
+ value = value.replace(new RegExp(`\\${thousandsSeparator}`, 'g'), '');
53582
+ return value;
53583
+ },
53584
+ /**
53585
+ * Valida si el valor es un porcentaje válido
53586
+ * @param {string} value - Valor a validar
53587
+ * @returns {boolean}
53588
+ */
53589
+ validate(value) {
53590
+ const cleanValue = value.replace(/[^0-9.]/g, '');
53591
+ const num = parseFloat(cleanValue);
53592
+ return !isNaN(num) && isFinite(num);
53593
+ }
53594
+ };
53595
+
53596
+ /**
53597
+ * Mapa de todos los presets disponibles
53598
+ */
53599
+ const PRESETS = {
53600
+ 'credit-card': creditCardPreset,
53601
+ 'phone-mx': phoneMxPreset,
53602
+ nss: nssPreset,
53603
+ currency: currencyPreset,
53604
+ percentage: percentagePreset
53605
+ };
53606
+
53607
+ /**
53608
+ * Obtiene un preset por nombre
53609
+ * @param {string} name - Nombre del preset
53610
+ * @returns {object|null} Preset o null si no existe
53611
+ */
53612
+ function getPreset(name) {
53613
+ return PRESETS[name] || null;
53614
+ }
53615
+
53616
+ /**
53617
+ * Registra un nuevo preset personalizado
53618
+ * @param {string} name - Nombre del preset
53619
+ * @param {object} preset - Configuración del preset
53620
+ */
53621
+ function registerPreset(name, preset) {
53622
+ PRESETS[name] = {
53623
+ name,
53624
+ ...preset
53625
+ };
53626
+ }
53627
+
53628
+ /**
53629
+ * Lista todos los presets disponibles
53630
+ * @returns {string[]} Array con los nombres de los presets
53631
+ */
53632
+ function listPresets() {
53633
+ return Object.keys(PRESETS);
53634
+ }
53635
+ /* harmony default export */ var mask_presets = ({
53636
+ PRESETS,
53637
+ getPreset,
53638
+ registerPreset,
53639
+ listPresets
53640
+ });
53110
53641
  ;// CONCATENATED MODULE: ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/ui/components/inputMask/EInputMask.vue?vue&type=script&setup=true&lang=js
53111
53642
 
53112
53643
 
53113
- const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53644
+
53645
+
53646
+ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = {
53647
+ class: "e-input-mask__container"
53648
+ };
53649
+ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_2 = ["id", "value"];
53650
+
53651
+
53114
53652
 
53115
53653
 
53116
53654
  /* harmony default export */ var EInputMaskvue_type_script_setup_true_lang_js = ({
@@ -53140,6 +53678,149 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53140
53678
  hiddenChar: {
53141
53679
  type: String,
53142
53680
  default: '*'
53681
+ },
53682
+ /**
53683
+ * Preset predefinido para tipos comunes de datos
53684
+ * @values 'credit-card' - Tarjeta de crédito (13-19 dígitos, primeros 6 y últimos 4 visibles)
53685
+ * @values 'phone-mx' - Teléfono mexicano (##) ## ## ## ##
53686
+ * @values 'nss' - Número de Seguro Social ###-##-####
53687
+ * @values 'currency' - Monto con símbolo $ y separadores de miles $1,234,567.89
53688
+ * @values 'percentage' - Porcentaje con símbolo % 12.34%
53689
+ */
53690
+ preset: {
53691
+ type: String,
53692
+ default: null
53693
+ },
53694
+ /**
53695
+ * Si es true, el v-model contendrá el valor formateado, si es false, solo los caracteres ingresados
53696
+ * @values true - v-model: "(55) 12 34 56 78" o "$1,234.56"
53697
+ * @values false - v-model: "5512345678" o "1234.56"
53698
+ */
53699
+ saveFormatted: {
53700
+ type: Boolean,
53701
+ default: false
53702
+ },
53703
+ /**
53704
+ * Override para la cantidad de dígitos visibles al inicio en presets dinámicos (credit-card)
53705
+ * @values 6 - por defecto para tarjetas de crédito
53706
+ */
53707
+ visiblePrefix: {
53708
+ type: Number,
53709
+ default: null
53710
+ },
53711
+ /**
53712
+ * Override para la cantidad de dígitos visibles al final en presets dinámicos (credit-card)
53713
+ * @values 4 - por defecto para tarjetas de crédito
53714
+ */
53715
+ visibleSuffix: {
53716
+ type: Number,
53717
+ default: null
53718
+ },
53719
+ /**
53720
+ * Override para la cantidad de decimales en presets numéricos (currency, percentage)
53721
+ * @values 2 - por defecto
53722
+ */
53723
+ decimals: {
53724
+ type: Number,
53725
+ default: null
53726
+ },
53727
+ /**
53728
+ * Override para el símbolo de moneda en preset currency
53729
+ * @values '$' - por defecto, '€', 'MXN', etc.
53730
+ */
53731
+ currencySymbol: {
53732
+ type: String,
53733
+ default: null
53734
+ },
53735
+ /**
53736
+ * Longitudes válidas para tarjetas de crédito (preset credit-card)
53737
+ * @values [15, 16, 19] - por defecto acepta tarjetas de 15 (Amex), 16 (Visa/MC) o 19 dígitos
53738
+ * @values [16] - solo tarjetas de 16 dígitos
53739
+ * @values [15, 16] - Amex y Visa/MC
53740
+ */
53741
+ cardLengths: {
53742
+ type: Array,
53743
+ default: () => [15, 16, 19]
53744
+ },
53745
+ /**
53746
+ * Mensaje de error cuando la tarjeta de crédito no es válida (algoritmo de Luhn)
53747
+ * @values 'El número de tarjeta no es válido' - mensaje por defecto
53748
+ */
53749
+ invalidCardMessage: {
53750
+ type: String,
53751
+ default: 'El número de tarjeta no es válido'
53752
+ },
53753
+ /**
53754
+ * Mensaje de error cuando la longitud de la tarjeta no es válida. Usa %lengths% como placeholder
53755
+ * @values 'El número debe tener %lengths% dígitos' - mensaje por defecto
53756
+ */
53757
+ invalidCardLengthMessage: {
53758
+ type: String,
53759
+ default: 'El número debe tener %lengths% dígitos'
53760
+ },
53761
+ /**
53762
+ * Indica si el campo es requerido
53763
+ * @values true - campo requerido, false - campo opcional
53764
+ */
53765
+ required: {
53766
+ type: Boolean,
53767
+ default: false
53768
+ },
53769
+ /**
53770
+ * Reglas de validación personalizadas
53771
+ * @values [(val) => !!val || 'Campo requerido'] - validación requerido
53772
+ */
53773
+ rules: {
53774
+ type: Array,
53775
+ default: () => []
53776
+ },
53777
+ /**
53778
+ * Etiqueta del input (usada en mensajes de error)
53779
+ * @values 'Número de tarjeta' - etiqueta del campo
53780
+ */
53781
+ label: {
53782
+ type: String,
53783
+ default: null
53784
+ },
53785
+ /**
53786
+ * Mensaje de error para campo requerido. Usa %label% como placeholder
53787
+ * @values 'El campo %label% es requerido' - mensaje por defecto
53788
+ */
53789
+ requiredMessage: {
53790
+ type: String,
53791
+ default: 'El campo %label% es requerido'
53792
+ },
53793
+ /**
53794
+ * Mensaje de error cuando el teléfono no tiene 10 dígitos (preset phone-mx)
53795
+ * @values 'El número de teléfono debe tener 10 dígitos' - mensaje por defecto
53796
+ */
53797
+ invalidPhoneMessage: {
53798
+ type: String,
53799
+ default: 'El número de teléfono debe tener 10 dígitos'
53800
+ },
53801
+ /**
53802
+ * Mensaje de error cuando el NSS no tiene 9 dígitos (preset nss)
53803
+ * @values 'El NSS debe tener 9 dígitos' - mensaje por defecto
53804
+ */
53805
+ invalidNssMessage: {
53806
+ type: String,
53807
+ default: 'El NSS debe tener 9 dígitos'
53808
+ },
53809
+ /**
53810
+ * Mensaje de error cuando el monto no es válido (preset currency)
53811
+ * @values 'El monto no es válido' - mensaje por defecto
53812
+ */
53813
+ invalidCurrencyMessage: {
53814
+ type: String,
53815
+ default: 'El monto no es válido'
53816
+ },
53817
+ /**
53818
+ * Mensaje de error cuando el porcentaje no es válido (preset percentage)
53819
+ * @values 'El porcentaje no es válido' - mensaje por defecto
53820
+ */
53821
+ invalidPercentageMessage: {
53822
+ type: String,
53823
+ default: 'El porcentaje no es válido'
53143
53824
  }
53144
53825
  },
53145
53826
  emits: ['update:modelValue', 'clean', 'onEnterValid', 'onEnter'],
@@ -53159,22 +53840,148 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53159
53840
  const displayValue = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.ref)('');
53160
53841
  const realValue = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.ref)(props.modelValue || '');
53161
53842
  const isCleaning = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.ref)(false);
53843
+ const lastValidationResult = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.ref)(true);
53844
+
53845
+ // Obtener el preset activo si existe
53846
+ const activePreset = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.computed)(() => {
53847
+ if (!props.preset) return null;
53848
+ return getPreset(props.preset);
53849
+ });
53162
53850
 
53163
- // Computed para verificar si la máscara está activa
53851
+ // Computed para verificar si la máscara está activa (por patrón o por preset)
53164
53852
  const isMaskActive = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.computed)(() => {
53165
- return !!props.maskPattern;
53853
+ return !!props.maskPattern || !!activePreset.value;
53854
+ });
53855
+
53856
+ // Computed para verificar si es un preset numérico
53857
+ const isNumericPreset = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.computed)(() => {
53858
+ return activePreset.value?.type === 'numeric';
53859
+ });
53860
+
53861
+ // Computed para verificar si es un preset dinámico
53862
+ const isDynamicPreset = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.computed)(() => {
53863
+ return activePreset.value?.type === 'dynamic';
53864
+ });
53865
+
53866
+ // Computed para verificar si es un preset fijo
53867
+ const isFixedPreset = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.computed)(() => {
53868
+ return activePreset.value?.type === 'fixed';
53869
+ });
53870
+
53871
+ // Función helper para extraer el valor limpio del displayValue
53872
+ const extractCleanValue = displayVal => {
53873
+ if (!displayVal) return '';
53874
+ if (isNumericPreset.value) {
53875
+ // Para currency/percentage, extraer números y punto decimal
53876
+ return displayVal.replace(/[^0-9.]/g, '');
53877
+ }
53878
+ // Para otros casos, extraer solo dígitos
53879
+ return displayVal.replace(/[^0-9]/g, '');
53880
+ };
53881
+
53882
+ // Computed para las reglas que se pasan a EInput (reutiliza el sistema de validación de EInput)
53883
+ const inputRules = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.computed)(() => {
53884
+ let rules = [];
53885
+
53886
+ // 1. Validación de required
53887
+ if (props.required) {
53888
+ rules.push(val => {
53889
+ const cleanVal = extractCleanValue(val);
53890
+ return !!cleanVal || props.requiredMessage.replace('%label%', props.label || '');
53891
+ });
53892
+ }
53893
+
53894
+ // 2. Reglas personalizadas del usuario (transformadas para usar valor limpio)
53895
+ props.rules.forEach(rule => {
53896
+ rules.push(val => {
53897
+ const cleanVal = extractCleanValue(val);
53898
+ return rule(cleanVal);
53899
+ });
53900
+ });
53901
+
53902
+ // 3. Validaciones de presets
53903
+ if (activePreset.value) {
53904
+ const presetName = activePreset.value.name;
53905
+ if (presetName === 'credit-card') {
53906
+ rules.push(() => {
53907
+ // Para credit-card, usar realValue directamente ya que displayValue tiene asteriscos
53908
+ const cleanVal = realValue.value || '';
53909
+ if (!cleanVal) return true; // Vacío es válido (required lo maneja)
53910
+
53911
+ // Validar longitud
53912
+ const validLengths = props.cardLengths || [15, 16, 19];
53913
+ if (!validLengths.includes(cleanVal.length)) {
53914
+ const lengthsStr = validLengths.join(', ').replace(/,([^,]*)$/, ' o$1');
53915
+ return props.invalidCardLengthMessage.replace('%lengths%', lengthsStr);
53916
+ }
53917
+
53918
+ // Validar Luhn solo si la longitud es correcta
53919
+ return mixins_default().methods.validateLuhn(cleanVal) || props.invalidCardMessage;
53920
+ });
53921
+ } else if (presetName === 'phone-mx') {
53922
+ rules.push(val => {
53923
+ const cleanVal = val ? val.replace(/[^0-9]/g, '') : '';
53924
+ if (!cleanVal) return true;
53925
+ return cleanVal.length === 10 || props.invalidPhoneMessage;
53926
+ });
53927
+ } else if (presetName === 'nss') {
53928
+ rules.push(val => {
53929
+ const cleanVal = val ? val.replace(/[^0-9]/g, '') : '';
53930
+ if (!cleanVal) return true;
53931
+ return cleanVal.length === 9 || props.invalidNssMessage;
53932
+ });
53933
+ } else if (presetName === 'currency') {
53934
+ rules.push(val => {
53935
+ const cleanVal = val ? val.replace(/[^0-9.]/g, '') : '';
53936
+ if (!cleanVal) return true;
53937
+ return !isNaN(parseFloat(cleanVal)) && isFinite(cleanVal) || props.invalidCurrencyMessage;
53938
+ });
53939
+ } else if (presetName === 'percentage') {
53940
+ rules.push(val => {
53941
+ const cleanVal = val ? val.replace(/[^0-9.]/g, '') : '';
53942
+ if (!cleanVal) return true;
53943
+ return !isNaN(parseFloat(cleanVal)) && isFinite(cleanVal) || props.invalidPercentageMessage;
53944
+ });
53945
+ }
53946
+ }
53947
+ return rules;
53948
+ });
53949
+
53950
+ // Opciones de configuración para el preset activo
53951
+ const presetOptions = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.computed)(() => {
53952
+ if (!activePreset.value) return {};
53953
+ return {
53954
+ visiblePrefix: props.visiblePrefix,
53955
+ visibleSuffix: props.visibleSuffix,
53956
+ hiddenChar: props.hiddenChar,
53957
+ decimals: props.decimals,
53958
+ currencySymbol: props.currencySymbol
53959
+ };
53960
+ });
53961
+
53962
+ // Computed para calcular el maxlength dinámico según el preset
53963
+ const inputLength = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.computed)(() => {
53964
+ if (activePreset.value?.name === 'credit-card') {
53965
+ // Usar el valor máximo de cardLengths + separadores
53966
+ const maxLength = Math.max(...props.cardLengths);
53967
+ // Calcular separadores: si groupSize=4, entonces cada 4 dígitos hay un separador
53968
+ const separators = Math.floor((maxLength - 1) / 4); // ej: 19 dígitos = 4 separadores
53969
+ return maxLength + separators;
53970
+ }
53971
+ return null; // Sin límite para otros presets
53166
53972
  });
53167
53973
 
53168
53974
  // Props que se pasan a EInput (excluyendo los propios de EInputMask)
53169
- // Excluimos maskPattern y hiddenChar para evitar pasarlos a EInput
53975
+ // Excluimos todas las props de EInputMask para evitar pasarlas a EInput
53976
+ const excludedProps = ['maskPattern', 'hiddenChar', 'preset', 'saveFormatted', 'visiblePrefix', 'visibleSuffix', 'decimals', 'currencySymbol', 'cardLengths', 'invalidCardMessage', 'invalidCardLengthMessage', 'required', 'rules', 'label', 'requiredMessage', 'invalidPhoneMessage', 'invalidNssMessage', 'invalidCurrencyMessage', 'invalidPercentageMessage'];
53170
53977
  const inputProps = (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.computed)(() => {
53171
- // eslint-disable-next-line no-unused-vars -- Variables desestructuradas intencionalmente para excluirlas de attrs
53172
- const {
53173
- maskPattern,
53174
- hiddenChar,
53175
- ...rest
53176
- } = attrs;
53177
- return rest;
53978
+ const result = {};
53979
+ for (const key in attrs) {
53980
+ if (!excludedProps.includes(key)) {
53981
+ result[key] = attrs[key];
53982
+ }
53983
+ }
53984
+ return result;
53178
53985
  });
53179
53986
 
53180
53987
  // Obtener el input nativo del componente EInput
@@ -53269,6 +54076,116 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53269
54076
  realValue: cleanValue
53270
54077
  };
53271
54078
  };
54079
+
54080
+ // ===== FUNCIONES DE PRESET DINÁMICO (CREDIT CARD) =====
54081
+ const applyDynamicPresetMask = value => {
54082
+ const preset = activePreset.value;
54083
+ if (!preset || preset.type !== 'dynamic') {
54084
+ return {
54085
+ displayValue: value,
54086
+ realValue: value
54087
+ };
54088
+ }
54089
+ const cleanValue = value.replace(/[^0-9]/g, '');
54090
+ // Usar el máximo de cardLengths en lugar de un valor hardcodeado
54091
+ const maxLength = Math.max(...props.cardLengths);
54092
+ const truncatedValue = cleanValue.slice(0, maxLength);
54093
+
54094
+ // Generar patrón dinámico basado en la longitud
54095
+ const pattern = preset.generatePattern(truncatedValue.length, presetOptions.value);
54096
+
54097
+ // Aplicar el patrón
54098
+ const hiddenChar = props.hiddenChar || preset.hiddenChar || '*';
54099
+ let displayValue = '';
54100
+ for (let i = 0; i < truncatedValue.length; i++) {
54101
+ if (pattern[i] === '#') {
54102
+ displayValue += truncatedValue[i];
54103
+ } else {
54104
+ displayValue += hiddenChar;
54105
+ }
54106
+ }
54107
+
54108
+ // Agregar separadores cada groupSize caracteres
54109
+ const separator = preset.separator || ' ';
54110
+ const groupSize = preset.groupSize || 4;
54111
+ if (separator && groupSize && displayValue.length > 0) {
54112
+ displayValue = displayValue.replace(new RegExp(`(.{${groupSize}})(?=.)`, 'g'), `$1${separator}`);
54113
+ }
54114
+ return {
54115
+ displayValue,
54116
+ realValue: truncatedValue
54117
+ };
54118
+ };
54119
+
54120
+ // ===== FUNCIONES DE PRESET FIJO (PHONE, NSS) =====
54121
+ const applyFixedPresetMask = value => {
54122
+ const preset = activePreset.value;
54123
+ if (!preset || preset.type !== 'fixed') {
54124
+ return {
54125
+ displayValue: value,
54126
+ realValue: value
54127
+ };
54128
+ }
54129
+ const result = preset.format(value);
54130
+ return {
54131
+ displayValue: result.displayValue,
54132
+ realValue: result.realValue
54133
+ };
54134
+ };
54135
+
54136
+ // ===== FUNCIONES DE PRESET NUMÉRICO (CURRENCY, PERCENTAGE) =====
54137
+ const applyNumericPresetMask = value => {
54138
+ const preset = activePreset.value;
54139
+ if (!preset || preset.type !== 'numeric') {
54140
+ return {
54141
+ displayValue: value,
54142
+ realValue: value
54143
+ };
54144
+ }
54145
+ const result = preset.format(value, presetOptions.value);
54146
+ return {
54147
+ displayValue: result.displayValue,
54148
+ realValue: result.realValue
54149
+ };
54150
+ };
54151
+
54152
+ // ===== FUNCIÓN UNIFICADA PARA APLICAR MÁSCARA =====
54153
+ const applyMask = value => {
54154
+ if (!value && value !== '0') {
54155
+ return {
54156
+ displayValue: '',
54157
+ realValue: ''
54158
+ };
54159
+ }
54160
+
54161
+ // Si hay un preset activo, usarlo
54162
+ if (activePreset.value) {
54163
+ if (isDynamicPreset.value) {
54164
+ return applyDynamicPresetMask(value);
54165
+ } else if (isFixedPreset.value) {
54166
+ return applyFixedPresetMask(value);
54167
+ } else if (isNumericPreset.value) {
54168
+ return applyNumericPresetMask(value);
54169
+ }
54170
+ }
54171
+
54172
+ // Si no hay preset, usar el patrón personalizado
54173
+ if (props.maskPattern) {
54174
+ return applyPatternMask(value, props.maskPattern);
54175
+ }
54176
+ return {
54177
+ displayValue: value,
54178
+ realValue: value
54179
+ };
54180
+ };
54181
+
54182
+ // ===== FUNCIÓN PARA OBTENER EL VALOR A EMITIR =====
54183
+ const getEmitValue = (realVal, displayVal) => {
54184
+ if (props.saveFormatted) {
54185
+ return displayVal;
54186
+ }
54187
+ return realVal;
54188
+ };
53272
54189
  const handlePatternInput = (currentDisplay, realValue, cursorPosition, pattern) => {
53273
54190
  if (!pattern) {
53274
54191
  return {
@@ -53457,10 +54374,34 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53457
54374
  }
53458
54375
  const nativeInput = getNativeInput();
53459
54376
  const cursorPosition = nativeInput?.selectionStart ?? value.length;
53460
- const result = handlePatternInput(value, realValue.value, cursorPosition, props.maskPattern);
54377
+ let result;
54378
+
54379
+ // Determinar qué tipo de máscara aplicar
54380
+ if (activePreset.value) {
54381
+ if (isDynamicPreset.value) {
54382
+ // Para presets dinámicos (credit-card), calcular posición del cursor
54383
+ result = handleDynamicPresetInput(value, cursorPosition);
54384
+ } else if (isFixedPreset.value) {
54385
+ // Para presets fijos (phone, nss), calcular posición del cursor
54386
+ result = handleFixedPresetInput(value, cursorPosition);
54387
+ } else if (isNumericPreset.value) {
54388
+ // Para presets numéricos (currency, percentage)
54389
+ result = handleNumericPresetInput(value, cursorPosition);
54390
+ }
54391
+ } else if (props.maskPattern) {
54392
+ // Usar el patrón personalizado existente
54393
+ result = handlePatternInput(value, realValue.value, cursorPosition, props.maskPattern);
54394
+ }
54395
+ if (!result) {
54396
+ result = {
54397
+ displayValue: value,
54398
+ realValue: value,
54399
+ cursorPosition: cursorPosition
54400
+ };
54401
+ }
53461
54402
  realValue.value = result.realValue;
53462
54403
  displayValue.value = result.displayValue;
53463
- emit('update:modelValue', result.realValue);
54404
+ emit('update:modelValue', getEmitValue(result.realValue, result.displayValue));
53464
54405
 
53465
54406
  // Usar doble nextTick para asegurar que el DOM se haya actualizado completamente
53466
54407
  // (similar a cómo EInput maneja el cursor después de actualizar el valor)
@@ -53479,8 +54420,205 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53479
54420
  });
53480
54421
  });
53481
54422
  };
54423
+
54424
+ // ===== MANEJO DE INPUT PARA PRESET DINÁMICO =====
54425
+ const handleDynamicPresetInput = (value, cursorPosition) => {
54426
+ const preset = activePreset.value;
54427
+ const hiddenChar = props.hiddenChar || preset.hiddenChar || '*';
54428
+ const separator = preset.separator || ' ';
54429
+ // Usar el máximo de cardLengths en lugar de un valor hardcodeado
54430
+ const maxLength = Math.max(...props.cardLengths);
54431
+
54432
+ // Reconstruir los dígitos combinando los visibles del display con los ocultos del realValue
54433
+ const reconstructedDigits = [];
54434
+ let realValueIndex = 0;
54435
+ for (let i = 0; i < value.length; i++) {
54436
+ const char = value[i];
54437
+ if (/[0-9]/.test(char)) {
54438
+ // Es un dígito visible, usarlo directamente
54439
+ reconstructedDigits.push(char);
54440
+ realValueIndex++;
54441
+ } else if (char === hiddenChar) {
54442
+ // Es un carácter oculto, recuperar el dígito del realValue
54443
+ if (realValueIndex < realValue.value.length) {
54444
+ reconstructedDigits.push(realValue.value[realValueIndex]);
54445
+ }
54446
+ realValueIndex++;
54447
+ }
54448
+ // Ignorar separadores y otros caracteres
54449
+ }
54450
+ const cleanValue = reconstructedDigits.join('');
54451
+ const truncatedValue = cleanValue.slice(0, maxLength);
54452
+
54453
+ // Contar dígitos y caracteres ocultos antes del cursor
54454
+ let digitsBeforeCursor = 0;
54455
+ for (let i = 0; i < Math.min(cursorPosition, value.length); i++) {
54456
+ const char = value[i];
54457
+ if (/[0-9]/.test(char) || char === hiddenChar) {
54458
+ digitsBeforeCursor++;
54459
+ }
54460
+ }
54461
+
54462
+ // Aplicar la máscara
54463
+ const masked = applyDynamicPresetMask(truncatedValue);
54464
+
54465
+ // Calcular nueva posición del cursor
54466
+ let newCursorPosition = 0;
54467
+ let digitsCounted = 0;
54468
+ for (let i = 0; i < masked.displayValue.length; i++) {
54469
+ const char = masked.displayValue[i];
54470
+ if (char !== separator) {
54471
+ digitsCounted++;
54472
+ if (digitsCounted === digitsBeforeCursor) {
54473
+ newCursorPosition = i + 1;
54474
+ // Avanzar el cursor más allá del separador si está justo después
54475
+ if (i + 1 < masked.displayValue.length && masked.displayValue[i + 1] === separator) {
54476
+ newCursorPosition = i + 2;
54477
+ }
54478
+ break;
54479
+ }
54480
+ }
54481
+ }
54482
+
54483
+ // Si no encontramos la posición, usar el final
54484
+ if (digitsCounted < digitsBeforeCursor) {
54485
+ newCursorPosition = masked.displayValue.length;
54486
+ }
54487
+ return {
54488
+ displayValue: masked.displayValue,
54489
+ realValue: masked.realValue,
54490
+ cursorPosition: newCursorPosition
54491
+ };
54492
+ };
54493
+
54494
+ // ===== MANEJO DE INPUT PARA PRESET FIJO =====
54495
+ const handleFixedPresetInput = (value, cursorPosition) => {
54496
+ const preset = activePreset.value;
54497
+ const cleanValue = value.replace(/[^0-9]/g, '');
54498
+ const maxLength = preset.maxLength || 10;
54499
+ const truncatedValue = cleanValue.slice(0, maxLength);
54500
+
54501
+ // Contar dígitos antes del cursor en el valor original
54502
+ let digitsBeforeCursor = 0;
54503
+ for (let i = 0; i < Math.min(cursorPosition, value.length); i++) {
54504
+ if (/[0-9]/.test(value[i])) {
54505
+ digitsBeforeCursor++;
54506
+ }
54507
+ }
54508
+
54509
+ // Aplicar la máscara
54510
+ const masked = applyFixedPresetMask(truncatedValue);
54511
+
54512
+ // Calcular nueva posición del cursor
54513
+ let newCursorPosition = 0;
54514
+ let digitsCounted = 0;
54515
+ for (let i = 0; i < masked.displayValue.length; i++) {
54516
+ if (/[0-9]/.test(masked.displayValue[i])) {
54517
+ digitsCounted++;
54518
+ if (digitsCounted === digitsBeforeCursor) {
54519
+ newCursorPosition = i + 1;
54520
+ // Avanzar el cursor más allá de caracteres no numéricos
54521
+ while (newCursorPosition < masked.displayValue.length && !/[0-9]/.test(masked.displayValue[newCursorPosition])) {
54522
+ newCursorPosition++;
54523
+ }
54524
+ break;
54525
+ }
54526
+ }
54527
+ }
54528
+
54529
+ // Si no encontramos la posición, usar el final
54530
+ if (digitsCounted < digitsBeforeCursor) {
54531
+ newCursorPosition = masked.displayValue.length;
54532
+ }
54533
+ return {
54534
+ displayValue: masked.displayValue,
54535
+ realValue: masked.realValue,
54536
+ cursorPosition: newCursorPosition
54537
+ };
54538
+ };
54539
+
54540
+ // ===== MANEJO DE INPUT PARA PRESET NUMÉRICO =====
54541
+ const handleNumericPresetInput = (value, cursorPosition) => {
54542
+ const preset = activePreset.value;
54543
+ const prefix = presetOptions.value.currencySymbol || preset.prefix || '';
54544
+ const suffix = preset.suffix || '';
54545
+ const decimalSeparator = preset.decimalSeparator || '.';
54546
+
54547
+ // Remover prefijo y sufijo si están presentes
54548
+ let cleanInput = value;
54549
+ if (prefix && cleanInput.startsWith(prefix)) {
54550
+ cleanInput = cleanInput.slice(prefix.length);
54551
+ }
54552
+ if (suffix && cleanInput.endsWith(suffix)) {
54553
+ cleanInput = cleanInput.slice(0, -suffix.length);
54554
+ }
54555
+
54556
+ // Contar dígitos y puntos decimales antes del cursor
54557
+ let relevantCharsBeforeCursor = 0;
54558
+ let inputBeforeCursor = value.slice(0, cursorPosition);
54559
+ if (prefix && inputBeforeCursor.startsWith(prefix)) {
54560
+ inputBeforeCursor = inputBeforeCursor.slice(prefix.length);
54561
+ }
54562
+ for (let i = 0; i < inputBeforeCursor.length; i++) {
54563
+ if (/[0-9.]/.test(inputBeforeCursor[i])) {
54564
+ relevantCharsBeforeCursor++;
54565
+ }
54566
+ }
54567
+
54568
+ // Aplicar la máscara
54569
+ const masked = applyNumericPresetMask(cleanInput);
54570
+
54571
+ // Calcular nueva posición del cursor
54572
+ let newCursorPosition = prefix.length;
54573
+ let charsCounted = 0;
54574
+
54575
+ // Obtener el display sin prefijo y sufijo para contar
54576
+ let displayWithoutPrefixSuffix = masked.displayValue;
54577
+ if (prefix && displayWithoutPrefixSuffix.startsWith(prefix)) {
54578
+ displayWithoutPrefixSuffix = displayWithoutPrefixSuffix.slice(prefix.length);
54579
+ }
54580
+ if (suffix && displayWithoutPrefixSuffix.endsWith(suffix)) {
54581
+ displayWithoutPrefixSuffix = displayWithoutPrefixSuffix.slice(0, -suffix.length);
54582
+ }
54583
+ for (let i = 0; i < displayWithoutPrefixSuffix.length; i++) {
54584
+ const char = displayWithoutPrefixSuffix[i];
54585
+ if (/[0-9]/.test(char) || char === decimalSeparator) {
54586
+ charsCounted++;
54587
+ if (charsCounted === relevantCharsBeforeCursor) {
54588
+ newCursorPosition = prefix.length + i + 1;
54589
+ break;
54590
+ }
54591
+ }
54592
+ }
54593
+
54594
+ // Si no encontramos la posición exacta, posicionar antes del sufijo
54595
+ if (charsCounted < relevantCharsBeforeCursor) {
54596
+ newCursorPosition = masked.displayValue.length - suffix.length;
54597
+ }
54598
+ return {
54599
+ displayValue: masked.displayValue,
54600
+ realValue: masked.realValue,
54601
+ cursorPosition: newCursorPosition
54602
+ };
54603
+ };
54604
+
54605
+ // ===== FUNCIONES DE VALIDACIÓN (delegan a EInput) =====
54606
+
54607
+ // Resetea todos los errores de validación (delega a EInput)
54608
+ const reset = () => {
54609
+ inputRef.value?.reset();
54610
+ lastValidationResult.value = true;
54611
+ };
54612
+
54613
+ // Valida el valor actual usando las reglas configuradas (delega a EInput)
54614
+ const validate = () => {
54615
+ const result = inputRef.value?.validate() ?? true;
54616
+ lastValidationResult.value = result;
54617
+ return result;
54618
+ };
53482
54619
  const handleBlur = () => {
53483
- emit('update:modelValue', realValue.value);
54620
+ // EInput maneja la validación internamente
54621
+ emit('update:modelValue', getEmitValue(realValue.value, displayValue.value));
53484
54622
  };
53485
54623
  const handleKeydown = event => {
53486
54624
  // Manejar backspace para máscaras activas
@@ -53496,20 +54634,30 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53496
54634
  return;
53497
54635
  }
53498
54636
 
54637
+ // Determinar qué caracteres son válidos según el tipo de máscara
54638
+ const hiddenChar = props.hiddenChar || '*';
54639
+ let validCharRegex;
54640
+ if (isNumericPreset.value) {
54641
+ // Para presets numéricos, los dígitos y el punto decimal son válidos
54642
+ const decimalSeparator = activePreset.value?.decimalSeparator || '.';
54643
+ validCharRegex = new RegExp(`[0-9${decimalSeparator.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}]`);
54644
+ } else {
54645
+ // Para otros presets, dígitos y el carácter oculto son válidos
54646
+ validCharRegex = new RegExp(`[0-9${hiddenChar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}]`);
54647
+ }
54648
+
53499
54649
  // Verificar si el cursor está justo después de un separador
53500
54650
  if (cursorPosition > 0 && cursorPosition <= value.length) {
53501
54651
  const charBeforeCursor = value[cursorPosition - 1];
53502
- const hiddenChar = props.hiddenChar || '*';
53503
- const hiddenCharRegex = new RegExp(`[0-9${hiddenChar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}]`);
53504
54652
 
53505
- // Si el carácter antes del cursor no es un dígito ni el carácter oculto, es un separador
53506
- if (!hiddenCharRegex.test(charBeforeCursor)) {
54653
+ // Si el carácter antes del cursor no es un carácter válido, es un separador/símbolo
54654
+ if (!validCharRegex.test(charBeforeCursor)) {
53507
54655
  // Prevenir el comportamiento por defecto
53508
54656
  event.preventDefault();
53509
54657
 
53510
54658
  // Buscar el dígito anterior (antes del separador)
53511
54659
  let digitPosition = cursorPosition - 1;
53512
- while (digitPosition > 0 && !hiddenCharRegex.test(value[digitPosition - 1])) {
54660
+ while (digitPosition > 0 && !validCharRegex.test(value[digitPosition - 1])) {
53513
54661
  digitPosition--;
53514
54662
  }
53515
54663
  if (digitPosition > 0) {
@@ -53560,22 +54708,40 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53560
54708
  const handlePaste = event => {
53561
54709
  if (!isMaskActive.value) return;
53562
54710
  event.preventDefault();
54711
+
54712
+ // Reset errores previos
54713
+ inputRef.value?.reset?.();
53563
54714
  const clipboardData = event.clipboardData || window.clipboardData;
53564
54715
  const pastedValue = clipboardData.getData('text');
53565
54716
 
53566
- // Validar que solo contenga números
53567
- if (!/^[0-9\s-]*$/.test(pastedValue)) {
53568
- return;
54717
+ // Validar según el tipo de máscara
54718
+ if (isNumericPreset.value) {
54719
+ // Para presets numéricos, permitir números, espacios, guiones y puntos
54720
+ if (!/^[0-9\s\-.,]*$/.test(pastedValue)) {
54721
+ return;
54722
+ }
54723
+ } else {
54724
+ // Para otros presets, validar que solo contenga números
54725
+ if (!/^[0-9\s-]*$/.test(pastedValue)) {
54726
+ return;
54727
+ }
53569
54728
  }
53570
- const result = applyPatternMask(pastedValue, props.maskPattern);
54729
+
54730
+ // Aplicar la máscara usando la función unificada
54731
+ const result = applyMask(pastedValue);
53571
54732
  realValue.value = result.realValue;
53572
54733
  displayValue.value = result.displayValue;
53573
- emit('update:modelValue', result.realValue);
54734
+ emit('update:modelValue', getEmitValue(result.realValue, result.displayValue));
53574
54735
  (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.nextTick)(() => {
53575
54736
  const input = getNativeInput();
53576
54737
  if (input) {
53577
54738
  const length = result.displayValue.length;
53578
54739
  input.setSelectionRange(length, length);
54740
+
54741
+ // Forzar revalidación con el nuevo valor
54742
+ (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.nextTick)(() => {
54743
+ inputRef.value?.validate?.();
54744
+ });
53579
54745
  }
53580
54746
  });
53581
54747
  };
@@ -53587,7 +54753,7 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53587
54753
  realValue.value = '';
53588
54754
  displayValue.value = '';
53589
54755
 
53590
- // Emitir eventos
54756
+ // Emitir eventos (el valor limpio siempre es vacío independiente de saveFormatted)
53591
54757
  emit('update:modelValue', '');
53592
54758
  emit('clean');
53593
54759
 
@@ -53628,14 +54794,14 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53628
54794
 
53629
54795
  // ===== FORMATO INICIAL =====
53630
54796
  const formatInitialValue = value => {
53631
- if (!value) {
54797
+ if (!value && value !== '0') {
53632
54798
  return {
53633
54799
  displayValue: '',
53634
54800
  realValue: ''
53635
54801
  };
53636
54802
  }
53637
54803
  if (isMaskActive.value) {
53638
- return applyPatternMask(value, props.maskPattern);
54804
+ return applyMask(value);
53639
54805
  }
53640
54806
  return {
53641
54807
  displayValue: value,
@@ -53649,7 +54815,10 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53649
54815
  if (isCleaning.value) {
53650
54816
  return;
53651
54817
  }
53652
- if (newValue !== realValue.value) {
54818
+
54819
+ // Comparar con el valor correcto según saveFormatted
54820
+ const currentEmitValue = getEmitValue(realValue.value, displayValue.value);
54821
+ if (newValue !== currentEmitValue) {
53653
54822
  const formatted = formatInitialValue(newValue);
53654
54823
  displayValue.value = formatted.displayValue;
53655
54824
  realValue.value = formatted.realValue;
@@ -53658,9 +54827,18 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53658
54827
  immediate: true
53659
54828
  });
53660
54829
 
54830
+ // Watcher para cambios en el preset
54831
+ (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.watch)(() => props.preset, () => {
54832
+ if (realValue.value) {
54833
+ const formatted = formatInitialValue(realValue.value);
54834
+ displayValue.value = formatted.displayValue;
54835
+ realValue.value = formatted.realValue;
54836
+ }
54837
+ });
54838
+
53661
54839
  // Lifecycle
53662
54840
  (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.onMounted)(() => {
53663
- if (props.modelValue) {
54841
+ if (props.modelValue || props.modelValue === '0') {
53664
54842
  const formatted = formatInitialValue(props.modelValue);
53665
54843
  displayValue.value = formatted.displayValue;
53666
54844
  realValue.value = formatted.realValue;
@@ -53691,33 +54869,53 @@ const EInputMaskvue_type_script_setup_true_lang_js_hoisted_1 = ["id", "value"];
53691
54869
 
53692
54870
  // Exponer métodos útiles
53693
54871
  __expose({
54872
+ /** Obtiene el valor real (sin formato) */
53694
54873
  getRealValue: () => realValue.value,
54874
+ /** Obtiene el valor de display (con formato) */
53695
54875
  getDisplayValue: () => displayValue.value,
54876
+ /** Obtiene el valor que se emite según saveFormatted */
54877
+ getEmittedValue: () => getEmitValue(realValue.value, displayValue.value),
54878
+ /** Limpia el input y resetea validaciones */
53696
54879
  clean: () => {
53697
54880
  displayValue.value = '';
53698
54881
  realValue.value = '';
54882
+ reset();
53699
54883
  emit('update:modelValue', '');
53700
54884
  emit('clean');
53701
- }
54885
+ },
54886
+ /** Valida el valor actual según las reglas y el preset activo (delega a EInput) */
54887
+ validate,
54888
+ /** Resetea todos los errores de validación (delega a EInput) */
54889
+ reset,
54890
+ /** Alias de reset para compatibilidad (delega a EInput) */
54891
+ resetValidation: reset,
54892
+ /** Indica si hay un error de validación activo */
54893
+ hasError: () => !lastValidationResult.value,
54894
+ /** Obtiene información del preset activo */
54895
+ getPresetInfo: () => activePreset.value
53702
54896
  });
53703
54897
  return (_ctx, _cache) => {
53704
- return (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.openBlock)(), (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createElementBlock)("div", null, [(0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createElementVNode)("input", {
54898
+ return (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.openBlock)(), (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createElementBlock)("div", EInputMaskvue_type_script_setup_true_lang_js_hoisted_1, [(0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createElementVNode)("input", {
53705
54899
  type: "hidden",
53706
54900
  id: hiddenInputId.value,
53707
54901
  value: realValue.value
53708
- }, null, 8, EInputMaskvue_type_script_setup_true_lang_js_hoisted_1), (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createVNode)(EInput, (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.mergeProps)({
54902
+ }, null, 8, EInputMaskvue_type_script_setup_true_lang_js_hoisted_2), (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createVNode)(EInput, (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.mergeProps)({
53709
54903
  id: inputId.value,
53710
54904
  ref: el => inputRef.value = el,
53711
54905
  modelValue: displayValue.value,
53712
54906
  "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => displayValue.value = $event)
53713
54907
  }, inputProps.value, {
54908
+ required: __props.required,
54909
+ rules: inputRules.value,
54910
+ label: __props.label,
54911
+ length: inputLength.value,
53714
54912
  "onUpdate:modelValue": handleDisplayUpdate,
53715
54913
  onBlur: handleBlur,
53716
54914
  onFocus: handleFocus,
53717
54915
  onClean: handleClean,
53718
54916
  onOnEnter: handleEnter,
53719
54917
  onOnEnterValid: handleEnterValid
53720
- }), null, 16, ["id", "modelValue"])]);
54918
+ }), null, 16, ["id", "modelValue", "required", "rules", "label", "length"])]);
53721
54919
  };
53722
54920
  }
53723
54921
  });
@@ -55235,10 +56433,10 @@ const ESelectvue_type_script_setup_true_lang_js_hoisted_11 = {
55235
56433
  }, null, 46, ESelectvue_type_script_setup_true_lang_js_hoisted_2), [[external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.vModelText, tempValueInput.value]]), (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createElementVNode)("div", {
55236
56434
  ref: `selectLabelSelectedSuggest-${(0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.unref)(instance).uid}`,
55237
56435
  class: (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.normalizeClass)(inputLabelSelectedClass.value)
55238
- }, (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.toDisplayString)(labelSelected.value), 3), showTooltip.value ? ((0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.openBlock)(), (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createBlock)(_component_e_tooltip, {
56436
+ }, (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.toDisplayString)(getFormattedOptionSelected(labelSelected.value)), 3), showTooltip.value ? ((0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.openBlock)(), (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createBlock)(_component_e_tooltip, {
55239
56437
  key: 0
55240
56438
  }, {
55241
- default: (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.withCtx)(() => [(0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createTextVNode)((0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.toDisplayString)(labelSelected.value), 1)]),
56439
+ default: (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.withCtx)(() => [(0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createTextVNode)((0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.toDisplayString)(getFormattedOptionSelected(labelSelected.value)), 1)]),
55242
56440
  _: 1
55243
56441
  })) : (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createCommentVNode)("", true), (0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.createElementVNode)("label", {
55244
56442
  for: `input-${(0,external_commonjs_vue_commonjs2_vue_root_Vue_namespaceObject.unref)(instance).uid}`,