@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.
- package/app/components/form-dialog-actions/form-dialog-actions.component.d.ts +17 -2
- package/app/directives/button.directive.d.ts +9 -1
- package/app/directives/confirm-tabs/confirm-tabs.directive.d.ts +32 -0
- package/app/directives/confirm-tabs/index.d.ts +1 -0
- package/app/directives/form/form.directive.d.ts +89 -29
- package/app/directives/form-base/form-base.directive.d.ts +86 -6
- package/app/directives/form-container/form-container.directive.d.ts +80 -0
- package/app/directives/form-container/index.d.ts +1 -0
- package/app/directives/index.d.ts +2 -1
- package/app/fs-form.module.d.ts +3 -2
- package/app/helpers/index.d.ts +1 -0
- package/app/helpers/within-same-dialog.d.ts +18 -0
- package/app/interfaces/form-owner.d.ts +17 -0
- package/app/interfaces/index.d.ts +1 -0
- package/app/services/fsform.service.d.ts +5 -5
- package/esm2022/app/components/form-dialog-actions/form-dialog-actions.component.mjs +50 -17
- package/esm2022/app/directives/button.directive.mjs +26 -9
- package/esm2022/app/directives/confirm-tabs/confirm-tabs.directive.mjs +50 -0
- package/esm2022/app/directives/confirm-tabs/index.mjs +2 -0
- package/esm2022/app/directives/form/form.directive.mjs +247 -241
- package/esm2022/app/directives/form-base/form-base.directive.mjs +255 -8
- package/esm2022/app/directives/form-container/form-container.directive.mjs +164 -0
- package/esm2022/app/directives/form-container/index.mjs +2 -0
- package/esm2022/app/directives/index.mjs +3 -2
- package/esm2022/app/fs-form.module.mjs +11 -6
- package/esm2022/app/guards/form-deactivate.guard.mjs +1 -1
- package/esm2022/app/helpers/index.mjs +2 -1
- package/esm2022/app/helpers/within-same-dialog.mjs +19 -0
- package/esm2022/app/interfaces/form-owner.mjs +2 -0
- package/esm2022/app/interfaces/index.mjs +2 -1
- package/esm2022/app/services/fsform.service.mjs +10 -7
- package/esm2022/public_api.mjs +3 -2
- package/fesm2022/firestitch-form.mjs +811 -320
- package/fesm2022/firestitch-form.mjs.map +1 -1
- package/package.json +1 -1
- package/public_api.d.ts +3 -1
- package/app/directives/form-group/form-group.directive.d.ts +0 -15
- package/app/directives/form-group/index.d.ts +0 -1
- package/esm2022/app/directives/form-group/form-group.directive.mjs +0 -35
- 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
|
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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; }; "
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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; }; "
|
|
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';
|
package/app/fs-form.module.d.ts
CHANGED
|
@@ -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-
|
|
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.
|
|
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
|
}
|
package/app/helpers/index.d.ts
CHANGED
|
@@ -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
|
+
}
|
|
@@ -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:
|
|
10
|
-
getFormDirectives(routeComponent: any):
|
|
9
|
+
registerFormDirective(routeComponent: Type<any>, directive: FsFormOwner): void;
|
|
10
|
+
getFormDirectives(routeComponent: any): FsFormOwner[];
|
|
11
11
|
removeFormDirective(routeComponent: any): void;
|
|
12
|
-
hasChanges(form:
|
|
13
|
-
confirmUnsaved(form:
|
|
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
|
}
|