@firestitch/form 18.0.29 → 18.0.31

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.
Files changed (40) hide show
  1. package/app/components/form-dialog-actions/form-dialog-actions.component.d.ts +17 -2
  2. package/app/directives/button.directive.d.ts +9 -1
  3. package/app/directives/confirm-tabs/confirm-tabs.directive.d.ts +32 -0
  4. package/app/directives/confirm-tabs/index.d.ts +1 -0
  5. package/app/directives/form/form.directive.d.ts +89 -29
  6. package/app/directives/form-base/form-base.directive.d.ts +86 -6
  7. package/app/directives/form-container/form-container.directive.d.ts +80 -0
  8. package/app/directives/form-container/index.d.ts +1 -0
  9. package/app/directives/index.d.ts +2 -1
  10. package/app/fs-form.module.d.ts +3 -2
  11. package/app/helpers/index.d.ts +1 -0
  12. package/app/helpers/within-same-dialog.d.ts +18 -0
  13. package/app/interfaces/form-owner.d.ts +17 -0
  14. package/app/interfaces/index.d.ts +1 -0
  15. package/app/services/fsform.service.d.ts +5 -5
  16. package/esm2022/app/components/form-dialog-actions/form-dialog-actions.component.mjs +50 -17
  17. package/esm2022/app/directives/button.directive.mjs +26 -9
  18. package/esm2022/app/directives/confirm-tabs/confirm-tabs.directive.mjs +50 -0
  19. package/esm2022/app/directives/confirm-tabs/index.mjs +2 -0
  20. package/esm2022/app/directives/form/form.directive.mjs +247 -241
  21. package/esm2022/app/directives/form-base/form-base.directive.mjs +255 -8
  22. package/esm2022/app/directives/form-container/form-container.directive.mjs +164 -0
  23. package/esm2022/app/directives/form-container/index.mjs +2 -0
  24. package/esm2022/app/directives/index.mjs +3 -2
  25. package/esm2022/app/fs-form.module.mjs +11 -6
  26. package/esm2022/app/guards/form-deactivate.guard.mjs +1 -1
  27. package/esm2022/app/helpers/index.mjs +2 -1
  28. package/esm2022/app/helpers/within-same-dialog.mjs +19 -0
  29. package/esm2022/app/interfaces/form-owner.mjs +2 -0
  30. package/esm2022/app/interfaces/index.mjs +2 -1
  31. package/esm2022/app/services/fsform.service.mjs +10 -7
  32. package/esm2022/public_api.mjs +3 -2
  33. package/fesm2022/firestitch-form.mjs +811 -320
  34. package/fesm2022/firestitch-form.mjs.map +1 -1
  35. package/package.json +1 -1
  36. package/public_api.d.ts +3 -1
  37. package/app/directives/form-group/form-group.directive.d.ts +0 -15
  38. package/app/directives/form-group/index.d.ts +0 -1
  39. package/esm2022/app/directives/form-group/form-group.directive.mjs +0 -35
  40. package/esm2022/app/directives/form-group/index.mjs +0 -2
@@ -1,18 +1,33 @@
1
1
  import { OnDestroy, OnInit } from '@angular/core';
2
2
  import * as i0 from "@angular/core";
3
3
  export declare class FsFormDialogActionsComponent implements OnInit, OnDestroy {
4
- private _form;
5
- private _cdRef;
4
+ /** Leave unset inside an `fsFormContainer` to follow whether it has anything to save. */
6
5
  save: boolean;
7
6
  create: boolean;
8
7
  close: boolean;
8
+ /** Leave unset inside an `fsFormContainer` to fill in whenever Save does not apply. */
9
9
  done: boolean;
10
10
  closeData: any;
11
11
  name: string;
12
12
  dirty: boolean;
13
13
  private _destroy$;
14
+ private _dialogRef;
15
+ private _container;
16
+ private _form;
17
+ private _cdRef;
18
+ /**
19
+ * Inside a container these default to whether anything mounted can actually
20
+ * save, so a footer left as a bare `<fs-form-dialog-actions>` swaps Save for
21
+ * Done as tabs change without the dialog listing which tabs are savable. An
22
+ * explicit binding always wins, and outside a container Save stays on as it
23
+ * always did.
24
+ */
25
+ get showSave(): boolean;
26
+ get showDone(): boolean;
14
27
  ngOnInit(): void;
15
28
  ngOnDestroy(): void;
29
+ /** Keeps a DI-resolved owner only when it belongs to these actions' dialog. */
30
+ private _ownDialogOnly;
16
31
  static ɵfac: i0.ɵɵFactoryDeclaration<FsFormDialogActionsComponent, never>;
17
32
  static ɵcmp: i0.ɵɵComponentDeclaration<FsFormDialogActionsComponent, "fs-form-dialog-actions", never, { "save": { "alias": "save"; "required": false; }; "create": { "alias": "create"; "required": false; }; "close": { "alias": "close"; "required": false; }; "done": { "alias": "done"; "required": false; }; "closeData": { "alias": "closeData"; "required": false; }; "name": { "alias": "name"; "required": false; }; }, {}, never, ["*"], true, never>;
18
33
  }
