@dile/ui 3.8.0 → 3.8.2
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.
|
@@ -183,4 +183,78 @@ describe('dile-input-mask', () => {
|
|
|
183
183
|
expect(el.maskedValue).toBe('AB-cd-12');
|
|
184
184
|
expect(el.value).toBe('ABcd12');
|
|
185
185
|
});
|
|
186
|
+
|
|
187
|
+
it('supports X pattern for alphanumeric uppercase', async () => {
|
|
188
|
+
const el = await renderInputMask('<dile-input-mask name="test" mask="XX-0000"></dile-input-mask>');
|
|
189
|
+
const input = el.shadowRoot.querySelector('input');
|
|
190
|
+
|
|
191
|
+
// X accepts both letters and digits
|
|
192
|
+
const chars = ['a', 'B', '1', '2', '3', '4'];
|
|
193
|
+
chars.forEach((char) => {
|
|
194
|
+
input.value += char;
|
|
195
|
+
const inputEvent = new Event('input', { bubbles: true });
|
|
196
|
+
input.dispatchEvent(inputEvent);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
await el.updateComplete;
|
|
200
|
+
|
|
201
|
+
// Letters should be uppercase, digits unchanged
|
|
202
|
+
expect(el.maskedValue).toBe('AB-1234');
|
|
203
|
+
expect(el.value).toBe('AB1234');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('supports x pattern for alphanumeric lowercase', async () => {
|
|
207
|
+
const el = await renderInputMask('<dile-input-mask name="test" mask="xx-0000"></dile-input-mask>');
|
|
208
|
+
const input = el.shadowRoot.querySelector('input');
|
|
209
|
+
|
|
210
|
+
// x accepts both letters and digits
|
|
211
|
+
const chars = ['A', 'b', '1', '2', '3', '4'];
|
|
212
|
+
chars.forEach((char) => {
|
|
213
|
+
input.value += char;
|
|
214
|
+
const inputEvent = new Event('input', { bubbles: true });
|
|
215
|
+
input.dispatchEvent(inputEvent);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
await el.updateComplete;
|
|
219
|
+
|
|
220
|
+
// Letters should be lowercase, digits unchanged
|
|
221
|
+
expect(el.maskedValue).toBe('ab-1234');
|
|
222
|
+
expect(el.value).toBe('ab1234');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('applies lazy separators only when next block is filled', async () => {
|
|
226
|
+
const el = await renderInputMask('<dile-input-mask name="iban" mask="AA-XXXX-0000"></dile-input-mask>');
|
|
227
|
+
const input = el.shadowRoot.querySelector('input');
|
|
228
|
+
|
|
229
|
+
// Type 'ES' - should show 'ES' without trailing separator
|
|
230
|
+
input.value = 'ES';
|
|
231
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
232
|
+
await el.updateComplete;
|
|
233
|
+
expect(el.maskedValue).toBe('ES');
|
|
234
|
+
|
|
235
|
+
// Type 'ESW' - should show 'ES-W' (separator added when next block starts)
|
|
236
|
+
input.value = 'ESW';
|
|
237
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
238
|
+
await el.updateComplete;
|
|
239
|
+
expect(el.maskedValue).toBe('ES-W');
|
|
240
|
+
|
|
241
|
+
// Type 'ESWSWQ' - should show 'ES-WSWQ'
|
|
242
|
+
input.value = 'ESWSWQ';
|
|
243
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
244
|
+
await el.updateComplete;
|
|
245
|
+
expect(el.maskedValue).toBe('ES-WSWQ');
|
|
246
|
+
|
|
247
|
+
// Type 'ESWSWQ0' - should show 'ES-WSWQ-0' (separator added when digits block starts)
|
|
248
|
+
input.value = 'ESWSWQ0';
|
|
249
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
250
|
+
await el.updateComplete;
|
|
251
|
+
expect(el.maskedValue).toBe('ES-WSWQ-0');
|
|
252
|
+
|
|
253
|
+
// Type complete 'ESWSWQ0000' - should show 'ES-WSWQ-0000'
|
|
254
|
+
input.value = 'ESWSWQ0000';
|
|
255
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
256
|
+
await el.updateComplete;
|
|
257
|
+
expect(el.maskedValue).toBe('ES-WSWQ-0000');
|
|
258
|
+
});
|
|
186
259
|
});
|
|
260
|
+
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generic mask pattern that supports letters (A/a), numbers (0), and literal characters.
|
|
2
|
+
* Generic mask pattern that supports letters (A/a/X/x), numbers (0), and literal characters.
|
|
3
3
|
* Pattern definition:
|
|
4
|
-
* 'A' =
|
|
5
|
-
* 'a' =
|
|
6
|
-
* '
|
|
7
|
-
*
|
|
4
|
+
* 'A' = uppercase letter only - will be transformed to UPPERCASE
|
|
5
|
+
* 'a' = lowercase letter only - will be transformed to lowercase
|
|
6
|
+
* 'X' = letter or digit - will be transformed to UPPERCASE if letter
|
|
7
|
+
* 'x' = letter or digit - will be transformed to lowercase if letter
|
|
8
|
+
* '0' = digit only (0-9)
|
|
9
|
+
* other chars = fixed literal characters in the mask (added only when next block is filled)
|
|
8
10
|
*
|
|
9
11
|
* Examples:
|
|
10
|
-
* 'aa00 0000 0000 0000 0000' = IBAN format (
|
|
12
|
+
* 'aa00 0000 0000 0000 0000' = IBAN format (lowercase letters + digits with lazy spaces)
|
|
13
|
+
* 'AA-XXXX-0000-0000' = Mixed format with lazy separators
|
|
11
14
|
* '(000) 000-0000' = US phone format
|
|
12
|
-
* 'AA-00-AA' = Format with uppercase letters and digits
|
|
13
15
|
*/
|
|
14
16
|
export class MaskPattern {
|
|
15
17
|
constructor(pattern, language = 'en') {
|
|
@@ -27,6 +29,10 @@ export class MaskPattern {
|
|
|
27
29
|
this.patternMap.push({ type: 'letterUpper', index: i });
|
|
28
30
|
} else if (char === 'a') {
|
|
29
31
|
this.patternMap.push({ type: 'letterLower', index: i });
|
|
32
|
+
} else if (char === 'X') {
|
|
33
|
+
this.patternMap.push({ type: 'alphanumUpper', index: i });
|
|
34
|
+
} else if (char === 'x') {
|
|
35
|
+
this.patternMap.push({ type: 'alphanumLower', index: i });
|
|
30
36
|
} else if (char === '0') {
|
|
31
37
|
this.patternMap.push({ type: 'digit', index: i });
|
|
32
38
|
} else {
|
|
@@ -49,24 +55,29 @@ export class MaskPattern {
|
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
maskIt(value) {
|
|
52
|
-
let valueIndex = 0;
|
|
53
58
|
let masked = '';
|
|
54
|
-
|
|
59
|
+
let slotIndex = 0; // Position in input slots (non-literals only)
|
|
60
|
+
|
|
55
61
|
for (let i = 0; i < this.patternMap.length; i++) {
|
|
56
62
|
const patternInfo = this.patternMap[i];
|
|
57
|
-
|
|
63
|
+
|
|
58
64
|
if (patternInfo.type === 'literal') {
|
|
59
|
-
|
|
65
|
+
// Lazy literals: only add if there are more input characters to fill slots after this literal
|
|
66
|
+
if (slotIndex < value.length) {
|
|
67
|
+
masked += patternInfo.char;
|
|
68
|
+
}
|
|
60
69
|
} else {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
// This is an input slot position
|
|
71
|
+
if (slotIndex < value.length) {
|
|
72
|
+
masked += value[slotIndex];
|
|
73
|
+
slotIndex++;
|
|
64
74
|
} else {
|
|
75
|
+
// No more input characters, stop
|
|
65
76
|
break;
|
|
66
77
|
}
|
|
67
78
|
}
|
|
68
79
|
}
|
|
69
|
-
|
|
80
|
+
|
|
70
81
|
return masked;
|
|
71
82
|
}
|
|
72
83
|
|
|
@@ -91,6 +102,15 @@ export class MaskPattern {
|
|
|
91
102
|
};
|
|
92
103
|
}
|
|
93
104
|
valueIndex++;
|
|
105
|
+
} else if (patternInfo.type === 'alphanumUpper' || patternInfo.type === 'alphanumLower') {
|
|
106
|
+
const char = value[valueIndex];
|
|
107
|
+
if (!/[a-zA-Z0-9]/.test(char)) {
|
|
108
|
+
return {
|
|
109
|
+
valid: false,
|
|
110
|
+
error: this.getErrorMessage('invalidAlphanumeric', valueIndex + 1, char),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
valueIndex++;
|
|
94
114
|
} else if (patternInfo.type === 'digit') {
|
|
95
115
|
const char = value[valueIndex];
|
|
96
116
|
if (!/[0-9]/.test(char)) {
|
|
@@ -113,11 +133,15 @@ export class MaskPattern {
|
|
|
113
133
|
getFormatDescription() {
|
|
114
134
|
const lettersUpper = this.patternMap.filter(p => p.type === 'letterUpper').length;
|
|
115
135
|
const lettersLower = this.patternMap.filter(p => p.type === 'letterLower').length;
|
|
136
|
+
const alphanumUpper = this.patternMap.filter(p => p.type === 'alphanumUpper').length;
|
|
137
|
+
const alphanumLower = this.patternMap.filter(p => p.type === 'alphanumLower').length;
|
|
116
138
|
const digits = this.patternMap.filter(p => p.type === 'digit').length;
|
|
117
139
|
|
|
118
140
|
const parts = [];
|
|
119
141
|
if (lettersUpper > 0) parts.push(`${lettersUpper} uppercase letter${lettersUpper !== 1 ? 's' : ''}`);
|
|
120
142
|
if (lettersLower > 0) parts.push(`${lettersLower} lowercase letter${lettersLower !== 1 ? 's' : ''}`);
|
|
143
|
+
if (alphanumUpper > 0) parts.push(`${alphanumUpper} alphanumeric (uppercase if letter)${alphanumUpper !== 1 ? 's' : ''}`);
|
|
144
|
+
if (alphanumLower > 0) parts.push(`${alphanumLower} alphanumeric (lowercase if letter)${alphanumLower !== 1 ? 's' : ''}`);
|
|
121
145
|
if (digits > 0) parts.push(`${digits} digit${digits !== 1 ? 's' : ''}`);
|
|
122
146
|
|
|
123
147
|
return parts.join(' + ');
|
|
@@ -130,6 +154,8 @@ export class MaskPattern {
|
|
|
130
154
|
/**
|
|
131
155
|
* Transforms a clean value according to the pattern's case requirements
|
|
132
156
|
* 'A' positions will be uppercase, 'a' positions will be lowercase
|
|
157
|
+
* 'X' positions: if letter, uppercase; if digit, unchanged
|
|
158
|
+
* 'x' positions: if letter, lowercase; if digit, unchanged
|
|
133
159
|
* Returns only the transformed value (letters and digits), without literals
|
|
134
160
|
* @param {string} cleanValue - The clean value (letters and digits only)
|
|
135
161
|
* @returns {string} The transformed value with case applied (no literals)
|
|
@@ -151,6 +177,18 @@ export class MaskPattern {
|
|
|
151
177
|
result += cleanValue[valueIndex].toLowerCase();
|
|
152
178
|
valueIndex++;
|
|
153
179
|
}
|
|
180
|
+
} else if (patternInfo.type === 'alphanumUpper') {
|
|
181
|
+
if (valueIndex < cleanValue.length) {
|
|
182
|
+
const char = cleanValue[valueIndex];
|
|
183
|
+
result += /[a-zA-Z]/.test(char) ? char.toUpperCase() : char;
|
|
184
|
+
valueIndex++;
|
|
185
|
+
}
|
|
186
|
+
} else if (patternInfo.type === 'alphanumLower') {
|
|
187
|
+
if (valueIndex < cleanValue.length) {
|
|
188
|
+
const char = cleanValue[valueIndex];
|
|
189
|
+
result += /[a-zA-Z]/.test(char) ? char.toLowerCase() : char;
|
|
190
|
+
valueIndex++;
|
|
191
|
+
}
|
|
154
192
|
} else if (patternInfo.type === 'digit') {
|
|
155
193
|
if (valueIndex < cleanValue.length) {
|
|
156
194
|
result += cleanValue[valueIndex];
|
|
@@ -168,22 +206,27 @@ export class MaskPattern {
|
|
|
168
206
|
en: {
|
|
169
207
|
invalidLetter: `Position ${position}: expected a letter but got "${char}"`,
|
|
170
208
|
invalidDigit: `Position ${position}: expected a digit but got "${char}"`,
|
|
209
|
+
invalidAlphanumeric: `Position ${position}: expected a letter or digit but got "${char}"`,
|
|
171
210
|
},
|
|
172
211
|
es: {
|
|
173
212
|
invalidLetter: `Posición ${position}: se esperaba una letra pero recibió "${char}"`,
|
|
174
213
|
invalidDigit: `Posición ${position}: se esperaba un dígito pero recibió "${char}"`,
|
|
214
|
+
invalidAlphanumeric: `Posición ${position}: se esperaba una letra o dígito pero recibió "${char}"`,
|
|
175
215
|
},
|
|
176
216
|
fr: {
|
|
177
217
|
invalidLetter: `Position ${position} : lettre attendue mais reçu "${char}"`,
|
|
178
218
|
invalidDigit: `Position ${position} : chiffre attendu mais reçu "${char}"`,
|
|
219
|
+
invalidAlphanumeric: `Position ${position} : lettre ou chiffre attendu mais reçu "${char}"`,
|
|
179
220
|
},
|
|
180
221
|
de: {
|
|
181
222
|
invalidLetter: `Position ${position}: Buchstabe erwartet, aber "${char}" erhalten`,
|
|
182
223
|
invalidDigit: `Position ${position}: Ziffer erwartet, aber "${char}" erhalten`,
|
|
224
|
+
invalidAlphanumeric: `Position ${position}: Buchstabe oder Ziffer erwartet, aber "${char}" erhalten`,
|
|
183
225
|
},
|
|
184
226
|
cat: {
|
|
185
227
|
invalidLetter: `Posició ${position}: s'esperava una lletra però va rebre "${char}"`,
|
|
186
228
|
invalidDigit: `Posició ${position}: s'esperava un dígit però va rebre "${char}"`,
|
|
229
|
+
invalidAlphanumeric: `Posició ${position}: s'esperava una lletra o dígit però va rebre "${char}"`,
|
|
187
230
|
},
|
|
188
231
|
};
|
|
189
232
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dile/ui",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.2",
|
|
4
4
|
"description": "UI Core components from dile-components.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
},
|
|
22
22
|
"homepage": "https://dile-components.com/",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@dile/iconlib": "^
|
|
24
|
+
"@dile/iconlib": "^2.0.0",
|
|
25
25
|
"@dile/icons": "^2.5.1",
|
|
26
26
|
"lit": "^2.7.0 || ^3.0.0"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "ceeab58670ba4c09593d0760a357ecac36214a44"
|
|
32
32
|
}
|