@webilix/ngx-form-m3 0.0.14 → 0.0.16

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.
@@ -128,23 +128,21 @@ class FormErrorDirective {
128
128
  elementRef;
129
129
  formGroupDirective;
130
130
  onSubmit() {
131
- if (this.formGroupDirective && this.formGroupDirective.control.invalid) {
132
- const invalidControl = this.elementRef.nativeElement.querySelector('.ng-invalid');
131
+ setTimeout(() => {
132
+ const invalidControl = this.elementRef.nativeElement.querySelector('.mat-form-field-invalid');
133
133
  if (invalidControl)
134
134
  return this.scrollToElement(invalidControl);
135
- }
136
- const invalidControl = this.elementRef.nativeElement.querySelector('.ngx-form-invalid');
137
- if (invalidControl)
138
- this.scrollToElement(invalidControl);
135
+ }, 150);
139
136
  }
140
137
  constructor(elementRef, formGroupDirective) {
141
138
  this.elementRef = elementRef;
142
139
  this.formGroupDirective = formGroupDirective;
143
140
  }
141
+ getElementTop(element) {
142
+ return element.getBoundingClientRect().top + window.scrollY - 100;
143
+ }
144
144
  scrollToElement(element) {
145
- const labelOffset = 150;
146
- const top = element.getBoundingClientRect().top + window.scrollY - labelOffset;
147
- window.scroll({ top, left: 0, behavior: 'smooth' });
145
+ window.scroll({ top: this.getElementTop(element), behavior: 'smooth' });
148
146
  }
149
147
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: FormErrorDirective, deps: [{ token: i0.ElementRef }, { token: i1.FormGroupDirective, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
150
148
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.5", type: FormErrorDirective, isStandalone: true, selector: "form.ngx-form", host: { listeners: { "submit": "onSubmit()" } }, ngImport: i0 });
@@ -193,6 +191,10 @@ class InputErrorPipe {
193
191
  return `زمان باید برابر یا بعد از ${value} انتخاب شده باشد.`;
194
192
  case 'maxmoment':
195
193
  return `زمان باید برابر یا قبل از ${value} انتخاب شده باشد.`;
194
+ case 'mincount':
195
+ return `انتخاب حداقل ${Helper.NUMBER.format(value)} گزینه الزامی است.`;
196
+ case 'maxcount':
197
+ return `امکان انتخاب بیشتر از ${Helper.NUMBER.format(value)} گزینه وجود ندارد.`;
196
198
  case 'pattern':
197
199
  switch (type) {
198
200
  case 'EMAIL':
@@ -467,6 +469,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.5", ngImpor
467
469
  args: [{ required: true }]
468
470
  }] } });
469
471
 
