@almoamendev/ngx-md3 0.0.16 → 0.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almoamendev/ngx-md3",
3
- "version": "0.0.16",
3
+ "version": "0.2.0",
4
4
  "description": "MD3-style Angular components & style library",
5
5
  "author": "Murtadha (Hussain) Almoamen",
6
6
  "license": "MIT",
@@ -1,9 +1,10 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { ViewContainerRef, Injector, ElementRef, AfterViewInit, OnDestroy, OnInit, Renderer2, Signal, InputSignal, AfterContentInit, Type, ComponentRef, InjectionToken, TemplateRef } from '@angular/core';
2
+ import { ViewContainerRef, Injector, ElementRef, AfterViewInit, OnDestroy, OnInit, Renderer2, Signal, InputSignal, InjectionToken, Type, AfterContentInit, ComponentRef, TemplateRef } from '@angular/core';
3
3
  import { FlexibleConnectedPositionStrategyOrigin, ConnectedPosition, OverlayRef } from '@angular/cdk/overlay';
4
4
  import * as i1 from '@angular/cdk/scrolling';
5
5
  import { AbstractControl } from '@angular/forms';
6
6
  import * as i1$1 from '@angular/router';
7
+ import { CdkDialogContainer, DialogRef as DialogRef$1 } from '@angular/cdk/dialog';
7
8
  import { Observable } from 'rxjs';
8
9
  import { CdkPortalOutlet } from '@angular/cdk/portal';
9
10
 
@@ -43,6 +44,12 @@ type ListLeadingType = 'icon' | 'avatar' | 'media' | 'selection-input';
43
44
 
44
45
  type ListLeadingSize = 'image' | 'small-video' | 'large-video';
45
46
 
