22// for details. All rights reserved. Use of this source code is governed by a
33// BSD-style license that can be found in the LICENSE file.
44
5+ import 'dart:async' ;
56import 'dart:html' as dom;
67
78import 'package:angular/angular.dart' ;
89import 'package:js/js_util.dart' as js_util;
10+ import 'package:angular_components/laminate/components/modal/modal.dart' ;
11+ import 'package:angular_components/laminate/popup/popup.dart' ;
912import 'package:angular_components/utils/browser/dom_service/dom_service.dart' ;
1013
1114/// Directive for elements able to be activated through mouse or keyboard, like
@@ -14,40 +17,92 @@ import 'package:angular_components/utils/browser/dom_service/dom_service.dart';
1417/// Will scroll the item into view as long as it was made active when mouse is
1518/// not hovering over it.
1619@Directive (selector: '[itemActive]' )
17- class ActiveItemDirective {
20+ class ActiveItemDirective implements AfterViewInit , OnDestroy {
1821 /// Dom element of the item, that will be scrolled to view on activate.
19- dom.HtmlElement _element;
22+ final dom.HtmlElement _element;
2023
2124 /// An instance of DomService, used to coordinate scrolling.
22- DomService _domService;
25+ final DomService _domService;
26+
27+ /// Parent modal if any.
28+ final Modal _modal;
29+
30+ /// Parent popup if any.
31+ final PopupRef _popupRef;
2332
2433 bool _active = false ;
2534
35+ bool _initialized = false ;
36+
37+ StreamSubscription _visibilitySubscription;
38+
2639 /// Whether the element is active.
2740 @HostBinding ('class.active' )
2841 bool get active => _active;
2942
30- ActiveItemDirective (this ._element, this ._domService);
43+ ActiveItemDirective (this ._element, this ._domService, @Optional () this ._modal,
44+ @Optional () this ._popupRef);
45+
46+ @override
47+ void ngOnDestroy () {
48+ _visibilitySubscription? .cancel ();
49+ _visibilitySubscription = null ;
50+ }
51+
52+ @override
53+ void ngAfterViewInit () {
54+ _initialized = true ;
55+ _scrollIntoViewIfNecessary ();
56+ }
3157
3258 /// Marks item as active from keyboard selection.
3359 @Input ()
3460 set itemActive (bool value) {
3561 if (value == _active) return ;
3662 _active = value;
37- if (_active && ! _hasHover) {
38- _domService.scheduleWrite (() {
39- try {
40- var options = js_util.newObject ();
41- js_util.setProperty (options, 'block' , 'nearest' );
42- js_util.setProperty (options, 'inline' , 'nearest' );
43- js_util.callMethod (_element, 'scrollIntoView' , [options]);
44- } catch (_) {
45- _element.scrollIntoView ();
46- }
47- });
63+ _scrollIntoViewIfNecessary ();
64+ }
65+
66+ bool get _shouldScrollIntoView => _initialized && _active && ! _hasHover;
67+
68+ void _scrollIntoViewIfNecessary () {
69+ _visibilitySubscription? .cancel ();
70+
71+ if (_shouldScrollIntoView) {
72+ var isVisible = _popupRef != null
73+ ? _popupRef.isVisible
74+ : _modal != null ? _modal.visible : true ;
75+ if (isVisible) {
76+ _scrollIntoView ();
77+ } else {
78+ var onVisibleChanged = _popupRef != null
79+ ? _popupRef.onVisibleChanged
80+ : _modal.onVisibleChanged;
81+ _visibilitySubscription = onVisibleChanged.listen ((isVisible) {
82+ if (isVisible) {
83+ _visibilitySubscription? .cancel ();
84+ if (_shouldScrollIntoView) {
85+ _scrollIntoView ();
86+ }
87+ }
88+ });
89+ }
4890 }
4991 }
5092
93+ void _scrollIntoView () {
94+ _domService.scheduleWrite (() {
95+ try {
96+ var options = js_util.newObject ();
97+ js_util.setProperty (options, 'block' , 'nearest' );
98+ js_util.setProperty (options, 'inline' , 'nearest' );
99+ js_util.callMethod (_element, 'scrollIntoView' , [options]);
100+ } catch (_) {
101+ _element.scrollIntoView ();
102+ }
103+ });
104+ }
105+
51106 /// Whether the element has hover.
52107 bool _hasHover = false ;
53108
0 commit comments