@firestitch/form 18.0.28 → 18.0.29

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.
@@ -263,6 +263,9 @@ class FsFormBaseDirective {
263
263
  ...this._buttons.toArray(),
264
264
  button,
265
265
  ]);
266
+ // QueryList.reset() does not emit `changes`; notify so subscribers (e.g. the
267
+ // form's dirty-submit-button tracking) react to buttons being registered.
268
+ this._buttons.notifyOnChanges();
266
269
  }
267
270
  removeButton(button) {
268
271
  if (this._activeSubmitButton === button) {
@@ -272,6 +275,7 @@ class FsFormBaseDirective {
272
275
  ...this._buttons.toArray()
273
276
  .filter((item) => (button !== item)),
274
277
  ]);
278
+ this._buttons.notifyOnChanges();
275
279
  }
276
280
  _registerConfirmTabs() {
277
281
  this._registerConfirmTabGroups(this._tabGroups.toArray());
@@ -823,10 +827,20 @@ class FsFormDirective extends FsFormBaseDirective {
823
827
  .buttons.changes
824
828
  .pipe(takeUntil(this.destroy$))
825
829
  .subscribe(() => {
826
- this._updateDirtySubmitButtons();
830
+ // Buttons (de)register during change detection (their ngOnInit/ngOnDestroy),
831
+ // so defer to a microtask to update the disabled state outside that pass and
832
+ // avoid ExpressionChangedAfterItHasBeenChecked on the button's disabled binding.
833
+ Promise.resolve().then(() => this._updateDirtySubmitButtons());
827
834
  });
835
+ // The initial content buttons register (addButton) before the subscription above
836
+ // exists, so their `changes` emission is missed. Apply the initial pristine/dirty
837
+ // disabled state once, deferred so it runs after the current change-detection pass.
838
+ Promise.resolve().then(() => this._updateDirtySubmitButtons());
828
839
  }
829
840
  _updateDirtySubmitButtons() {
841
+ if (!this.ngForm) {
842
+ return;
843
+ }
830
844
  this._getFormGroup()
831
845
  .buttons
832
846
  .filter((button) => button.submit)
@@ -957,8 +971,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImpor
957
971
  class FsButtonDirective {
958
972
  name;
959
973
  dirtySubmit = true;
974
+ set submitDisabled(value) {
975
+ this._submitDisabled = value;
976
+ this._applyDisabled();
977
+ }
978
+ get submitDisabled() {
979
+ return this._submitDisabled;
980
+ }
960
981
  transitionStyle = null;
961
982
  submit = false;
983
+ _submitDisabled = false;
984
+ _formDisabled = false;
962
985
  _previousDisabled = false;
963
986
  _destroy$ = new Subject();
964
987
  _formGroup = inject(FsFormGroupDirective, { optional: true });
@@ -969,7 +992,12 @@ class FsButtonDirective {
969
992
  _formBase;
970
993
  ngOnInit() {
971
994
  this.submit = this._elementRef.nativeElement.getAttribute('type') === 'submit';
972
- this._formBase = this._form || this._formGroup;
995
+ // Prefer the enclosing form group over the form: the form's own button
996
+ // management (dirty-submit tracking, activeSubmitButton, reset) resolves its
997
+ // registry via `_formGroup || this`, so buttons must register with the same
998
+ // owner. Registering with the form while the form reads the group leaves the
999
+ // group's button list empty and the submit button never gets dirty-disabled.
1000
+ this._formBase = this._formGroup || this._form;
973
1001
  if (this._formBase) {
974
1002
  this._formBase.addButton(this);
975
1003
  if (this.submit) {
@@ -991,16 +1019,16 @@ class FsButtonDirective {
991
1019
  disable() {
992
1020
  if (this._matButton && !this._formBase.activeSubmitButton) {
993
1021
  this._previousDisabled = this._matButton.disabled;
994
- this._matButton.disabled = true;
1022
+ this._formDisabled = true;
995
1023
  this._matButton.disableRipple = true;
996
- this._cdRef.markForCheck();
1024
+ this._applyDisabled();
997
1025
  }
998
1026
  }
999
1027
  enable() {
1000
1028
  if (this._matButton) {
1001
- this._matButton.disabled = false;
1029
+ this._formDisabled = false;
1002
1030
  this._matButton.disableRipple = true;
1003
- this._cdRef.markForCheck();
1031
+ this._applyDisabled();
1004
1032
  }
1005
1033
  }
1006
1034
  process() {
@@ -1048,6 +1076,15 @@ class FsButtonDirective {
1048
1076
  this._destroy$.complete();
1049
1077
  this._formBase?.removeButton(this);
1050
1078
  }
1079
+ // Effective disabled combines the consumer-controlled submitDisabled input with
1080
+ // the form's submit-lifecycle state, so form enable/disable never clobbers an
1081
+ // explicit [submitDisabled] and the two channels stay independent.
1082
+ _applyDisabled() {
1083
+ if (this._matButton) {
1084
+ this._matButton.disabled = this._submitDisabled || this._formDisabled;
1085
+ this._cdRef.markForCheck();
1086
+ }
1087
+ }
1051
1088
  _resetClass() {
1052
1089
  this.element.classList
1053
1090
  .remove('fs-form-submit-button-success', 'fs-form-submit-button-error', 'fs-form-submit-button-process');
@@ -1078,7 +1115,7 @@ class FsButtonDirective {
1078
1115
  }
1079
1116
  }
1080
1117
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: FsButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1081
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.7", type: FsButtonDirective, isStandalone: true, selector: "[mat-raised-button],[mat-button],[mat-flat-button],[mat-stroked-button]", inputs: { name: "name", dirtySubmit: "dirtySubmit" }, host: { properties: { "style.transition": "this.transitionStyle" } }, ngImport: i0 });
1118
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.7", type: FsButtonDirective, isStandalone: true, selector: "[mat-raised-button],[mat-button],[mat-flat-button],[mat-stroked-button]", inputs: { name: "name", dirtySubmit: "dirtySubmit", submitDisabled: "submitDisabled" }, host: { properties: { "style.transition": "this.transitionStyle" } }, ngImport: i0 });
1082
1119
  }
1083
1120
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: FsButtonDirective, decorators: [{
1084
1121
  type: Directive,
@@ -1090,6 +1127,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImpor
1090
1127
  type: Input
1091
1128
  }], dirtySubmit: [{
1092
1129
  type: Input
1130
+ }], submitDisabled: [{
1131
+ type: Input
1093
1132
  }], transitionStyle: [{
1094
1133
  type: HostBinding,
1095
1134
  args: ['style.transition']
@@ -1174,7 +1213,7 @@ class FsFormDialogActionsComponent {
1174
1213
  this._destroy$.complete();
1175
1214
  }
1176
1215
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: FsFormDialogActionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1177
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.7", type: FsFormDialogActionsComponent, isStandalone: true, selector: "fs-form-dialog-actions", inputs: { save: "save", create: "create", close: "close", done: "done", closeData: "closeData", name: "name" }, ngImport: i0, template: "<div\n class=\"form-buttons\"\n [ngClass]=\"{ 'save-create': save || create }\">\n @if (save || create) {\n <button\n mat-button\n type=\"submit\"\n color=\"primary\"\n [name]=\"name\">\n {{ create ? 'Create' : 'Save' }}\n </button>\n <button\n mat-button\n type=\"button\"\n class=\"cancel-button\"\n [disabled]=\"close && !dirty && !create\"\n [mat-dialog-close]=\"null\">\n Cancel\n </button>\n }\n @if (done) {\n <button\n mat-button\n type=\"button\"\n color=\"primary\"\n [mat-dialog-close]=\"null\">\n Done\n </button>\n }\n @if (save || create) {\n <ng-template [ngTemplateOutlet]=\"content\"></ng-template>\n }\n @if (close) {\n <button\n mat-button\n type=\"button\"\n class=\"close-button\"\n fsFormDialogClose\n [closeData]=\"closeData\"\n [color]=\"dirty ? 'basic' : 'primary'\">\n Close\n </button>\n }\n @if (!(save || create)) {\n <ng-template [ngTemplateOutlet]=\"content\"></ng-template>\n }\n</div>\n<ng-template #content>\n <ng-content></ng-content>\n</ng-template>", styles: [":host{width:100%}.form-buttons.save-create .close-button{float:right}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: FsButtonDirective, selector: "[mat-raised-button],[mat-button],[mat-flat-button],[mat-stroked-button]", inputs: ["name", "dirtySubmit"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: FsFormDialogCloseDirective, selector: "[fsFormDialogClose],[fs-form-dialog-close]", inputs: ["closeData"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1216
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.7", type: FsFormDialogActionsComponent, isStandalone: true, selector: "fs-form-dialog-actions", inputs: { save: "save", create: "create", close: "close", done: "done", closeData: "closeData", name: "name" }, ngImport: i0, template: "<div\n class=\"form-buttons\"\n [ngClass]=\"{ 'save-create': save || create }\">\n @if (save || create) {\n <button\n mat-button\n type=\"submit\"\n color=\"primary\"\n [name]=\"name\">\n {{ create ? 'Create' : 'Save' }}\n </button>\n <button\n mat-button\n type=\"button\"\n class=\"cancel-button\"\n [disabled]=\"close && !dirty && !create\"\n [mat-dialog-close]=\"null\">\n Cancel\n </button>\n }\n @if (done) {\n <button\n mat-button\n type=\"button\"\n color=\"primary\"\n [mat-dialog-close]=\"null\">\n Done\n </button>\n }\n @if (save || create) {\n <ng-template [ngTemplateOutlet]=\"content\"></ng-template>\n }\n @if (close) {\n <button\n mat-button\n type=\"button\"\n class=\"close-button\"\n fsFormDialogClose\n [closeData]=\"closeData\"\n [color]=\"dirty ? 'basic' : 'primary'\">\n Close\n </button>\n }\n @if (!(save || create)) {\n <ng-template [ngTemplateOutlet]=\"content\"></ng-template>\n }\n</div>\n<ng-template #content>\n <ng-content></ng-content>\n</ng-template>", styles: [":host{width:100%}.form-buttons.save-create .close-button{float:right}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: FsButtonDirective, selector: "[mat-raised-button],[mat-button],[mat-flat-button],[mat-stroked-button]", inputs: ["name", "dirtySubmit", "submitDisabled"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: FsFormDialogCloseDirective, selector: "[fsFormDialogClose],[fs-form-dialog-close]", inputs: ["closeData"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1178
1217
  }
1179
1218
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: FsFormDialogActionsComponent, decorators: [{
1180
1219
  type: Component,