47
+ /**
48
+ * What happens to a dialog that is already open when a new dialog opens.
49
+ * - close: the open dialogs are closed before the new one opens.
50
+ * - hide: the dialog on top is hidden and shown again once the new one closes.
51
+ */
52
+ type PreviousDialog = 'close' | 'hide';
46
53
  interface DialogConfig<D = unknown> {
47
54
  /**
48
55
  * Optional data passed to the component opened inside the dialog.
@@ -55,6 +62,11 @@ interface DialogConfig<D = unknown> {
55
62
  */
56
63
  bindDataToInputs?: boolean;
57
64
  disableCloseEvents?: boolean;
65
+ /**
66
+ * What happens to a dialog that is already open when this dialog opens.
67
+ * A hidden dialog keeps its state and is shown again once this dialog closes.
68
+ */
69
+ previousDialog?: PreviousDialog;
58
70
  role?: DialogRole;
59
71
  ariaLabel?: string;
60
72
  ariaLabelledBy?: string;
@@ -68,6 +80,27 @@ interface DialogConfig<D = unknown> {
68
80
  viewContainerRef?: ViewContainerRef;
69
81
  injector?: Injector;
70
82
  }
83
+ /**
84
+ * What DialogRef needs from the shell hosting a dialog. Implemented by both the
85
+ * regular dialog and the full screen dialog, so the reference can drive either.
86
+ */
87
+ interface DialogContainer {
88
+ /** Element that plays the enter and exit transition. */
89
+ readonly surfaceElement: HTMLElement | null;
90
+ /** Starts the enter animation. Delayed when the dialog replaces another one. */
91
+ startEnterAnimation(): void;
92
+ /** Shows or hides the surface, which is what drives both transitions. */
93
+ setActive(value: boolean): void;
94
+ /** Moves focus back inside the dialog when it is not there already. */
95
+ recaptureFocus(): void;
96
+ }
97
+
98
+ /**
99
+ * Configuration of a full screen dialog. It is the dialog configuration without
100
+ * `previousDialog`: a full screen dialog always replaces the one that is open,
101
+ * because only one of them can be open at a time.
102
+ */
103
+ type FullScreenDialogConfig<D = unknown> = Omit<DialogConfig<D>, 'previousDialog'>;
71
104
 
72
105
  type MenuPositionX = 'start' | 'end' | 'before' | 'after' | 'center';
73
106
  type MenuPositionY = 'above' | 'below' | 'center';
@@ -934,17 +967,24 @@ declare class SupportingText {
934
967
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SupportingText, "[md3-supporting-text]", never, {}, {}, never, never, true, never>;
935
968
  }
936
969
 
937
- declare class Dialog implements AfterContentInit {
938
- private readonly portalOutlet;
970
+ /**
971
+ * Material 3 dialog shell. It extends the CDK dialog container, so focus
972
+ * trapping, focus restoration and the ARIA attributes are handled by the CDK
973
+ * while this component only owns the MD3 surface and its animation.
974
+ */
975
+ declare class Dialog extends CdkDialogContainer implements DialogContainer {
976
+ private readonly surface;
939
977
  isActive: _angular_core.WritableSignal<boolean>;
940
978
  protected readonly config: DialogConfig<unknown>;
941
- protected get role(): string;
942
- ngAfterContentInit(): void;
979
+ get surfaceElement(): HTMLElement | null;
943
980
  /**
944
- * The service creates this wrapper first, then calls this method to mount
945
- * the user supplied component into the dialog container.
981
+ * Started by DialogService, either right after the dialog is created or
982
+ * once the dialog it replaces has had its head start. The animation runs on
983
+ * the next frame so the surface has a real from/to state.
946
984
  */
947
- attachContent<T>(component: Type<T>, injector: Injector): ComponentRef<T>;
985
+ startEnterAnimation(): void;
986
+ setActive(value: boolean): void;
987
+ recaptureFocus(): void;
948
988
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Dialog, never>;
949
989
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Dialog, "md3-dialog", never, {}, {}, never, never, true, never>;
950
990
  }
@@ -953,36 +993,120 @@ declare const DIALOG_DATA: InjectionToken<unknown>;
953
993
  declare const DIALOG_CONFIG: InjectionToken<DialogConfig<unknown>>;
954
994
  declare const DIALOG_COMPONENT: InjectionToken<Type<unknown>>;
955
995
  declare class DialogRef<T = unknown, R = unknown> {
956
- private readonly overlayRef;
957
- private readonly previouslyFocusedElement;
996
+ private readonly cdkRef;
997
+ /** Whether this reference belongs to a full screen dialog. */
998
+ readonly isFullScreen: boolean;
958
999
  private readonly closed;
959
- private isClosed;
960
- dialogInstance?: Dialog;
1000
+ private readonly closing;
1001
+ private closePromise;
1002
+ private closeStarted;
1003
+ private closeSettled;
1004
+ private hidden;
1005
+ /** Shell hosting the dialog: the regular dialog or the full screen one. */
1006
+ dialogInstance?: DialogContainer;
961
1007
  /**
962
1008
  * Filled by DialogService after the user component is attached. Keeping the
963
1009
  * instance here lets callers imperatively update inputs when that is useful.
964
1010
  */
965
1011
  componentInstance?: T;
966
- constructor(overlayRef: OverlayRef, previouslyFocusedElement: HTMLElement | null);
967
- close(result?: R): void;
968
- private startCloseAnimation;
1012
+ /** Overlay hosting the dialog, useful to reach the panel and the scrim. */
1013
+ get overlayRef(): OverlayRef;
1014
+ /** Whether the dialog started closing. Such a dialog cannot be hidden or shown anymore. */
1015
+ get isClosing(): boolean;
1016
+ /** Whether the dialog is currently hidden behind another dialog. */
1017
+ get isHidden(): boolean;
1018
+ constructor(cdkRef: DialogRef$1<R, T>, disableCloseEvents: boolean,
1019
+ /** Whether this reference belongs to a full screen dialog. */
1020
+ isFullScreen?: boolean);
1021
+ /** Closes the dialog. The promise resolves once the exit animation is done. */
1022
+ close(result?: R): Promise<void>;
1023
+ /**
1024
+ * Hides the dialog with the closing animation while keeping it alive, so
1025
+ * its content, form state and subscriptions survive until show() is called.
1026
+ * The promise resolves once the dialog is out of sight.
1027
+ */
1028
+ hide(): Promise<void>;
1029
+ /**
1030
+ * Brings a hidden dialog back with the opening animation. Focus moves back
1031
+ * into the dialog even when it was never hidden, which is what makes it
1032
+ * usable again once the dialog above it closes.
1033
+ */
1034
+ show(): void;
1035
+ /** Emits when the dialog starts closing, before the exit animation runs. */
1036
+ beforeClosed(): Observable<void>;
969
1037
  afterClosed(): Observable<R | undefined>;
1038
+ private connectCloseEvents;
1039
+ private toggleHiddenState;
1040
+ private startClosing;
1041
+ private startCloseAnimation;
1042
+ /** Resolves when the surface finished animating, with a timeout as a safety net. */
1043
+ private waitForSurfaceAnimation;
1044
+ /** Emits the result once the CDK reference is closed. Focus is restored by the container. */
1045
+ private settle;
970
1046
  }
971
1047
 
972
1048
  declare class DialogService {
1049
+ private readonly cdkDialog;
973
1050
  private readonly overlay;
974
1051
  private readonly injector;
975
1052
  private readonly document;
1053
+ /** Open dialogs, from the first one opened to the one currently on top. */
1054
+ private readonly refs;
1055
+ /**
1056
+ * Page scrolling is blocked here instead of per overlay, so stacked dialogs
1057
+ * cannot unblock the page while another dialog is still open.
1058
+ */
1059
+ private readonly scrollBlock;
1060
+ /** Element that was focused before the first dialog of the stack opened. */
1061
+ private rootTrigger;
1062
+ get openDialogs(): readonly DialogRef<any, any>[];
1063
+ /** The full screen dialog that is open, if there is one. */
1064
+ get fullScreenDialog(): DialogRef<any, any> | undefined;
976
1065
  open<T, D = unknown, R = unknown>(component: Type<T>, config?: DialogConfig<D>): DialogRef<T, R>;
1066
+ /**
1067
+ * Opens a dialog that covers the whole screen. Only one can be open at a
1068
+ * time, so it replaces whatever is open, and while it is up the side sheet
1069
+ * outlets belong to it: side sheets open inside the dialog, not behind it.
1070
+ */
1071
+ openFullScreen<T, D = unknown, R = unknown>(component: Type<T>, config?: FullScreenDialogConfig<D>): DialogRef<T, R>;
1072
+ /** Wires a freshly created dialog into the stack and starts its animation. */
1073
+ private registerDialog;
1074
+ /** Closes every open dialog, including the hidden and the full screen ones. */
1075
+ closeAll(): Promise<void>;
1076
+ /** Closes the regular dialogs and leaves a full screen dialog alone. */
1077
+ private closeRegularDialogs;
977
1078
  private mergeConfig;
978
- private createOverlay;
1079
+ /**
1080
+ * Hides or closes the dialog that is on screen. Its scrim stays up until
1081
+ * the dialog taking over is ready for it, so the page behind never
1082
+ * brightens between the two dialogs.
1083
+ */
1084
+ private dismissPrevious;
979
1085
  private startOpenAnimation;
980
- private createInjector;
1086
+ /** Opening animation for a dialog that took the place of another one. */
1087
+ private startReplacingAnimation;
1088
+ /** Opening animation for a dialog coming back from behind a closing one. */
1089
+ private startRestoringAnimation;
1090
+ /**
1091
+ * Moves both scrims to their new value in a single frame, without a
1092
+ * transition on either side, so the dim behind the dialogs never changes.
1093
+ */
1094
+ private beginScrimHandover;
1095
+ /** Gives the scrims their transitions back once the handover is painted. */
1096
+ private endScrimHandover;
981
1097
  private bindDataToInputs;
982
- private connectCloseEvents;
983
- private focusDialog;
984
- private getFocusedElement;
985
1098
  private canBindDataToInputs;
1099
+ /** Last dialog of the stack that is not closing yet. */
1100
+ private topRef;
1101
+ /**
1102
+ * Same, without the full screen dialog: it hosts the dialogs opened on top
1103
+ * of it, so it is never the one that gets hidden or closed to make room.
1104
+ */
1105
+ private topRegularRef;
1106
+ private restorePreviousDialog;
1107
+ private removeRef;
1108
+ private updateScrollBlock;
1109
+ private getFocusedElement;
986
1110
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DialogService, never>;
987
1111
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<DialogService>;
988
1112
  }
@@ -1002,6 +1126,42 @@ declare class DialogActions {
1002
1126
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DialogActions, "md3-dialog-actions", never, {}, {}, never, ["*"], true, never>;
1003
1127
  }
1004
1128
 
1129
+ /**
1130
+ * Material 3 full screen dialog shell. It covers the viewport and lays its
1131
+ * content out like the scaffold does, with a main pane and a side sheet outlet
1132
+ * on each side, so side sheets opened while it is up land inside the dialog
1133
+ * instead of behind it.
1134
+ */
1135
+ declare class FullScreenDialog extends CdkDialogContainer implements DialogContainer, AfterViewInit {
1136
+ private readonly surface;
1137
+ private readonly startOutlet;
1138
+ private readonly endOutlet;
1139
+ private readonly sheets;
1140
+ isActive: _angular_core.WritableSignal<boolean>;
1141
+ protected readonly config: DialogConfig<unknown>;
1142
+ get surfaceElement(): HTMLElement | null;
1143
+ ngAfterViewInit(): void;
1144
+ ngOnDestroy(): void;
1145
+ startEnterAnimation(): void;
1146
+ setActive(value: boolean): void;
1147
+ recaptureFocus(): void;
1148
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<FullScreenDialog, never>;
1149
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<FullScreenDialog, "md3-fullscreen-dialog", never, {}, {}, never, never, true, never>;
1150
+ }
1151
+
1152
+ /**
1153
+ * Header of a full screen dialog: a leading icon button to leave the dialog,
1154
+ * the headline, and a trailing action. It sticks to the top of the dialog while
1155
+ * the content scrolls underneath.
1156
+ */
1157
+ declare class FullScreenDialogHeader implements ButtonContext {
1158
+ title: _angular_core.InputSignal<string>;
1159
+ buttonContextSize: Signal<ButtonSize>;
1160
+ buttonContextWidth: Signal<IconButtonWidth>;
1161
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<FullScreenDialogHeader, never>;
1162
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<FullScreenDialogHeader, "md3-fullscreen-dialog-header", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; }, {}, never, ["[md3-icon-button][md3-header-leading]", "[md3-header-trailing]"], true, never>;
1163
+ }
1164
+
1005
1165
  declare class Menu implements AfterContentInit {
1006
1166
  private el;
1007
1167
  private readonly portalOutlet;
@@ -1163,11 +1323,19 @@ declare class SideSheetRef<T = unknown, R = unknown> {
1163
1323
 
1164
1324
  declare class SheetsService {
1165
1325
  private readonly injector;
1326
+ /**
1327
+ * Outlets registered per side, from the page scaffold up to whatever took
1328
+ * them over last. Sheets always open in the outlet on top, so a full screen
1329
+ * dialog can host them while it is up and hand them back when it closes.
1330
+ */
1166
1331
  private readonly states;
1167
1332
  registerSideSheetOutlet(side: SideSheetSide, outlet: CdkPortalOutlet): void;
1168
1333
  unregisterSideSheetOutlet(side: SideSheetSide, outlet: CdkPortalOutlet): void;
1169
1334
  openSideSheet<T = unknown, D = unknown, R = unknown>(component: Type<T>, config?: SideSheetConfig<D>): SideSheetRef<T, R>;
1170
1335
  closeSideSheet<R = unknown>(side?: SideSheetSide, result?: R): void;
1336
+ /** Outlet that sheets currently open in: the last one that registered. */
1337
+ private activeState;
1338
+ private getOutletStack;
1171
1339
  private mergeConfig;
1172
1340
  private createInjector;
1173
1341
  private bindDataToInputs;
@@ -1294,5 +1462,5 @@ declare class SnackbarService {
1294
1462
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<SnackbarService>;
1295
1463
  }
1296
1464
 
1297
- export { AppBar, AppBarLogo, Avatar, Badge, Button, ButtonGroup, Card, Checkbox, ChipAvatar, Chips, CircularProgressIndicator, DIALOG_COMPONENT, DIALOG_CONFIG, DIALOG_DATA, Dialog, DialogActions, DialogBody, DialogHeader, DialogRef, DialogService, Divider, FloatingActionButton, Grid, GridItem, IconButton, IconElement, InputElement, LayoutService, LinearProgressIndicator, List, ListItem, ListItemPrimaryAction, ListLeading, ListSlot, LoadingIndicator, MENU_COMPONENT, MENU_CONFIG, MENU_DATA, MaterialIcon, Menu, MenuGroup, MenuItem, MenuRef, MenuService, NavigationBar, NavigationGroup, NavigationItem, NavigationRail, RadioButton, SIDE_SHEET_COMPONENT, SIDE_SHEET_CONFIG, SIDE_SHEET_DATA, SNACKBAR_ACTION_LABEL, SNACKBAR_CONFIG, SNACKBAR_MESSAGE, Scaffold, ScaffoldBar, ScaffoldPane, ScaffoldRail, SheetsService, SideSheetActions, SideSheetBody, SideSheetHeader, SideSheetRef, Slider, Snackbar, SnackbarRef, SnackbarService, SplitButton, StateComponent, SupportingText, Switch, TextField, TypeBody, TypeDisplay, TypeHeadline, TypeLabel, TypeTitle };
1298
- export type { AppBarScrollingStyle, AppBarType, ButtonGroupSelection, ButtonGroupType, ButtonSize, ButtonType, CardType, ChipStyle, ChipType, DialogConfig, DialogRole, FabSize, FabType, IconButtonType, IconButtonWidth, ListLeadingSize, ListLeadingType, Md3NavigationMode, MenuConfig, MenuPositionOrigin, MenuPositionX, MenuPositionY, MenuScrollStrategy, SideSheetConfig, SideSheetContainer, SideSheetSide, SideSheetType, SliderSize, SnackbarConfig, SnackbarDismiss, SnackbarDismissReason, SnackbarPoliteness, SplitButtonType, TextColor, TextSize };
1465
+ export { AppBar, AppBarLogo, Avatar, Badge, Button, ButtonGroup, Card, Checkbox, ChipAvatar, Chips, CircularProgressIndicator, DIALOG_COMPONENT, DIALOG_CONFIG, DIALOG_DATA, Dialog, DialogActions, DialogBody, DialogHeader, DialogRef, DialogService, Divider, FloatingActionButton, FullScreenDialog, FullScreenDialogHeader, Grid, GridItem, IconButton, IconElement, InputElement, LayoutService, LinearProgressIndicator, List, ListItem, ListItemPrimaryAction, ListLeading, ListSlot, LoadingIndicator, MENU_COMPONENT, MENU_CONFIG, MENU_DATA, MaterialIcon, Menu, MenuGroup, MenuItem, MenuRef, MenuService, NavigationBar, NavigationGroup, NavigationItem, NavigationRail, RadioButton, SIDE_SHEET_COMPONENT, SIDE_SHEET_CONFIG, SIDE_SHEET_DATA, SNACKBAR_ACTION_LABEL, SNACKBAR_CONFIG, SNACKBAR_MESSAGE, Scaffold, ScaffoldBar, ScaffoldPane, ScaffoldRail, SheetsService, SideSheetActions, SideSheetBody, SideSheetHeader, SideSheetRef, Slider, Snackbar, SnackbarRef, SnackbarService, SplitButton, StateComponent, SupportingText, Switch, TextField, TypeBody, TypeDisplay, TypeHeadline, TypeLabel, TypeTitle };
1466
+ export type { AppBarScrollingStyle, AppBarType, ButtonGroupSelection, ButtonGroupType, ButtonSize, ButtonType, CardType, ChipStyle, ChipType, DialogConfig, DialogContainer, DialogRole, FabSize, FabType, FullScreenDialogConfig, IconButtonType, IconButtonWidth, ListLeadingSize, ListLeadingType, Md3NavigationMode, MenuConfig, MenuPositionOrigin, MenuPositionX, MenuPositionY, MenuScrollStrategy, PreviousDialog, SideSheetConfig, SideSheetContainer, SideSheetSide, SideSheetType, SliderSize, SnackbarConfig, SnackbarDismiss, SnackbarDismissReason, SnackbarPoliteness, SplitButtonType, TextColor, TextSize };