<commit_before>// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:html';

import 'package:angular/angular.dart';
import 'package:angular_components/focus/focus_trap.dart';
import 'package:angular_components/laminate/components/modal/modal.dart';
import 'package:angular_components/model/a11y/keyboard_handler_mixin.dart';
import 'package:angular_components/utils/browser/dom_service/dom_service.dart';
import 'package:angular_components/utils/disposer/disposer.dart';
import 'package:angular_components/utils/id_generator/id_generator.dart';

/// A styled container following the Material Spec for Dialogs.
///
/// __Supported Content:__
///
/// - `[header]` -- Header content for the dialog.
/// - `[footer]` -- Footer content for the dialog.
///
/// __Attributes:__
///
///  - `headered` -- Adds a gray background to the dialog header.
///  - `info` -- Styles the dialog as an info dialog.
///
@Component(
  selector: 'material-dialog',
  templateUrl: 'material_dialog.html',
  styleUrls: ['material_dialog.scss.css'],
  directives: [FocusTrapComponent, NgIf],
  changeDetection: ChangeDetectionStrategy.OnPush,
)
class MaterialDialogComponent
    with KeyboardHandlerMixin
    implements AfterContentChecked, OnDestroy {
  @HostBinding('attr.role')
  static const hostRole = 'dialog';

  @HostBinding('attr.aria-modal')
  static const ariaModel = 'true';

  @HostBinding('attr.aria-labelledby')
  String get headerId => shouldShowHeader ? _uid : null;

  final HtmlElement _rootElement;
  final DomService _domService;
  final ChangeDetectorRef _changeDetector;
  final ModalComponent _modal;
  final _disposer = Disposer.oneShot();
  final _uid = SequentialIdGenerator.fromUUID().nextId();

  HtmlElement _mainElement;
  bool _shouldShowHeader = true;
  bool _shouldShowFooter = true;
  bool shouldShowTopScrollStroke = false;
  bool shouldShowBottomScrollStroke = false;

  final _isInFullscreenModeStreamController = StreamController<bool>();
  bool _isInFullscreenMode;
  bool _shouldListenForFullscreenChanges = false;

  MaterialDialogComponent(this._rootElement, this._domService,
      this._changeDetector, @Optional() this._modal) {
    escapeHandler = _defaultEscapeHandler;
  }

  @ViewChild('main', read: HtmlElement)
  set main(HtmlElement element) {
    _mainElement = element;
    _disposer.addStreamSubscription(element.onScroll.listen((_) {
      _setHeaderFooterScrollBorder();
    }));
    if (_modal == null) return;
    _disposer.addStreamSubscription(_modal.onOpen.listen((_) {
      _setHeaderFooterScrollBorder();
    }));
  }

  /// Function to handle escape key events from the dialog. By default it tries
  /// to close the parent modal, if any.
  @Input()
  KeyboardEventHandler escapeHandler;

  /// Error to show up in the error section of the dialog.
  @Input()
  String error;

  /// Whether to hide the dialog header.
  @Input()
  set hideHeader(bool shouldHideHeader) =>
      _shouldShowHeader = !shouldHideHeader;

  /// Whether to hide the dialog footer.
  @Input()
  set hideFooter(bool shouldHideFooter) =>
      _shouldShowFooter = !shouldHideFooter;

  bool get shouldShowHeader => _shouldShowHeader;

  bool get shouldShowFooter => _shouldShowFooter;

  /// Whether to show the top/bottom borders on the <main> part of the dialog to
  /// indicate whether there is more content that the user can reach by
  /// scrolling.
  @Input()
  bool shouldShowScrollStrokes = true;

  void _setHeaderFooterScrollBorder() {
    if (!shouldShowScrollStrokes) return;
    _disposer.addDisposable(_domService.scheduleRead(() {
      var shouldShowTopScrollStroke =
          _mainElement.scrollTop > 0 && error == null;
      var shouldShowBottomScrollStroke =
          _mainElement.clientHeight < _mainElement.scrollHeight &&
              _mainElement.scrollTop <
                  _mainElement.scrollHeight - _mainElement.clientHeight;
      if (shouldShowTopScrollStroke != this.shouldShowTopScrollStroke ||
          shouldShowBottomScrollStroke != this.shouldShowBottomScrollStroke) {
        this.shouldShowTopScrollStroke = shouldShowTopScrollStroke;
        this.shouldShowBottomScrollStroke = shouldShowBottomScrollStroke;
        _changeDetector
          ..markForCheck()
          // This detectChanges() here is for updating the classes when the page
          // initially loads (b/31916790).
          ..detectChanges();
      }
    }));
  }

  /// Determines whether to listen for when the dialog enters or exits
  /// fullscreen mode.
  @Input('listenForFullscreenChanges')
  set shouldListenForFullscreenChanges(bool shouldListenForFullscreenChanges) {
    // Don't do anything if [shouldListenForFullscreenChanges] is set to false.
    if (!shouldListenForFullscreenChanges) return;

    // If [_shouldListenForFullscreenChanges] is set to true, then that means
    // that we're already listening to fullscreen changes, so don't do anything.
    if (_shouldListenForFullscreenChanges) return;

    _shouldListenForFullscreenChanges = shouldListenForFullscreenChanges;
    _disposer.addStreamSubscription(window.onResize.listen((_) {
      _listenForFullscreenChanges();
    }));
  }

  /// Stream for when the dialog enters or exits fullscreen mode.
  @Output('fullscreenMode')
  Stream<bool> get isInFullscreenMode =>
      _isInFullscreenModeStreamController.stream;

  void _listenForFullscreenChanges() {
    if (!_shouldListenForFullscreenChanges) return;<commit_msg>cleanup: Try replacing ".detectChanges()" with ".runAfterChanges...".<commit_after>// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:html';

import 'package:angular/angular.dart';
import 'package:angular_components/focus/focus_trap.dart';
import 'package:angular_components/laminate/components/modal/modal.dart';
import 'package:angular_components/model/a11y/keyboard_handler_mixin.dart';
import 'package:angular_components/utils/browser/dom_service/dom_service.dart';
import 'package:angular_components/utils/disposer/disposer.dart';
import 'package:angular_components/utils/id_generator/id_generator.dart';

/// A styled container following the Material Spec for Dialogs.
///
/// __Supported Content:__
///
/// - `[header]` -- Header content for the dialog.
/// - `[footer]` -- Footer content for the dialog.
///
/// __Attributes:__
///
///  - `headered` -- Adds a gray background to the dialog header.
///  - `info` -- Styles the dialog as an info dialog.
///
@Component(
  selector: 'material-dialog',
  templateUrl: 'material_dialog.html',
  styleUrls: ['material_dialog.scss.css'],
  directives: [FocusTrapComponent, NgIf],
  changeDetection: ChangeDetectionStrategy.OnPush,
)
class MaterialDialogComponent
    with KeyboardHandlerMixin
    implements AfterContentChecked, OnDestroy {
  @HostBinding('attr.role')
  static const hostRole = 'dialog';

  @HostBinding('attr.aria-modal')
  static const ariaModel = 'true';

  @HostBinding('attr.aria-labelledby')
  String get headerId => shouldShowHeader ? _uid : null;

  final HtmlElement _rootElement;
  final DomService _domService;
  final ChangeDetectorRef _changeDetector;
  final NgZone _ngZone;
  final ModalComponent _modal;
  final _disposer = Disposer.oneShot();
  final _uid = SequentialIdGenerator.fromUUID().nextId();

  HtmlElement _mainElement;
  bool _shouldShowHeader = true;
  bool _shouldShowFooter = true;
  bool shouldShowTopScrollStroke = false;
  bool shouldShowBottomScrollStroke = false;

  final _isInFullscreenModeStreamController = StreamController<bool>();
  bool _isInFullscreenMode;
  bool _shouldListenForFullscreenChanges = false;

  MaterialDialogComponent(
    this._rootElement,
    this._domService,
    this._changeDetector,
    this._ngZone,
    @Optional() this._modal,
  ) {
    escapeHandler = _defaultEscapeHandler;
  }

  @ViewChild('main', read: HtmlElement)
  set main(HtmlElement element) {
    _mainElement = element;
    _disposer.addStreamSubscription(element.onScroll.listen((_) {
      _setHeaderFooterScrollBorder();
    }));
    if (_modal == null) return;
    _disposer.addStreamSubscription(_modal.onOpen.listen((_) {
      _setHeaderFooterScrollBorder();
    }));
  }

  /// Function to handle escape key events from the dialog. By default it tries
  /// to close the parent modal, if any.
  @Input()
  KeyboardEventHandler escapeHandler;

  /// Error to show up in the error section of the dialog.
  @Input()
  String error;

  /// Whether to hide the dialog header.
  @Input()
  set hideHeader(bool shouldHideHeader) =>
      _shouldShowHeader = !shouldHideHeader;

  /// Whether to hide the dialog footer.
  @Input()
  set hideFooter(bool shouldHideFooter) =>
      _shouldShowFooter = !shouldHideFooter;

  bool get shouldShowHeader => _shouldShowHeader;

  bool get shouldShowFooter => _shouldShowFooter;

  /// Whether to show the top/bottom borders on the <main> part of the dialog to
  /// indicate whether there is more content that the user can reach by
  /// scrolling.
  @Input()
  bool shouldShowScrollStrokes = true;

  void _setHeaderFooterScrollBorder() {
    if (!shouldShowScrollStrokes) return;
    _disposer.addDisposable(_domService.scheduleRead(() {
      var shouldShowTopScrollStroke =
          _mainElement.scrollTop > 0 && error == null;
      var shouldShowBottomScrollStroke =
          _mainElement.clientHeight < _mainElement.scrollHeight &&
              _mainElement.scrollTop <
                  _mainElement.scrollHeight - _mainElement.clientHeight;
      if (shouldShowTopScrollStroke != this.shouldShowTopScrollStroke ||
          shouldShowBottomScrollStroke != this.shouldShowBottomScrollStroke) {
        this.shouldShowTopScrollStroke = shouldShowTopScrollStroke;
        this.shouldShowBottomScrollStroke = shouldShowBottomScrollStroke;
        // Update the DOM based on the newly modified state.
        _ngZone.runAfterChangesObserved(() {
          _changeDetector.markForCheck();
        });
      }
    }));
  }

  /// Determines whether to listen for when the dialog enters or exits
  /// fullscreen mode.
  @Input('listenForFullscreenChanges')
  set shouldListenForFullscreenChanges(bool shouldListenForFullscreenChanges) {
    // Don't do anything if [shouldListenForFullscreenChanges] is set to false.
    if (!shouldListenForFullscreenChanges) return;

    // If [_shouldListenForFullscreenChanges] is set to true, then that means
    // that we're already listening to fullscreen changes, so don't do anything.
    if (_shouldListenForFullscreenChanges) return;

    _shouldListenForFullscreenChanges = shouldListenForFullscreenChanges;
    _disposer.addStreamSubscription(window.onResize.listen((_) {
      _listenForFullscreenChanges();
    }));
  }

  /// Stream for when the dialog enters or exits fullscreen mode.
  @Output('fullscreenMode')
  Stream<bool> get isInFullscreenMode =>
      _isInFullscreenModeStreamController.stream;

  void _listenForFullscreenChanges() {
    if (!_shouldListenForFullscreenChanges) return;
{'fraction_non_alphanumeric': 0.09619291582015722, 'fraction_numerical': 0.001943291228690045, 'mean_word_length': 3.989863375936536, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2401583', 'n_tokens_mistral': 3234, 'n_tokens_neox': 3170, 'n_words': 835}