<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 'dart:math';

import 'package:angular/angular.dart';
import 'package:angular/meta.dart';
import 'package:intl/intl.dart';
import 'package:angular_components/button_decorator/button_decorator.dart';
import 'package:angular_components/content/deferred_content.dart';
import 'package:angular_components/content/deferred_content_aware.dart';
import 'package:angular_components/interfaces/has_disabled.dart';
import 'package:angular_components/focus/focus.dart';
import 'package:angular_components/focus/keyboard_only_focus_indicator.dart';
import 'package:angular_components/material_icon/material_icon.dart';
import 'package:angular_components/material_yes_no_buttons/material_yes_no_buttons.dart';
import 'package:angular_components/model/action/async_action.dart';
import 'package:angular_components/model/observable/observable.dart';
import 'package:angular_components/utils/angular/id/id.dart';
import 'package:angular_components/utils/browser/dom_service/dom_service.dart';
import 'package:angular_components/utils/disposer/disposer.dart';

/// A material-styled expansion-panel.
///
/// One or more panels are grouped together in a expansion-panel-set. When a
/// panel is clicked on, the panel contents expand. A panel consists of a name,
/// a value, optional secondary text, and the expanded panel contents.
///
/// Content element with attribute `value` will be used as the "value" of the
/// panel contents when it is in a collapsed state
///
/// Interactions with a panel are done through the parent expansion-set. The set
/// takes into account the state of the other panels in the set, and issues the
/// appropriate actions onto each individual panel.
///
/// __Attributes:__
///
///  - `wide` -- When specified the width of the panel when expanded, is
///    slightly wider then its width when collapsed.
///  - `flat` -- Indicates that the panel should not "float" or separate from
///    other panels when expanded.
///  - `forceContentWhenClosed` -- Keeps expansion panel content in the DOM when
///    the expansion panel is closed. This should only be used in rare
///    circumstances as the content will be tabbable and so will be worse for
///    accessibility.
///
/// __Content Reference:__
///
/// - `focusOnOpen` -- Mark a Focusable or DOM element with #focusOnOpen in the
///   content to have that item be focused when the expansion panel opens.
///
@Component(
  selector: 'material-expansionpanel',
  directives: [
    AutoIdDirective,
    ButtonDirective,
    DeferredContentDirective,
    MaterialIconComponent,
    MaterialSaveCancelButtonsDirective,
    MaterialYesNoButtonsComponent,
    NgIf,
    EnterAcceptsDirective,
    KeyboardOnlyFocusIndicatorDirective,
    KeyUpBoundaryDirective
  ],
  providers: [
    ExistingProvider(DeferredContentAware, MaterialExpansionPanel),
    ExistingProvider(HasDisabled, MaterialExpansionPanel),
    ExistingProvider(FocusableItem, MaterialExpansionPanel),
    ExistingProvider(Focusable, MaterialExpansionPanel),
  ],
  templateUrl: 'material_expansionpanel.html',
  styleUrls: ['material_expansionpanel.scss.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  visibility: Visibility.all, // injected
)
class MaterialExpansionPanel
    implements
        DeferredContentAware,
        HasDisabled,
        OnInit,
        OnDestroy,
        FocusableItem {
  final NgZone _ngZone;
  final ChangeDetectorRef _changeDetector;
  final DomService _domService;
  final _disposer = Disposer.oneShot();
  final _defaultExpandIcon = 'expand_less';
  final bool shouldExpandOnLeft;
  final bool forceContentWhenClosed;
  final _pendingExpandedPanelHeightReads = <Completer<String>>[];

  bool initialized = false;

  MaterialExpansionPanel(
      this._ngZone,
      this._changeDetector,
      this._domService,
      @Attribute('shouldExpandOnLeft') String expandOnLeft,
      @Attribute('forceContentWhenClosed') String forceContent)
      : shouldExpandOnLeft = expandOnLeft != null,
        forceContentWhenClosed = forceContent != null;

  /// Set the auto focus child so that we can focus on it when the panel opens.
  ///
  /// Unfortunately, this only selects the first [AutoFocusDirective] in the
  /// contents of the expansion panel, which means that if there is another
  /// [AutoFocusDirective] in an <ng-content> that is not the .content, that
  /// will get focused instead of the [AutoFocusDirective] inside the .content.
  @visibleForTemplate
  @ContentChild(AutoFocusDirective)
  AutoFocusDirective autoFocusChild;

  Focusable _focusOnOpenChild;

  /// Sets the focus child so that we can focus on it when the panel opens.
  @ContentChild('focusOnOpen')
  set focusOnOpenChild(dynamic element) {
    if (element is Focusable) {
      _focusOnOpenChild = element;
    } else if (element is ElementRef) {
      _focusOnOpenChild = RootFocusable(element.nativeElement);
    } else {
      assert(
          element == null,
          'Warning expansion panel content has a #focus'
          'child which is not an Element, or Focusable');
    }
  }

  HtmlElement _mainPanel;
  @ViewChild('mainPanel')
  set mainPanel(HtmlElement mainPanel) {
    _mainPanel = mainPanel;
    _disposer.addStreamSubscription(_mainPanel.onTransitionEnd.listen((_) {
      // Clear height override so it will match the active child's height.
      _mainPanel.style.height = '';
    }));
  }

  HtmlElement _headerPanel;
  @ViewChild('headerPanel')
  set headerPanel(HtmlElement headerPanel) {
    _headerPanel = headerPanel;
    _disposer.addStreamSubscription(_headerPanel.onTransitionEnd.listen((_) {
      // Clear height override so it will match the active child's height.
      _headerPanel.style.height = '';
    }));
  }

  HtmlElement _mainContent;
  @ViewChild('mainContent')
  set mainContent(HtmlElement mainContent) {
    _mainContent = mainContent;
    if (_mainContent == null) return;
    _completeExpandedPanelHeightReadsIfPossible();
  }

  void _completeExpandedPanelHeightReadsIfPossible() {
    if (_mainContent == null || _contentWrapper == null) return;
    if (_pendingExpandedPanelHeightReads.isNotEmpty) {
      var height = _readMainContentHeight();
      for (var completer in _pendingExpandedPanelHeightReads) {
        completer.complete(height);
      }
      _pendingExpandedPanelHeightReads.clear();
    }
  }

  HtmlElement _headerContent;
  @ViewChild('headerContent')
  set headerContent(HtmlElement headerContent) =>
      _headerContent = headerContent;

  HtmlElement _actionContent;<commit_msg>Only fire events and DOM changes on on-target TransitionEnd events.<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 'dart:math';

import 'package:angular/angular.dart';
import 'package:angular/meta.dart';
import 'package:intl/intl.dart';
import 'package:angular_components/button_decorator/button_decorator.dart';
import 'package:angular_components/content/deferred_content.dart';
import 'package:angular_components/content/deferred_content_aware.dart';
import 'package:angular_components/interfaces/has_disabled.dart';
import 'package:angular_components/focus/focus.dart';
import 'package:angular_components/focus/keyboard_only_focus_indicator.dart';
import 'package:angular_components/material_icon/material_icon.dart';
import 'package:angular_components/material_yes_no_buttons/material_yes_no_buttons.dart';
import 'package:angular_components/model/action/async_action.dart';
import 'package:angular_components/model/observable/observable.dart';
import 'package:angular_components/utils/angular/id/id.dart';
import 'package:angular_components/utils/browser/dom_service/dom_service.dart';
import 'package:angular_components/utils/disposer/disposer.dart';

/// A material-styled expansion-panel.
///
/// One or more panels are grouped together in a expansion-panel-set. When a
/// panel is clicked on, the panel contents expand. A panel consists of a name,
/// a value, optional secondary text, and the expanded panel contents.
///
/// Content element with attribute `value` will be used as the "value" of the
/// panel contents when it is in a collapsed state
///
/// Interactions with a panel are done through the parent expansion-set. The set
/// takes into account the state of the other panels in the set, and issues the
/// appropriate actions onto each individual panel.
///
/// __Attributes:__
///
///  - `wide` -- When specified the width of the panel when expanded, is
///    slightly wider then its width when collapsed.
///  - `flat` -- Indicates that the panel should not "float" or separate from
///    other panels when expanded.
///  - `forceContentWhenClosed` -- Keeps expansion panel content in the DOM when
///    the expansion panel is closed. This should only be used in rare
///    circumstances as the content will be tabbable and so will be worse for
///    accessibility.
///
/// __Content Reference:__
///
/// - `focusOnOpen` -- Mark a Focusable or DOM element with #focusOnOpen in the
///   content to have that item be focused when the expansion panel opens.
///
@Component(
  selector: 'material-expansionpanel',
  directives: [
    AutoIdDirective,
    ButtonDirective,
    DeferredContentDirective,
    MaterialIconComponent,
    MaterialSaveCancelButtonsDirective,
    MaterialYesNoButtonsComponent,
    NgIf,
    EnterAcceptsDirective,
    KeyboardOnlyFocusIndicatorDirective,
    KeyUpBoundaryDirective
  ],
  providers: [
    ExistingProvider(DeferredContentAware, MaterialExpansionPanel),
    ExistingProvider(HasDisabled, MaterialExpansionPanel),
    ExistingProvider(FocusableItem, MaterialExpansionPanel),
    ExistingProvider(Focusable, MaterialExpansionPanel),
  ],
  templateUrl: 'material_expansionpanel.html',
  styleUrls: ['material_expansionpanel.scss.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  visibility: Visibility.all, // injected
)
class MaterialExpansionPanel
    implements
        DeferredContentAware,
        HasDisabled,
        OnInit,
        OnDestroy,
        FocusableItem {
  final NgZone _ngZone;
  final ChangeDetectorRef _changeDetector;
  final DomService _domService;
  final _disposer = Disposer.oneShot();
  final _defaultExpandIcon = 'expand_less';
  final bool shouldExpandOnLeft;
  final bool forceContentWhenClosed;
  final _pendingExpandedPanelHeightReads = <Completer<String>>[];

  bool initialized = false;

  MaterialExpansionPanel(
      this._ngZone,
      this._changeDetector,
      this._domService,
      @Attribute('shouldExpandOnLeft') String expandOnLeft,
      @Attribute('forceContentWhenClosed') String forceContent)
      : shouldExpandOnLeft = expandOnLeft != null,
        forceContentWhenClosed = forceContent != null;

  /// Set the auto focus child so that we can focus on it when the panel opens.
  ///
  /// Unfortunately, this only selects the first [AutoFocusDirective] in the
  /// contents of the expansion panel, which means that if there is another
  /// [AutoFocusDirective] in an <ng-content> that is not the .content, that
  /// will get focused instead of the [AutoFocusDirective] inside the .content.
  @visibleForTemplate
  @ContentChild(AutoFocusDirective)
  AutoFocusDirective autoFocusChild;

  Focusable _focusOnOpenChild;

  /// Sets the focus child so that we can focus on it when the panel opens.
  @ContentChild('focusOnOpen')
  set focusOnOpenChild(dynamic element) {
    if (element is Focusable) {
      _focusOnOpenChild = element;
    } else if (element is ElementRef) {
      _focusOnOpenChild = RootFocusable(element.nativeElement);
    } else {
      assert(
          element == null,
          'Warning expansion panel content has a #focus'
          'child which is not an Element, or Focusable');
    }
  }

  HtmlElement _mainPanel;
  @ViewChild('mainPanel')
  set mainPanel(HtmlElement mainPanel) {
    _mainPanel = mainPanel;
    _disposer.addStreamSubscription(_mainPanel.onTransitionEnd
        .where((e) => e.eventPhase == Event.AT_TARGET)
        .listen((_) {
      // Clear height override so it will match the active child's height.
      _mainPanel.style.height = '';
    }));
  }

  HtmlElement _headerPanel;
  @ViewChild('headerPanel')
  set headerPanel(HtmlElement headerPanel) {
    _headerPanel = headerPanel;
    _disposer.addStreamSubscription(_headerPanel.onTransitionEnd
        .where((e) => e.eventPhase == Event.AT_TARGET)
        .listen((_) {
      // Clear height override so it will match the active child's height.
      _headerPanel.style.height = '';
    }));
  }

  HtmlElement _mainContent;
  @ViewChild('mainContent')
  set mainContent(HtmlElement mainContent) {
    _mainContent = mainContent;
    if (_mainContent == null) return;
    _completeExpandedPanelHeightReadsIfPossible();
  }

  void _completeExpandedPanelHeightReadsIfPossible() {
    if (_mainContent == null || _contentWrapper == null) return;
    if (_pendingExpandedPanelHeightReads.isNotEmpty) {
      var height = _readMainContentHeight();
      for (var completer in _pendingExpandedPanelHeightReads) {
        completer.complete(height);
      }
      _pendingExpandedPanelHeightReads.clear();
    }
  }

  HtmlElement _headerContent;
  @ViewChild('headerContent')
  set headerContent(HtmlElement headerContent) =>
      _headerContent = headerContent;

  HtmlElement _actionContent;
{'fraction_non_alphanumeric': 0.09175512578159081, 'fraction_numerical': 0.0005816489748436818, 'mean_word_length': 4.012755102040816, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 13, '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': '6543400', 'n_tokens_mistral': 3842, 'n_tokens_neox': 3779, 'n_words': 1106}