472
+ const MaxCountValidator = (max) => {
473
+ return (formControl) => {
474
+ const value = Array.isArray(formControl.value) ? formControl.value : [];
475
+ return value.length > max ? { maxcount: max } : null;
476
+ };
477
+ };
478
+
479
+ const MinCountValidator = (min) => {
480
+ return (formControl) => {
481
+ const value = Array.isArray(formControl.value) ? formControl.value : [];
482
+ return value.length === 0 ? { required: true } : value.length < min ? { mincount: min } : null;
483
+ };
484
+ };
485
+
470
486
  const MaxDateValidator = (max) => {
471
487
  const jalali = JalaliDateTime();
472
488
  return (formControl) => {
@@ -835,6 +851,59 @@ class InputMomentMethods extends InputMethods {
835
851
  }
836
852
  }
837
853
 
854
+ class InputMultiSelectComponent {
855
+ listHeight;
856
+ formControl = inject(INPUT_CONTROL);
857
+ input = inject(INPUT_TYPE);
858
+ config = inject(INPUT_CONFIG);
859
+ values;
860
+ isButtonDisabled;
861
+ ids = this.formControl.value || [];
862
+ ngOnInit() {
863
+ this.listHeight = `${this.input.listMaxHeight || 310}px`;
864
+ }
865
+ toggleValue(id) {
866
+ if (!this.ids.includes(id))
867
+ this.ids.push(id);
868
+ else
869
+ this.ids = this.ids.filter((i) => i !== id);
870
+ this.formControl.setValue(this.ids.length === 0 ? null : this.ids);
871
+ this.formControl.markAsTouched();
872
+ }
873
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: InputMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
874
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.5", type: InputMultiSelectComponent, isStandalone: true, selector: "ng-component", inputs: { values: "values", isButtonDisabled: "isButtonDisabled" }, host: { attributes: { "selector": "input-multi-select" }, properties: { "style.--listHeight": "this.listHeight" } }, ngImport: i0, template: "<mat-form-field [appearance]=\"input.appearance || config.appearance\">\n @if (formControl.invalid) { <mat-error>{{ formControl.errors | InputErrorPipe : input.type }}</mat-error> }\n\n <!-- HINT -->\n @if (input.hint) { <mat-hint>{{ input.hint }}</mat-hint> }\n\n <!-- BUTTON -->\n @if (input.button) {\n <span matIconSuffix>\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"isButtonDisabled\"\n (click)=\"input.button.onClick(values)\"\n [tabIndex]=\"-1\"\n >\n <mat-icon [style.color]=\"isButtonDisabled ? undefined : input.button.color\">\n {{ input.button.icon }}\n </mat-icon>\n </button>\n </span>\n }\n\n <!-- INPUT -->\n <input matInput type=\"text\" [name]=\"input.name\" [formControl]=\"formControl\" [style.display]=\"'none !important'\" />\n <div class=\"ngx-helper-form-m3-multi-select-input\" [class.ngx-form-m3-disabled-input]=\"formControl.disabled\">\n <div class=\"title\">{{ input.title }}</div>\n <div class=\"options\">\n @for (item of input.options; track $index) {\n <div>\n <div class=\"option\" (click)=\"toggleValue(item.id)\">\n <mat-icon>{{ ids.includes(item.id) ? 'check_box' : 'check_box_outline_blank' }}</mat-icon>\n <div class=\"message\" [class.ngx-form-m3-en]=\"!!input.english\">{{ item.title }}</div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- DESCRIPTION -->\n @if (input.description) {\n <div\n class=\"ngx-form-m3-input-description\"\n [class.ngx-form-m3-disabled-input]=\"formControl.disabled\"\n [innerHTML]=\"input.description | MultiLinePipe\"\n ></div>\n }\n</mat-form-field>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: i3$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "pipe", type: InputErrorPipe, name: "InputErrorPipe" }, { kind: "pipe", type: MultiLinePipe, name: "MultiLinePipe" }] });
875
+ }
876
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.5", ngImport: i0, type: InputMultiSelectComponent, decorators: [{
877
+ type: Component,
878
+ args: [{ host: { selector: 'input-multi-select' }, imports: [ReactiveFormsModule, MatFormField, MatIcon, MatIconButton, MatInputModule, InputErrorPipe, MultiLinePipe], template: "<mat-form-field [appearance]=\"input.appearance || config.appearance\">\n @if (formControl.invalid) { <mat-error>{{ formControl.errors | InputErrorPipe : input.type }}</mat-error> }\n\n <!-- HINT -->\n @if (input.hint) { <mat-hint>{{ input.hint }}</mat-hint> }\n\n <!-- BUTTON -->\n @if (input.button) {\n <span matIconSuffix>\n <button\n mat-icon-button\n type=\"button\"\n [disabled]=\"isButtonDisabled\"\n (click)=\"input.button.onClick(values)\"\n [tabIndex]=\"-1\"\n >\n <mat-icon [style.color]=\"isButtonDisabled ? undefined : input.button.color\">\n {{ input.button.icon }}\n </mat-icon>\n </button>\n </span>\n }\n\n <!-- INPUT -->\n <input matInput type=\"text\" [name]=\"input.name\" [formControl]=\"formControl\" [style.display]=\"'none !important'\" />\n <div class=\"ngx-helper-form-m3-multi-select-input\" [class.ngx-form-m3-disabled-input]=\"formControl.disabled\">\n <div class=\"title\">{{ input.title }}</div>\n <div class=\"options\">\n @for (item of input.options; track $index) {\n <div>\n <div class=\"option\" (click)=\"toggleValue(item.id)\">\n <mat-icon>{{ ids.includes(item.id) ? 'check_box' : 'check_box_outline_blank' }}</mat-icon>\n <div class=\"message\" [class.ngx-form-m3-en]=\"!!input.english\">{{ item.title }}</div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- DESCRIPTION -->\n @if (input.description) {\n <div\n class=\"ngx-form-m3-input-description\"\n [class.ngx-form-m3-disabled-input]=\"formControl.disabled\"\n [innerHTML]=\"input.description | MultiLinePipe\"\n ></div>\n }\n</mat-form-field>\n" }]
879
+ }], propDecorators: { listHeight: [{
880
+ type: HostBinding,
881
+ args: ['style.--listHeight']
882
+ }], values: [{
883
+ type: Input,
884
+ args: [{ required: true }]
885
+ }], isButtonDisabled: [{
886
+ type: Input,
887
+ args: [{ required: true }]
888
+ }] } });
889
+
890
+ class InputMultiSelectMethods extends InputMethods {
891
+ control(input, validators) {
892
+ const ids = input.options.map((option) => option.id);
893
+ const value = (input.value || []).filter((id) => ids.includes(id));
894
+ if (input.minCount)
895
+ validators.push(MinCountValidator(input.minCount));
896
+ if (input.maxCount)
897
+ validators.push(MaxCountValidator(input.maxCount));
898
+ return new FormControl(value, validators);
899
+ }
900
+ value(value, input) {
901
+ const ids = input.options.map((option) => option.id);
902
+ value = (Array.isArray(value) ? value : []).filter((id) => ids.includes(id));
903
+ return value.length > 0 ? value : null;
904
+ }
905
+ }
906
+
838
907
  class InputNameComponent {
839
908
  formControl = inject(INPUT_CONTROL);
840
909
  input = inject(INPUT_TYPE);
@@ -1255,6 +1324,7 @@ const InputInfo = {
1255
1324
  IP: { title: 'آدرس آی‌پی', methods: new InputIpMethods(), component: InputIpComponent },
1256
1325
  MOBILE: { title: 'موبایل', methods: new InputMobileMethods(), component: InputMobileComponent },
1257
1326
  MOMENT: { title: 'زمان', methods: new InputMomentMethods(), component: InputMomentComponent },
1327
+ 'MULTI-SELECT': { title: 'چند انتخابی', methods: new InputMultiSelectMethods(), component: InputMultiSelectComponent },
1258
1328
  NAME: { title: 'نام و نام خانوادگی', methods: new InputNameMethods(), component: InputNameComponent },
1259
1329
  NUMBER: { title: 'مقدار عددی', methods: new InputNumberMethods(), component: InputNumberComponent },
1260
1330
  PASSWORD: { title: 'کلمه عبور', methods: new InputPasswordMethods(), component: InputPasswordComponent },
@@ -1455,7 +1525,9 @@ class NgxFormComponent {
1455
1525
  }
1456
1526
  setInput(input) {
1457
1527
  const name = input.name;
1458
- const validators = input.optional || ('readonly' in input && input.readonly) ? [] : [Validators.required];
1528
+ const optional = 'optional' in input && !!input.optional;
1529
+ const readonly = 'readonly' in input && !!input.readonly;
1530
+ const validators = !optional && !readonly ? [Validators.required] : [];
1459
1531
  this.formGroup.setControl(name, InputInfo[input.type].methods.control(input, validators));
1460
1532
  }
1461
1533
  checkInputs() {