@@ -10,6 +10,7 @@ import 'package:angular_components/content/deferred_content_aware.dart';
1010import 'package:angular_components/src/laminate/components/modal/modal_controller_directive.dart' ;
1111import 'package:angular_components/laminate/overlay/overlay.dart' ;
1212import 'package:angular_components/model/action/async_action.dart' ;
13+ import 'package:angular_components/utils/browser/dom_service/dom_service.dart' ;
1314import 'package:angular_components/utils/disposer/disposer.dart' ;
1415
1516/// May be added to DI to enforce that a single [Modal] is visible at a time.
@@ -47,6 +48,13 @@ class GlobalModalStack {
4748 }
4849 _stack.add (modal);
4950 }
51+
52+ /// Tell all the modals to close in reverse order.
53+ Future <void > closeAll () async {
54+ for (var modal in _stack.reversed.toList ()) {
55+ await modal.close ();
56+ }
57+ }
5058}
5159
5260/// An ADT that can be injected by content that lives within a modal.
@@ -155,7 +163,7 @@ class ModalComponent
155163 final Element _element;
156164 final Modal _parentModal;
157165 final GlobalModalStack _stack;
158- final NgZone _ngZone ;
166+ final DomService _domService ;
159167
160168 @override
161169 Stream <AsyncAction > get onOpen => _onOpen.stream;
@@ -187,7 +195,7 @@ class ModalComponent
187195 Future <bool > _pendingOpen;
188196 Future <bool > _pendingClose;
189197
190- ModalComponent (OverlayService overlayService, this ._element, this ._ngZone ,
198+ ModalComponent (OverlayService overlayService, this ._element, this ._domService ,
191199 @Optional () @SkipSelf () this ._parentModal, @Optional () this ._stack) {
192200 _createdOverlayRef (
193201 overlayService.createOverlayRefSync (OverlayState .Dialog ));
@@ -251,8 +259,8 @@ class ModalComponent
251259 //
252260 // If it has a parent, we should temporarily hide it.
253261 void _showModalOverlay ({bool temporary = false }) {
254- _saveFocus ();
255262 if (! temporary) {
263+ _saveFocus ();
256264 if (_stack != null ) {
257265 _stack.onModalOpened (this );
258266 } else if (_parentModal != null ) {
@@ -266,8 +274,8 @@ class ModalComponent
266274 //
267275 // If it has a parent, we should re-show it.
268276 void _hideModalOverlay ({bool temporary = false }) {
269- _restoreFocus ();
270277 if (! temporary) {
278+ _restoreFocus ();
271279 if (_stack != null ) {
272280 _stack.onModalClosed (this );
273281 } else if (_parentModal != null ) {
@@ -284,18 +292,20 @@ class ModalComponent
284292 void _restoreFocus () {
285293 if (_lastFocusedElement == null ) return ;
286294 if (_stack != null && _stack.length > 1 || _parentModal != null ) return ;
287- // Only restore focus if the current active element is inside this overlay.
288- // Note in a browser activeElement is never null and the null check below is
289- // only for testing.
290- if (document.activeElement == null ||
291- ! _resolvedOverlayRef.overlayElement.contains (document.activeElement)) {
292- return ;
293- }
294295 final elementToFocus = _lastFocusedElement;
295- scheduleMicrotask (() {
296- // Note that if the [elementToFocus] is no longer in the document,
297- // the body element will be focused instead.
298- elementToFocus.focus ();
296+ _domService.scheduleWrite (() {
297+ // Only restore focus if the current active element is inside this overlay
298+ // or the focus was lost.
299+ // Note in a browser activeElement is never null and the null check below
300+ // is only for testing.
301+ if (document.activeElement != null &&
302+ (_resolvedOverlayRef.overlayElement
303+ .contains (document.activeElement) ||
304+ document.activeElement == document.body)) {
305+ // Note that if the [elementToFocus] is no longer in the document,
306+ // the body element will be focused instead.
307+ elementToFocus.focus ();
308+ }
299309 });
300310 }
301311
0 commit comments