@@ -11,7 +11,8 @@ export declare class FsButtonDirective implements OnInit, OnDestroy {
11
11
  private _formDisabled;
12
12
  private _previousDisabled;
13
13
  private _destroy$;
14
- private _formGroup;
14
+ private _dialogRef;
15
+ private _container;
15
16
  private _form;
16
17
  private _matButton;
17
18
  private _elementRef;
@@ -27,6 +28,13 @@ export declare class FsButtonDirective implements OnInit, OnDestroy {
27
28
  get element(): any;
28
29
  reset(): void;
29
30
  ngOnDestroy(): void;
31
+ /**
32
+ * Keeps a DI-resolved owner only when it belongs to this button's dialog. A
33
+ * dialog opened with `viewContainerRef` resolves through to whatever opened
34
+ * it, so without this the inner dialog's Save would register with - and
35
+ * submit - the outer dialog's form set.
36
+ */
37
+ private _ownDialogOnly;
30
38
  private _applyDisabled;
31
39
  private _resetClass;
32
40
  private _getSvg;
@@ -0,0 +1,32 @@
1
+ import { OnInit } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ /**
4
+ * Brings a tab group into the enclosing form set's unsaved-changes confirm.
5
+ *
6
+ * An owner picks up tab groups with `@ContentChildren(MatTabGroup)`, which reaches
7
+ * through its own projected content - including lazily mounted tab bodies - but
8
+ * stops at a child component's view. So a tab group declared inside a child
9
+ * component's own template is invisible to the dialog that contains it, and
10
+ * switching between those tabs never prompts even though switching the outer ones
11
+ * does. That is not a shape worth avoiding: a dialog whose Profile tab is its own
12
+ * component, with its own tabs inside, is the normal way to keep a large dialog
13
+ * readable.
14
+ *
15
+ * DI does cross the boundary a content query cannot, so put this on the nested
16
+ * group and it registers itself with the owner above it:
17
+ *
18
+ * <mat-tab-group fsFormConfirmTabs [(selected)]="tab">
19
+ *
20
+ * Confirming stays gated on the owner's `confirm` and `confirmTabs` inputs, so
21
+ * this only opts the group in - it never forces a prompt on.
22
+ */
23
+ export declare class FsFormConfirmTabsDirective implements OnInit {
24
+ private _tabGroup;
25
+ private _dialogRef;
26
+ private _container;
27
+ private _form;
28
+ ngOnInit(): void;
29
+ private _ownDialogOnly;
30
+ static ɵfac: i0.ɵɵFactoryDeclaration<FsFormConfirmTabsDirective, never>;
31
+ static ɵdir: i0.ɵɵDirectiveDeclaration<FsFormConfirmTabsDirective, "mat-tab-group[fsFormConfirmTabs]", never, {}, {}, never, never, true, never>;
32
+ }
@@ -0,0 +1 @@
1
+ export * from './confirm-tabs.directive';
@@ -3,34 +3,37 @@ import { NgForm } from '@angular/forms';
3
3
  import { Observable } from 'rxjs';
4
4
  import { FsFormBaseDirective } from '../form-base';
5
5
  import { ConfirmResult } from './../../enums/confirm-result';
6
- import { ConfirmConfig, SubmittedEvent } from './../../interfaces';
6
+ import { FormStatus } from './../../enums/form-status';
7
+ import { SubmittedEvent } from './../../interfaces';
7
8
  import { SubmitEvent } from './../../interfaces/submit-event';
8
9
  import * as i0 from "@angular/core";
9
10
  export declare class FsFormDirective extends FsFormBaseDirective implements OnInit, OnDestroy, AfterContentInit, OnChanges {
10
11
  ngForm: NgForm;
11
- private _form;
12
- private _element;
13
- private _message;
14
- private _formGroup;
15
- private _dialogRef;
16
- private _drawerRef;
17
- private _route;
18
12
  wrapperSelector: string;
19
13
  messageSelector: string;
20
14
  hintSelector: string;
21
15
  labelSelector: string;
22
16
  autocomplete: boolean;
23
- shortcuts: boolean;
24
- confirm: ConfirmConfig | boolean;
25
- confirmDialog: boolean;
26
- confirmDrawer: boolean;
27
- confirmBrowser: boolean;
28
17
  dirtySubmitButton: boolean;
29
18
  set submit(submit$: (event: SubmitEvent) => Observable<any>);
30
19
  get submit(): (event: SubmitEvent) => Observable<any>;
31
20
  successDelay: number;
32
21
  errorDelay: number;
33
- deactivationGuard: boolean;
22
+ /**
23
+ * Whether this form joins the set around it - the `fsForm` it is nested inside,
24
+ * or the `fsFormContainer` enclosing it.
25
+ *
26
+ * Forms in a set behave as one submittable unit: the outermost form is what the
27
+ * submit buttons drive, and validation, dirty state, the unsaved-changes
28
+ * confirm and the pristine reset all span the set. That is what lets a form
29
+ * wrap only one tab's fields while the dialog footer stays outside it.
30
+ *
31
+ * Set `[link]="false"` for a panel that owns its own saving - an instant-save
32
+ * side panel, say. It keeps every fsForm feature for itself, but its dirty
33
+ * state never enables the outer Save button, its validation never blocks the
34
+ * outer submit, and the outer submit never touches it.
35
+ */
36
+ link: boolean;
34
37
  submitEvent: EventEmitter<SubmitEvent>;
35
38
  invalid: EventEmitter<SubmitEvent>;
36
39
  valid: EventEmitter<SubmitEvent>;
@@ -39,19 +42,73 @@ export declare class FsFormDirective extends FsFormBaseDirective implements OnIn
39
42
  cleared: EventEmitter<SubmitEvent>;
40
43
  fsFormClass: boolean;
41
44
  private _registerControl;
42
- private _dialogBackdropEscape;
43
45
  private _snapshot;
44
- private _activatedRouteConfig;
45
46
  private _status$;
46
47
  private _submit$;
48
+ private _linkedForms;
49
+ private _containerParent;
50
+ private _dirtyChange$;
51
+ private _message;
52
+ private _container;
53
+ private _ancestorForm;
54
+ /**
55
+ * Emits whenever the set's dirty state may have moved. Anything outside the
56
+ * form that renders off it - the dialog actions, say - can't watch
57
+ * `ngForm.valueChanges` on its own, because the edits happen in a nested form's
58
+ * NgForm, not this one's.
59
+ */
60
+ get dirtyChange$(): Observable<void>;
61
+ get submitted$(): Observable<SubmittedEvent>;
62
+ get status$(): Observable<FormStatus>;
63
+ get element(): HTMLElement;
64
+ /** True when a Save button driving this form would have something to run. */
65
+ get hasSubmit(): boolean;
66
+ /**
67
+ * The form this one is linked into, or null when it is a root - either because
68
+ * nothing encloses it or because `[link]="false"` cut it loose. A container
69
+ * supplies the parent when the forms are siblings rather than nested.
70
+ */
71
+ get parentForm(): FsFormDirective;
72
+ /** The outermost form of this set. Submit buttons drive this one. */
73
+ get rootForm(): FsFormDirective;
74
+ /** This form plus every form linked into it, at any depth. */
75
+ get linkedForms(): FsFormDirective[];
76
+ /** True when this form or anything linked into it has unsaved changes. */
77
+ get dirtyLinked(): boolean;
78
+ /**
79
+ * True when this form or anything linked into it has a submit handler - i.e.
80
+ * there is something for a Save button to do. A dialog footer can bind to this
81
+ * to swap Save for Done as the mounted tab changes, instead of hard-coding
82
+ * which tab names happen to be savable.
83
+ */
84
+ get submits(): boolean;
47
85
  get submitting(): boolean;
48
86
  get validating(): boolean;
49
87
  get completing(): boolean;
88
+ /**
89
+ * True when nothing above this form speaks for the set it belongs to. Reads the
90
+ * raw injections rather than `parentForm`, so an unlinked panel inside a dialog
91
+ * still defers to whatever encloses it instead of registering a second backdrop
92
+ * confirm on the same dialog.
93
+ */
94
+ private get _owns();
50
95
  private get _submitEvent();
51
96
  private get _formValidState$();
52
97
  private get _formInvalidState$();
53
98
  private get _submitter();
54
99
  ngOnInit(): void;
100
+ /**
101
+ * Take a nested form into this one's set. Called by the child on init, or by a
102
+ * container linking its siblings - consumers never call it directly.
103
+ */
104
+ linkForm(form: FsFormDirective): void;
105
+ unlinkForm(form: FsFormDirective): void;
106
+ /**
107
+ * Adopt a parent that DI could not supply. Only a container calls this, to link
108
+ * forms that are siblings in the template into one set.
109
+ */
110
+ linkTo(form: FsFormDirective): void;
111
+ unlinkFrom(): void;
55
112
  ngOnChanges(changes: SimpleChanges): void;
56
113
  clearSubmit(submit?: (event: SubmitEvent) => Observable<any>): void;
57
114
  registerSubmit(submit$: () => Observable<any>): void;
@@ -72,22 +129,27 @@ export declare class FsFormDirective extends FsFormBaseDirective implements OnIn
72
129
  enable(): void;
73
130
  disable(): void;
74
131
  validate(): void;
75
- submit$(submitEvent: SubmitEvent): Observable<SubmittedEvent>;
132
+ submit$(submitEvent?: SubmitEvent): Observable<SubmittedEvent>;
76
133
  private _listenSubmit;
77
134
  private _listenFormStatus;
78
- private _listenWindowClose;
79
- private _activeDialog;
80
- private _listenHotKeys;
81
- private _formClose;
82
135
  private _getActiveSubmitButton;
83
- private _elementInForm;
84
136
  private _completeSubmit;
85
- private _getFormGroup;
137
+ /**
138
+ * Whoever holds the submit buttons for this form. A container when one encloses
139
+ * it, otherwise the root of its linked set - the button that saves a tab's form
140
+ * typically sits outside it, down in the dialog footer.
141
+ *
142
+ * Only a form that actually joined the container may drive its buttons. DI
143
+ * hands `_container` to every form inside, including one cut loose with
144
+ * `[link]="false"`, and an instant-save panel is usually `[confirm]="false"`
145
+ * too - so without this it would take the `!confirm` branch below and
146
+ * re-enable the footer's Save button that the linked set had just disabled.
147
+ */
148
+ private _getOwner;
149
+ /** Keeps a DI-resolved owner only when it belongs to this form's dialog. */
150
+ private _ownDialogOnly;
86
151
  private _resetButtons;
87
152
  private _registerConfirm;
88
- private _registerDrawerClose;
89
- private _registerConfirmDrawerClose;
90
- private _registerConfirmDialogBackdropEscape;
91
153
  private _registerAutocomplete;
92
154
  private _registerDirtySubmitButton;
93
155
  private _updateDirtySubmitButtons;
@@ -97,8 +159,6 @@ export declare class FsFormDirective extends FsFormBaseDirective implements OnIn
97
159
  private _disableButtons;
98
160
  private _waitUntilStatusPending;
99
161
  private _handleError;
100
- private _registerCanDeactivateGuard;
101
- private _cleanupCanDeactivate;
102
162
  static ɵfac: i0.ɵɵFactoryDeclaration<FsFormDirective, never>;
103
- static ɵdir: i0.ɵɵDirectiveDeclaration<FsFormDirective, "[fsForm]", ["fsForm"], { "wrapperSelector": { "alias": "wrapperSelector"; "required": false; }; "messageSelector": { "alias": "messageSelector"; "required": false; }; "hintSelector": { "alias": "hintSelector"; "required": false; }; "labelSelector": { "alias": "labelSelector"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; "shortcuts": { "alias": "shortcuts"; "required": false; }; "confirm": { "alias": "confirm"; "required": false; }; "confirmDialog": { "alias": "confirmDialog"; "required": false; }; "confirmDrawer": { "alias": "confirmDrawer"; "required": false; }; "confirmBrowser": { "alias": "confirmBrowser"; "required": false; }; "dirtySubmitButton": { "alias": "dirtySubmitButton"; "required": false; }; "submit": { "alias": "submit"; "required": false; }; "successDelay": { "alias": "successDelay"; "required": false; }; "errorDelay": { "alias": "errorDelay"; "required": false; }; "deactivationGuard": { "alias": "deactivationGuard"; "required": false; }; }, { "submitEvent": "fsForm"; "invalid": "invalid"; "valid": "valid"; "submitted": "submitted"; "reseted": "reseted"; "cleared": "cleared"; }, never, never, true, never>;
163
+ static ɵdir: i0.ɵɵDirectiveDeclaration<FsFormDirective, "[fsForm]", ["fsForm"], { "wrapperSelector": { "alias": "wrapperSelector"; "required": false; }; "messageSelector": { "alias": "messageSelector"; "required": false; }; "hintSelector": { "alias": "hintSelector"; "required": false; }; "labelSelector": { "alias": "labelSelector"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; "dirtySubmitButton": { "alias": "dirtySubmitButton"; "required": false; }; "submit": { "alias": "submit"; "required": false; }; "successDelay": { "alias": "successDelay"; "required": false; }; "errorDelay": { "alias": "errorDelay"; "required": false; }; "link": { "alias": "link"; "required": false; }; }, { "submitEvent": "fsForm"; "invalid": "invalid"; "valid": "valid"; "submitted": "submitted"; "reseted": "reseted"; "cleared": "cleared"; }, never, never, true, never>;
104
164
  }
@@ -1,34 +1,114 @@
1
- import { AfterContentInit, ChangeDetectorRef, NgZone, OnDestroy, QueryList } from '@angular/core';
1
+ import { AfterContentInit, ChangeDetectorRef, ElementRef, NgZone, OnDestroy, OnInit, QueryList } from '@angular/core';
2
+ import { ActivatedRoute, Route } from '@angular/router';
3
+ import { MatDialogRef } from '@angular/material/dialog';
2
4
  import { MatTabGroup } from '@angular/material/tabs';
5
+ import { DrawerRef } from '@firestitch/drawer';
3
6
  import { Observable } from 'rxjs';
4
7
  import { ConfirmResult } from '../../enums';
5
- import { ConfirmConfig } from '../../interfaces';
8
+ import { FormStatus } from '../../enums/form-status';
9
+ import { ConfirmConfig, SubmitEvent, SubmittedEvent } from '../../interfaces';
10
+ import { FsForm } from '../../services/fsform.service';
6
11
  import { FsButtonDirective } from '../button.directive';
12
+ import type { FsFormDirective } from '../form/form.directive';
7
13
  import * as i0 from "@angular/core";
8
- export declare abstract class FsFormBaseDirective implements AfterContentInit, OnDestroy {
14
+ /**
15
+ * Shared by the two things that can own a set of forms: an `fsForm` sitting at
16
+ * the root of its linked set, and an `fsFormContainer` holding forms that are
17
+ * siblings rather than nested.
18
+ *
19
+ * It holds the submit-button registry both of them expose to buttons, and the
20
+ * lifecycle that belongs to the set as a whole rather than to any one form -
21
+ * the dialog backdrop and Escape, Ctrl+S, the browser close prompt, the drawer
22
+ * and tab-switch confirms. Exactly one owner registers that lifecycle; see
23
+ * `_registerOwnerLifecycle`.
24
+ */
25
+ export declare abstract class FsFormBaseDirective implements OnInit, AfterContentInit, OnDestroy {
9
26
  confirmTabs: boolean;
10
- tabGroup: MatTabGroup;
27
+ confirm: ConfirmConfig | boolean;
28
+ confirmDialog: boolean;
29
+ confirmDrawer: boolean;
30
+ confirmBrowser: boolean;
31
+ shortcuts: boolean;
32
+ deactivationGuard: boolean;
11
33
  protected _ngZone: NgZone;
12
34
  protected _cdRef: ChangeDetectorRef;
35
+ protected _element: ElementRef<any>;
36
+ protected _form: FsForm;
37
+ protected _route: ActivatedRoute;
38
+ protected _dialogRef: MatDialogRef<any, any>;
39
+ protected _drawerRef: DrawerRef<any, any>;
40
+ protected _dialogBackdropEscape: boolean;
41
+ protected _activatedRouteConfig: Route | null;
13
42
  private _tabGroups;
14
43
  private _buttons;
15
44
  private _activeSubmitButton;
45
+ private _confirmTabGroups;
16
46
  private _destroy$;
17
47
  abstract submitting: boolean;
18
- abstract confirm: ConfirmConfig | boolean;
48
+ /** True when anything in the owned set has unsaved changes. */
49
+ abstract dirtyLinked: boolean;
50
+ /** Every form in the owned set, at any depth. */
51
+ abstract linkedForms: FsFormDirective[];
52
+ /** Emits whenever the owned set's dirty state may have moved. */
53
+ abstract dirtyChange$: Observable<void>;
54
+ /** Emits once a submit over the owned set has completed successfully. */
55
+ abstract submitted$: Observable<SubmittedEvent>;
56
+ /** Tracks the owned set's submit lifecycle, so the drawer can wait one out. */
57
+ abstract status$: Observable<FormStatus>;
58
+ /**
59
+ * The dialog this owner lives in, or null outside one. Used to keep a form set
60
+ * from spanning a dialog boundary - see `withinSameDialog`.
61
+ */
62
+ get dialogRef(): MatDialogRef<any>;
63
+ ngOnInit(): void;
19
64
  ngAfterContentInit(): void;
20
65
  get destroy$(): Observable<void>;
21
66
  ngOnDestroy(): void;
22
67
  get buttons(): QueryList<FsButtonDirective>;
23
68
  get activeSubmitButton(): FsButtonDirective;
24
69
  set activeSubmitButton(button: FsButtonDirective);
70
+ /**
71
+ * Bring a tab group into this owner's unsaved-changes confirm.
72
+ *
73
+ * `_tabGroups` only reaches groups in this owner's projected content, so a group
74
+ * declared inside a child component's own template has to announce itself.
75
+ * `fsFormConfirmTabs` calls this - consumers never call it directly.
76
+ */
77
+ registerTabGroup(tabGroup: MatTabGroup): void;
25
78
  addButton(button: FsButtonDirective): void;
26
79
  removeButton(button: FsButtonDirective): void;
80
+ /**
81
+ * Everything here acts on the form set as a whole, so exactly one thing may
82
+ * register it. A form linked into a parent defers to that parent; every form
83
+ * inside a container defers to the container. Registering twice would put two
84
+ * confirms on the same backdrop click and two handlers on the same Ctrl+S.
85
+ */
86
+ protected _registerOwnerLifecycle(): void;
87
+ /** The half of the same ownership that needs projected content resolved. */
88
+ protected _registerOwnerContentLifecycle(): void;
89
+ /**
90
+ * Whether the element is inside what this owner covers. Overridden by the
91
+ * container, whose own element is a poor proxy: it may be an `<ng-container>`,
92
+ * which is a comment node and contains nothing.
93
+ */
94
+ protected _containsElement(el: Element): boolean;
95
+ protected _nodeContains(root: Node, el: Element): boolean;
96
+ protected _formClose(): void;
97
+ private _listenWindowClose;
98
+ private _activeDialog;
99
+ private _listenHotKeys;
100
+ private _registerConfirmDialogBackdropEscape;
101
+ private _registerDrawerClose;
102
+ private _registerConfirmDrawerClose;
27
103
  private _registerConfirmTabs;
28
104
  private _registerConfirmTabGroups;
29
105
  private _registerConfirmTabGroup;
106
+ private _registerCanDeactivateGuard;
107
+ private _cleanupCanDeactivate;
30
108
  abstract triggerSubmit(): void;
31
109
  abstract triggerConfirm(): Observable<ConfirmResult>;
110
+ abstract reset(): void;
111
+ abstract submit$(event?: SubmitEvent): Observable<SubmittedEvent>;
32
112
  static ɵfac: i0.ɵɵFactoryDeclaration<FsFormBaseDirective, never>;
33
- static ɵdir: i0.ɵɵDirectiveDeclaration<FsFormBaseDirective, never, never, { "confirmTabs": { "alias": "confirmTabs"; "required": false; }; "tabGroup": { "alias": "tabGroup"; "required": false; }; }, {}, ["_tabGroups"], never, false, never>;
113
+ static ɵdir: i0.ɵɵDirectiveDeclaration<FsFormBaseDirective, never, never, { "confirmTabs": { "alias": "confirmTabs"; "required": false; }; "confirm": { "alias": "confirm"; "required": false; }; "confirmDialog": { "alias": "confirmDialog"; "required": false; }; "confirmDrawer": { "alias": "confirmDrawer"; "required": false; }; "confirmBrowser": { "alias": "confirmBrowser"; "required": false; }; "shortcuts": { "alias": "shortcuts"; "required": false; }; "deactivationGuard": { "alias": "deactivationGuard"; "required": false; }; }, {}, ["_tabGroups"], never, false, never>;
34
114
  }
@@ -0,0 +1,80 @@
1
+ import { Observable } from 'rxjs';
2
+ import { ConfirmResult } from '../../enums';
3
+ import { FormStatus } from '../../enums/form-status';
4
+ import { FsFormOwner, SubmitEvent, SubmittedEvent } from '../../interfaces';
5
+ import { FsFormBaseDirective } from '../form-base';
6
+ import type { FsFormDirective } from '../form/form.directive';
7
+ import * as i0 from "@angular/core";
8
+ /**
9
+ * Bounds a set of forms without being one.
10
+ *
11
+ * An `fsForm` can only reach the forms nested inside it through DI, which forces
12
+ * a wrapper carrying `ngForm` around anything that needs to see them - and that
13
+ * wrapper then owns a real, empty `NgForm` whose control namespace every field
14
+ * below it falls into. A container has no `ngForm` and no controls. It only
15
+ * watches: forms register themselves as they mount, buttons resolve to it, and
16
+ * `fs-form-dialog-actions` reads Save-vs-Done off it. Put it on whatever element
17
+ * already frames the layout - or on an `<ng-container>` for no element at all.
18
+ *
19
+ * Forms in a container are siblings rather than nested, so on registration each
20
+ * one is linked into the first. That reproduces exactly the structure DI nesting
21
+ * builds, which is why validation, dirty state, the unsaved-changes confirm and
22
+ * the submit chain all span a container without any of that machinery knowing
23
+ * containers exist.
24
+ *
25
+ * A form marked `[link]="false"` never registers, so an instant-save panel stays
26
+ * out of the set the same way it does when nested.
27
+ */
28
+ export declare class FsFormContainerDirective extends FsFormBaseDirective implements FsFormOwner {
29
+ private _forms;
30
+ private _formsChange$;
31
+ /** The forms registered into this container, in registration order. */
32
+ get forms(): FsFormDirective[];
33
+ /**
34
+ * The form the rest of the set is linked into, or null while the container
35
+ * holds none - an open tab that has nothing to save, say.
36
+ */
37
+ get rootForm(): FsFormDirective;
38
+ /** Emits whenever a form registers or deregisters. */
39
+ get formsChange$(): Observable<void>;
40
+ get linkedForms(): FsFormDirective[];
41
+ /**
42
+ * True when anything in the container has a submit handler - i.e. there is
43
+ * something for a Save button to do. `fs-form-dialog-actions` reads this to
44
+ * swap Save for Done as the mounted tab changes, so a dialog never has to keep
45
+ * a list of which tabs happen to be savable.
46
+ */
47
+ get submits(): boolean;
48
+ get dirtyLinked(): boolean;
49
+ get submitting(): boolean;
50
+ /**
51
+ * Emits whenever the set's dirty state may have moved, including when the set
52
+ * itself changes. Anything rendering off it - the dialog actions - subscribes
53
+ * once and outlives any particular form, so this has to follow the root across
54
+ * tab switches rather than bind to whichever form happened to be first.
55
+ */
56
+ get dirtyChange$(): Observable<void>;
57
+ get submitted$(): Observable<SubmittedEvent>;
58
+ get status$(): Observable<FormStatus>;
59
+ /**
60
+ * Take a form into the container. Called by the form on init - consumers never
61
+ * call it directly.
62
+ */
63
+ registerForm(form: FsFormDirective): void;
64
+ /**
65
+ * Material attaches an incoming tab body before it detaches the outgoing one,
66
+ * so losing the root is a routine reshuffle rather than teardown. Re-elect and
67
+ * re-link the survivors instead of leaving them pointing at a dead root, and
68
+ * only let go of forms actually registered - clearing unconditionally would
69
+ * drop the form that just arrived.
70
+ */
71
+ deregisterForm(form?: FsFormDirective): void;
72
+ triggerSubmit(): void;
73
+ triggerConfirm(): Observable<ConfirmResult>;
74
+ submit$(event?: SubmitEvent): Observable<SubmittedEvent>;
75
+ reset(): void;
76
+ validate(): void;
77
+ protected _containsElement(el: Element): boolean;
78
+ static ɵfac: i0.ɵɵFactoryDeclaration<FsFormContainerDirective, never>;
79
+ static ɵdir: i0.ɵɵDirectiveDeclaration<FsFormContainerDirective, "fs-form-container,[fsFormContainer]", ["fsFormContainer"], {}, {}, never, never, true, never>;
80
+ }
@@ -0,0 +1 @@
1
+ export * from './form-container.directive';
@@ -1,5 +1,6 @@
1
+ export * from './confirm-tabs';
1
2
  export * from './form';
2
- export * from './form-group';
3
+ export * from './form-container';
3
4
  export * from './validators';
4
5
  export * from './button.directive';
5
6
  export * from './form-dialog-close.directive';
@@ -34,10 +34,11 @@ import * as i31 from "./directives/button.directive";
34
34
  import * as i32 from "./directives/submit-button.directive";
35
35
  import * as i33 from "./directives/form-template.directive";
36
36
  import * as i34 from "./directives/form-template-outlet.directive";
37
- import * as i35 from "./directives/form-group/form-group.directive";
37
+ import * as i35 from "./directives/form-container/form-container.directive";
38
+ import * as i36 from "./directives/confirm-tabs/confirm-tabs.directive";
38
39
  export declare class FsFormModule {
39
40
  static forRoot(): ModuleWithProviders<FsFormModule>;
40
41
  static ɵfac: i0.ɵɵFactoryDeclaration<FsFormModule, never>;
41
- static ɵmod: i0.ɵɵNgModuleDeclaration<FsFormModule, never, [typeof i1.CommonModule, typeof i2.FormsModule, typeof i3.MatButtonModule, typeof i4.MatDialogModule, typeof i4.MatDialogModule, typeof i5.FsDialogModule, typeof i6.FsFormDirective, typeof i7.FsControlDirective, typeof i8.FsFormRequiredDirective, typeof i9.FsFormMinDirective, typeof i10.FsFormMaxDirective, typeof i11.FsFormMinLengthDirective, typeof i12.FsFormMaxLengthDirective, typeof i13.FsFormEmailDirective, typeof i14.FsFormEmailsDirective, typeof i15.FsFormPhoneDirective, typeof i16.FsFormCompareDirective, typeof i17.FsFormIntegerDirective, typeof i18.FsFormNumericDirective, typeof i19.FsFormPatternDirective, typeof i20.FsFormFunctionDirective, typeof i21.FsFormDateRangeDirective, typeof i22.FsFormGreaterDirective, typeof i23.FsFormGreaterEqualDirective, typeof i24.FsFormLesserDirective, typeof i25.FsFormLesserEqualDirective, typeof i26.FsFormUrlDirective, typeof i27.FsFormDialogCloseDirective, typeof i28.FsFormValidateDirective, typeof i29.FsFormDialogActionsComponent, typeof i30.FsFormNoFsValidatorsDirective, typeof i31.FsButtonDirective, typeof i32.FsSubmitButtonDirective, typeof i33.FsFormTemplateDirective, typeof i33.FsFormTemplateDirective, typeof i34.FsFormTemplateOutletDirective, typeof i35.FsFormGroupDirective], [typeof i6.FsFormDirective, typeof i7.FsControlDirective, typeof i8.FsFormRequiredDirective, typeof i9.FsFormMinDirective, typeof i10.FsFormMaxDirective, typeof i11.FsFormMinLengthDirective, typeof i12.FsFormMaxLengthDirective, typeof i13.FsFormEmailDirective, typeof i14.FsFormEmailsDirective, typeof i15.FsFormPhoneDirective, typeof i16.FsFormCompareDirective, typeof i17.FsFormIntegerDirective, typeof i18.FsFormNumericDirective, typeof i19.FsFormPatternDirective, typeof i20.FsFormFunctionDirective, typeof i21.FsFormDateRangeDirective, typeof i22.FsFormGreaterDirective, typeof i23.FsFormGreaterEqualDirective, typeof i24.FsFormLesserDirective, typeof i25.FsFormLesserEqualDirective, typeof i26.FsFormUrlDirective, typeof i27.FsFormDialogCloseDirective, typeof i28.FsFormValidateDirective, typeof i29.FsFormDialogActionsComponent, typeof i30.FsFormNoFsValidatorsDirective, typeof i31.FsButtonDirective, typeof i32.FsSubmitButtonDirective, typeof i33.FsFormTemplateDirective, typeof i33.FsFormTemplateDirective, typeof i34.FsFormTemplateOutletDirective, typeof i35.FsFormGroupDirective]>;
42
+ static ɵmod: i0.ɵɵNgModuleDeclaration<FsFormModule, never, [typeof i1.CommonModule, typeof i2.FormsModule, typeof i3.MatButtonModule, typeof i4.MatDialogModule, typeof i4.MatDialogModule, typeof i5.FsDialogModule, typeof i6.FsFormDirective, typeof i7.FsControlDirective, typeof i8.FsFormRequiredDirective, typeof i9.FsFormMinDirective, typeof i10.FsFormMaxDirective, typeof i11.FsFormMinLengthDirective, typeof i12.FsFormMaxLengthDirective, typeof i13.FsFormEmailDirective, typeof i14.FsFormEmailsDirective, typeof i15.FsFormPhoneDirective, typeof i16.FsFormCompareDirective, typeof i17.FsFormIntegerDirective, typeof i18.FsFormNumericDirective, typeof i19.FsFormPatternDirective, typeof i20.FsFormFunctionDirective, typeof i21.FsFormDateRangeDirective, typeof i22.FsFormGreaterDirective, typeof i23.FsFormGreaterEqualDirective, typeof i24.FsFormLesserDirective, typeof i25.FsFormLesserEqualDirective, typeof i26.FsFormUrlDirective, typeof i27.FsFormDialogCloseDirective, typeof i28.FsFormValidateDirective, typeof i29.FsFormDialogActionsComponent, typeof i30.FsFormNoFsValidatorsDirective, typeof i31.FsButtonDirective, typeof i32.FsSubmitButtonDirective, typeof i33.FsFormTemplateDirective, typeof i33.FsFormTemplateDirective, typeof i34.FsFormTemplateOutletDirective, typeof i35.FsFormContainerDirective, typeof i36.FsFormConfirmTabsDirective], [typeof i6.FsFormDirective, typeof i7.FsControlDirective, typeof i8.FsFormRequiredDirective, typeof i9.FsFormMinDirective, typeof i10.FsFormMaxDirective, typeof i11.FsFormMinLengthDirective, typeof i12.FsFormMaxLengthDirective, typeof i13.FsFormEmailDirective, typeof i14.FsFormEmailsDirective, typeof i15.FsFormPhoneDirective, typeof i16.FsFormCompareDirective, typeof i17.FsFormIntegerDirective, typeof i18.FsFormNumericDirective, typeof i19.FsFormPatternDirective, typeof i20.FsFormFunctionDirective, typeof i21.FsFormDateRangeDirective, typeof i22.FsFormGreaterDirective, typeof i23.FsFormGreaterEqualDirective, typeof i24.FsFormLesserDirective, typeof i25.FsFormLesserEqualDirective, typeof i26.FsFormUrlDirective, typeof i27.FsFormDialogCloseDirective, typeof i28.FsFormValidateDirective, typeof i29.FsFormDialogActionsComponent, typeof i30.FsFormNoFsValidatorsDirective, typeof i31.FsButtonDirective, typeof i32.FsSubmitButtonDirective, typeof i33.FsFormTemplateDirective, typeof i33.FsFormTemplateDirective, typeof i34.FsFormTemplateOutletDirective, typeof i35.FsFormContainerDirective, typeof i36.FsFormConfirmTabsDirective]>;
42
43
  static ɵinj: i0.ɵɵInjectorDeclaration<FsFormModule>;
43
44
  }
@@ -1 +1,2 @@
1
1
  export * from './confirm-result-continue';
2
+ export * from './within-same-dialog';
@@ -0,0 +1,18 @@
1
+ import { MatDialogRef } from '@angular/material/dialog';
2
+ import type { FsFormBaseDirective } from '../directives/form-base';
3
+ /**
4
+ * Whether an owner found through DI belongs to the same dialog as whoever found
5
+ * it - and may therefore be joined, or driven by a button.
6
+ *
7
+ * A form set must never span a dialog boundary. Owners are resolved by walking
8
+ * the element injector, and CDK parents a dialog opened with `viewContainerRef`
9
+ * to the opener's injector, so a dialog opened from inside another dialog's form
10
+ * would otherwise register into that form's set: the outer footer would flip to
11
+ * Save the moment the inner dialog opened and would then submit the inner form,
12
+ * while the inner dialog - no longer the owner of its own set - would stop
13
+ * confirming its unsaved changes on backdrop click.
14
+ *
15
+ * Identity of the `MatDialogRef` is the test: everything inside one dialog
16
+ * injects the same instance, and everything outside any dialog gets null.
17
+ */
18
+ export declare function withinSameDialog(owner: FsFormBaseDirective, dialogRef: MatDialogRef<any>): boolean;
@@ -0,0 +1,17 @@
1
+ import { Observable } from 'rxjs';
2
+ import type { FsFormDirective } from '../directives/form/form.directive';
3
+ import { ConfirmConfig } from './confirm-config';
4
+ import { SubmitEvent } from './submit-event';
5
+ import { SubmittedEvent } from './submitted-event';
6
+ /**
7
+ * What the unsaved-changes confirm and the route guard need from whatever owns a
8
+ * set of forms. Both an `fsForm` at the root of its linked set and an
9
+ * `fsFormContainer` holding several satisfy it, so neither caller has to know
10
+ * which of the two it was handed.
11
+ */
12
+ export interface FsFormOwner {
13
+ confirm: ConfirmConfig | boolean;
14
+ linkedForms: FsFormDirective[];
15
+ reset(): void;
16
+ submit$(event?: SubmitEvent): Observable<SubmittedEvent>;
17
+ }
@@ -2,3 +2,4 @@ export * from './submit-event';
2
2
  export * from './submitted-event';
3
3
  export * from './confirm-config';
4
4
  export * from './confirm-tab-group';
5
+ export * from './form-owner';
@@ -1,16 +1,16 @@
1
1
  import { Type } from '@angular/core';
2
2
  import { Observable } from 'rxjs';
3
- import type { FsFormDirective } from '../directives/form/form.directive';
4
3
  import { ConfirmResult } from '../enums';
4
+ import { FsFormOwner } from '../interfaces';
5
5
  import * as i0 from "@angular/core";
6
6
  export declare class FsForm {
7
7
  private _formDirectiveStore;
8
8
  private _dialog;
9
- registerFormDirective(routeComponent: Type<any>, directive: FsFormDirective): void;
10
- getFormDirectives(routeComponent: any): FsFormDirective[];
9
+ registerFormDirective(routeComponent: Type<any>, directive: FsFormOwner): void;
10
+ getFormDirectives(routeComponent: any): FsFormOwner[];
11
11
  removeFormDirective(routeComponent: any): void;
12
- hasChanges(form: FsFormDirective): boolean;
13
- confirmUnsaved(form: FsFormDirective): Observable<ConfirmResult>;
12
+ hasChanges(form: FsFormOwner): boolean;
13
+ confirmUnsaved(form: FsFormOwner): Observable<ConfirmResult>;
14
14
  static ɵfac: i0.ɵɵFactoryDeclaration<FsForm, never>;
15
15
  static ɵprov: i0.ɵɵInjectableDeclaration<FsForm>;
16
16
  }