Skip to content

Commit ddd7b20

Browse files
author
igor.nepipenko
committed
feat(ref:no-ref): ng21 v4
1 parent 9dcc08d commit ddd7b20

File tree

8 files changed

+129
-42
lines changed

8 files changed

+129
-42
lines changed

angular.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
"outputHashing": "all"
5050
},
5151
"development": {
52+
"define": {
53+
"VERSION": "'dev'"
54+
},
5255
"optimization": false,
5356
"extractLicenses": false,
5457
"sourceMap": true
@@ -58,14 +61,15 @@
5861
},
5962
"serve": {
6063
"builder": "@angular/build:dev-server",
61-
"options": {
62-
"buildTarget": "ngx-mask:build"
63-
},
6464
"configurations": {
6565
"production": {
6666
"buildTarget": "ngx-mask:build:production"
67+
},
68+
"development": {
69+
"buildTarget": "ngx-mask:build:development"
6770
}
68-
}
71+
},
72+
"defaultConfiguration": "development"
6973
},
7074
"test": {
7175
"builder": "@angular/build:unit-test",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ngx-mask",
3-
"version": "20.0.3",
3+
"version": "21.0.0",
44
"description": "Awesome ngx mask",
55
"license": "MIT",
66
"engines": {

projects/ngx-mask-lib/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ngx-mask",
3-
"version": "20.0.3",
3+
"version": "21.0.0",
44
"description": "awesome ngx mask",
55
"keywords": [
66
"ng2-mask",

projects/ngx-mask-lib/src/lib/ngx-mask.service.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,15 @@ export class NgxMaskService extends NgxMaskApplierService {
707707

708708
private _regExpForRemove(specialCharactersForRemove: string[]): RegExp {
709709
return new RegExp(
710-
specialCharactersForRemove.map((item: string) => `\\${item}`).join('|'),
710+
specialCharactersForRemove
711+
.map((item: string) => {
712+
// Only escape characters that have special meaning in regex
713+
if (/[.*+?^${}()|[\]\\/-]/.test(item)) {
714+
return `\\${item}`;
715+
}
716+
return item;
717+
})
718+
.join('|'),
711719
'gi'
712720
);
713721
}

projects/ngx-mask-lib/src/test/drop-special-charaters.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,14 @@ describe('Directive: Mask (Drop special characters)', () => {
157157
equal('1234567890', '(123) 456-7890', fixture);
158158
expect(component.form.valid).equal(true);
159159
});
160+
161+
it('should drop all special characters including letters from mask like ext.', () => {
162+
component.mask.set('(000) 000-0000 ext. 000000');
163+
component.specialCharacters.set(['e', 'x', 't', ' ', '(', ')', '-', '.']);
164+
component.dropSpecialCharacters.set(true);
165+
component.showMaskTyped.set(true);
166+
167+
equal('1231231123112333', '(123) 123-1123 ext. 112333', fixture);
168+
expect(component.form.value).equal('1231231123112333');
169+
});
160170
});

src/app/options/options.component.html

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@
5050
</ng-template>
5151

5252
<ng-template #inputTemplate let-ex let-placeholder="placeholder">
53-
<div class="flex">
53+
<!-- Reactive Forms (FormControl) -->
54+
<div class="flex flex-col gap-1 mb-2">
55+
<span class="text-xs text-white/50 uppercase tracking-wider">Reactive Forms</span>
5456
<input
5557
[placeholder]="placeholder || ''"
5658
[placeHolderCharacter]="ex._placholderCharacter || '_'"
@@ -59,11 +61,10 @@
5961
[dropSpecialCharacters]="ex._dropSpecialCharacters ?? true"
6062
[mask]="ex._mask || ''"
6163
[thousandSeparator]="ex._thousandSeparator || ' '"
62-
[formField]="ex.control.form"
6364
[allowNegativeNumbers]="ex._allowNegativeNumbers || null"
6465
[specialCharacters]="ex._specialCharacters || specialCharacters"
6566
[shownMaskExpression]="ex._shownMaskExpression"
66-
[(ngModel)]="ex.control.model"
67+
[formControl]="ex.control.formControl"
6768
[apm]="ex._apm || false"
6869
[decimalMarker]="ex._decimalMarker || '.'"
6970
[leadZero]="ex._leadZero || false"
@@ -75,24 +76,77 @@
7576
[outputTransformFn]="ex._outputTransformFn || outputTransformFn"
7677
[inputTransformFn]="ex._inputTransformFn || inputTransformFn"
7778
class="w-full h-[51px] placeholder:text-white/25 text-full-white py-3 px-5 outline-none bg-black border-b-2px border-b-white rounded-10px focus:border-b-yellow hover:border-b-yellow hover:bg-full-white/25 focus:bg-full-white/25" />
78-
</div>
79-
<div class="flex gap-50px">
8079
<jsdaddy-open-source-card-content
81-
[value]="ex.control.form().value() | isEmpty"
82-
title="Form Control:"
80+
[value]="ex.control.formControl.value | isEmpty"
81+
title="FormControl:"
8382
color="yellow-view" />
83+
</div>
84+
85+
<!-- Template-driven (ngModel) -->
86+
<div class="flex flex-col gap-1 mb-2">
87+
<span class="text-xs text-white/50 uppercase tracking-wider">Template-driven</span>
88+
<input
89+
[placeholder]="placeholder || ''"
90+
[placeHolderCharacter]="ex._placholderCharacter || '_'"
91+
[prefix]="ex._prefix || ''"
92+
[suffix]="ex._suffix || ''"
93+
[dropSpecialCharacters]="ex._dropSpecialCharacters ?? true"
94+
[mask]="ex._mask || ''"
95+
[thousandSeparator]="ex._thousandSeparator || ' '"
96+
[allowNegativeNumbers]="ex._allowNegativeNumbers || null"
97+
[specialCharacters]="ex._specialCharacters || specialCharacters"
98+
[shownMaskExpression]="ex._shownMaskExpression"
99+
[(ngModel)]="ex.control.model"
100+
[apm]="ex._apm || false"
101+
[decimalMarker]="ex._decimalMarker || '.'"
102+
[leadZero]="ex._leadZero || false"
103+
[keepCharacterPositions]="ex._keepCharacterPositions || false"
104+
[validation]="ex._validation || false"
105+
[showMaskTyped]="ex._showMaskTyped || false"
106+
[clearIfNotMatch]="ex._clearIfNotMatch"
107+
[hiddenInput]="ex._hiddenInput || null"
108+
[outputTransformFn]="ex._outputTransformFn || outputTransformFn"
109+
[inputTransformFn]="ex._inputTransformFn || inputTransformFn"
110+
class="w-full h-[51px] placeholder:text-white/25 text-full-white py-3 px-5 outline-none bg-black border-b-2px border-b-white rounded-10px focus:border-b-yellow hover:border-b-yellow hover:bg-full-white/25 focus:bg-full-white/25" />
84111
<jsdaddy-open-source-card-content
85-
[value]="ex.control.model | isEmpty"
86-
title="Ng Model:"
112+
[value]="ex.control.model() | isEmpty"
113+
title="NgModel:"
87114
color="yellow-view" />
88115
</div>
89-
<jsdaddy-open-source-card-content [value]="ex._mask" title="Mask:" color="green-view" />
90-
@if (ex._validation) {
116+
117+
<!-- Signal Forms -->
118+
<div class="flex flex-col gap-1 mb-2">
119+
<span class="text-xs text-white/50 uppercase tracking-wider">Signal Forms</span>
120+
<input
121+
[placeholder]="placeholder || ''"
122+
[placeHolderCharacter]="ex._placholderCharacter || '_'"
123+
[prefix]="ex._prefix || ''"
124+
[suffix]="ex._suffix || ''"
125+
[dropSpecialCharacters]="ex._dropSpecialCharacters ?? true"
126+
[mask]="ex._mask || ''"
127+
[thousandSeparator]="ex._thousandSeparator || ' '"
128+
[allowNegativeNumbers]="ex._allowNegativeNumbers || null"
129+
[specialCharacters]="ex._specialCharacters || specialCharacters"
130+
[shownMaskExpression]="ex._shownMaskExpression"
131+
[formField]="ex.control.signalForm.value"
132+
[apm]="ex._apm || false"
133+
[decimalMarker]="ex._decimalMarker || '.'"
134+
[leadZero]="ex._leadZero || false"
135+
[keepCharacterPositions]="ex._keepCharacterPositions || false"
136+
[validation]="ex._validation || false"
137+
[showMaskTyped]="ex._showMaskTyped || false"
138+
[clearIfNotMatch]="ex._clearIfNotMatch"
139+
[hiddenInput]="ex._hiddenInput || null"
140+
[outputTransformFn]="ex._outputTransformFn || outputTransformFn"
141+
[inputTransformFn]="ex._inputTransformFn || inputTransformFn"
142+
class="w-full h-[51px] placeholder:text-white/25 text-full-white py-3 px-5 outline-none bg-black border-b-2px border-b-white rounded-10px focus:border-b-yellow hover:border-b-yellow hover:bg-full-white/25 focus:bg-full-white/25" />
91143
<jsdaddy-open-source-card-content
92-
[value]="ex.control.form().errors() | json"
93-
title="Mask error:"
144+
[value]="ex.control.signalForm.value().value() | isEmpty"
145+
title="Signal Form:"
94146
color="yellow-view" />
95-
}
147+
</div>
148+
149+
<jsdaddy-open-source-card-content [value]="ex._mask" title="Mask:" color="green-view" />
96150
</ng-template>
97151

98152
<ng-template #pipeTemplate>

src/app/options/options.component.ts

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import {
1010
untracked,
1111
viewChildren,
1212
} from '@angular/core';
13-
import { JsonPipe, NgOptimizedImage, NgTemplateOutlet } from '@angular/common';
14-
import { FormsModule } from '@angular/forms';
13+
import { NgOptimizedImage, NgTemplateOutlet } from '@angular/common';
14+
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
1515
import { form, FormField } from '@angular/forms/signals';
1616
import { initialConfig, NgxMaskDirective, NgxMaskPipe } from 'ngx-mask';
1717
import { HighlightModule } from 'ngx-highlightjs';
@@ -36,9 +36,9 @@ import type {
3636
standalone: true,
3737
providers: [ScrollService, AccordionService],
3838
imports: [
39-
JsonPipe,
4039
NgTemplateOutlet,
4140
FormsModule,
41+
ReactiveFormsModule,
4242
FormField,
4343
HighlightModule,
4444
NgxMaskDirective,
@@ -79,7 +79,7 @@ export class OptionsComponent {
7979
public readonly activeCardId = toSignal(this.scrollService.activeCard$);
8080

8181
public constructor() {
82-
// Effect to create FieldTrees when config changes
82+
// Effect to create all form types when config changes
8383
// This runs in injection context (constructor)
8484
effect(() => {
8585
const configs = this.cardExamplesConfig();
@@ -89,24 +89,35 @@ export class OptionsComponent {
8989
}
9090

9191
untracked(() => {
92-
runInInjectionContext(this.injector, () => {
93-
const runtimeExamples = configs.map((config) => {
94-
if ('_pipe' in config) {
95-
return config;
96-
}
97-
// Create FieldTree from config (form() requires injection context)
98-
const modelSignal = signal<string | null>(config.control.initialValue);
99-
return {
100-
...config,
101-
control: {
102-
form: form(modelSignal),
103-
model: config.control.model,
104-
},
105-
} as TExample<MaskOptions>;
106-
});
92+
const runtimeExamples = configs.map((config) => {
93+
if ('_pipe' in config) {
94+
return config;
95+
}
96+
const initialValue = config.control.initialValue;
10797

108-
this.cardExamples.set(runtimeExamples);
98+
// Create FormControl for Reactive Forms
99+
const formControl = new FormControl<string | null>(initialValue);
100+
101+
// Create signal for ngModel (Template-driven)
102+
const modelSignal = signal<string | null>(initialValue);
103+
104+
// Create FieldTree for Signal Forms (requires injection context)
105+
const signalFormModel = signal({ value: initialValue });
106+
const signalForm = runInInjectionContext(this.injector, () =>
107+
form(signalFormModel)
108+
);
109+
110+
return {
111+
...config,
112+
control: {
113+
formControl,
114+
model: modelSignal,
115+
signalForm,
116+
},
117+
} as TExample<MaskOptions>;
109118
});
119+
120+
this.cardExamples.set(runtimeExamples);
110121
});
111122
});
112123

src/libraries

0 commit comments

Comments
 (0)