iron-doc-property.html 6.72 KB
<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link rel="import" href="../marked-element/marked-element.html">
<link rel="import" href="../paper-styles/typography.html">
<link rel="import" href="../polymer/polymer.html">

<!--
Renders documentation describing a specific property of an element.

Give it a hydrolysis `PropertyDescriptor` (via `descriptor`), and watch it go!
-->
<dom-module id="iron-doc-property">

  <link rel="import" type="css" href="iron-doc-property.css">

  <template>
    <div id="transitionMask">
      <div id="signature">
        <span class="name">{{descriptor.name}}</span><span class="params">(<span>{{_paramText}}</span>)</span>
        <span class="return" hidden$="{{!descriptor.return}}"><span class="type">{{descriptor.return.type}}</span></span>
      </div>
      <div id="details">
        <div id="meta" hidden$="{{_computeHideMeta(descriptor)}}">
          <span id="type" class="type">{{descriptor.type}}</span>
          <span id="default" hidden$="{{_computeHideDefault(descriptor.default)}}">default: <span class="value">{{_computeDefaultDisplay(descriptor.default)}}</span></span>
          <template is="dom-if" if="{{descriptor.readOnly}}"><span>&nbsp;readOnly</span></template>
          <template is="dom-if" if="{{descriptor.notify}}"><span>&nbsp;notify</span></template>
        </div>
        <ol id="params" hidden$="{{_computeHideParams(descriptor,return)}}">
          <template is="dom-repeat" items="{{descriptor.params}}">
            <li hidden$="{{!item.type}}">
              <span class="name">{{item.name}}</span>
              <span class="type">{{item.type}}</span>
              <marked-element markdown="{{item.desc}}"></marked-element>
            </li>
          </template>
          <li class="return" hidden$="{{!descriptor.return}}">Returns
            <span class="type">{{descriptor.return.type}}</span>
            <marked-element markdown="{{descriptor.return.desc}}"></marked-element>
          </li>
        </ol>
        <marked-element id="desc" markdown="{{descriptor.desc}}" hidden$="{{!descriptor.desc}}"></marked-element>
      </div>
    </div>
  </template>

</dom-module>

<script>
(function() {

  Polymer({

    is: 'iron-doc-property',

    properties: {

      /**
       * The [Hydrolysis](https://github.com/PolymerLabs/hydrolysis)-generated
       * element descriptor to display details for.
       *
       * Alternatively, the element descriptor can be provided as JSON via the text content
       * of this element.
       *
       * @type {hydrolysis.PropertyDescriptor}
       */
      descriptor: {
        type:     Object,
        observer: '_descriptorChanged',
      },

      /**
       * Whether the property should show a one-liner, or full summary.
       *
       * Note that this property _is_ reflected as an attribute, but we perform
       * the reflection manually. In order to support the CSS transitions, we
       * must calculate the element height before setting the attribute.
       */
      collapsed: {
        type:     Boolean,
        value:    false,
        observer: '_collapsedChanged',
      },

    },

    listeners: {
      'transitionEnd':       '_onTransitionEnd',
      'webkitTransitionEnd': '_onTransitionEnd',
    },

    ready: function() {
      this._isReady = true;
    },

    /**
     * Resets any state that was set up for transitions.
     *
     * We are careful to reset our explicit heights after a transition
     * completes, so that the property doesn't clip values if the user resizes
     * their viewport.
     */
    _onTransitionEnd: function(event) {
      if (event.path[0] !== this.$.transitionMask) return;
      this.$.transitionMask.style.height = '';
    },

    _descriptorChanged: function() {
      this.toggleAttribute('private',       this.descriptor.private);
      this.toggleAttribute('configuration', this.descriptor.configuration);
      this.toggleAttribute('function',      this.descriptor.function);
      this._paramText = (this.descriptor.params || []).map(function(param) {
        return param.name;
      }).join(', ');
    },

    /**
     * Reflects `collapsed` as the `_collapsed` attribute.
     *
     * "Why not use `reflectToAttribute: true`?", you ask? A fine question!
     *
     * We avoid simple reflection purely because there is no purely declarative
     * way of transitioning to/from `height: auto`. This callback manages
     * setting explicit heights for the property so that CSS can interpolate it.
     *
     * @see #_onTransitionEnd
     */
    _collapsedChanged: function() {
      if (!this._isReady) {
        this.toggleAttribute('_collapsed', this.collapsed);
        return;
      }

      var container = this.$.transitionMask;
      var collapsed = this.collapsed;

      // Measure `height: auto`, which we will need regardless of transition
      // direction. We assume that the collapsed state has an explicit height
      // set via CSS rules; so we do not bother measuring that.
      container.style.height = 'auto';
      var fullHeight = container.offsetHeight;

      // Then, we reset to the start state. Changing directions mid-transition
      // is _not_ supported!
      if (this.collapsed) {
        container.style.height = fullHeight + 'px'; // Height 'auto'.
      } else {
        container.style.height = ''; // Height specified by CSS rule.
      }

      // We must wait a frame so that the transition engine has a chance to know
      // that something actually changed.
      requestAnimationFrame(function() {
        this.toggleAttribute('_collapsed', collapsed);
        if (this.collapsed) {
          container.style.height = ''; // Height specified by CSS rule.
        } else {
          container.style.height = fullHeight + 'px'; // Height 'auto'.
        }
      }.bind(this));
    },

    // hidden if no type and no defaults
    _computeHideMeta: function(descriptor) {
      return descriptor.type === undefined &&  descriptor.default === undefined;
    },

    // hidden if no params, and no return value
    _computeHideParams: function(descriptor,ret) {
      return (!descriptor.params || descriptor.params.length === 0) && !ret;
    },

    _computeHideDefault: function(def) {
      return def === undefined;
    },

    _computeDefaultDisplay: function(def) {
      if (def === '')
        return "''";
      return def;
    }

  });

})();
</script>