UNPKG

npm-polymer-elements

Version:

Polymer Elements package for npm

766 lines (629 loc) 21.3 kB
<!-- 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="../polymer/polymer.html"> <link rel="import" href="../iron-media-query/iron-media-query.html"> <link rel="import" href="../iron-selector/iron-selector.html"> <link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html"> <!-- Material design: [Navigation drawer](https://www.google.com/design/spec/patterns/navigation-drawer.html) `paper-drawer-panel` contains a drawer panel and a main panel. The drawer and the main panel are side-by-side with drawer on the left. When the browser window size is smaller than the `responsiveWidth`, `paper-drawer-panel` changes to narrow layout. In narrow layout, the drawer will be stacked on top of the main panel. The drawer will slide in/out to hide/reveal the main panel. Use the attribute `drawer` to indicate that the element is the drawer panel and `main` to indicate that the element is the main panel. Example: <paper-drawer-panel> <div drawer> Drawer panel... </div> <div main> Main panel... </div> </paper-drawer-panel> The drawer and the main panels are not scrollable. You can set CSS overflow property on the elements to make them scrollable or use `paper-header-panel`. Example: <paper-drawer-panel> <paper-header-panel drawer> <paper-toolbar></paper-toolbar> <div> Drawer content... </div> </paper-header-panel> <paper-header-panel main> <paper-toolbar></paper-toolbar> <div> Main content... </div> </paper-header-panel> </paper-drawer-panel> An element that should toggle the drawer will automatically do so if it's given the `paper-drawer-toggle` attribute. Also this element will automatically be hidden in wide layout. Example: <paper-drawer-panel> <paper-header-panel drawer> <paper-toolbar> <div>Application</div> </paper-toolbar> <div> Drawer content... </div> </paper-header-panel> <paper-header-panel main> <paper-toolbar> <paper-icon-button icon="menu" paper-drawer-toggle></paper-icon-button> <div>Title</div> </paper-toolbar> <div> Main content... </div> </paper-header-panel> </paper-drawer-panel> To position the drawer to the right, add `right-drawer` attribute. <paper-drawer-panel right-drawer> <div drawer> Drawer panel... </div> <div main> Main panel... </div> </paper-drawer-panel> ### Styling To change the main container: paper-drawer-panel { --paper-drawer-panel-main-container: { background-color: gray; }; } To change the drawer container when it's in the left side: paper-drawer-panel { --paper-drawer-panel-left-drawer-container: { background-color: white; }; } To change the drawer container when it's in the right side: paper-drawer-panel { --paper-drawer-panel-right-drawer-container: { background-color: white; }; } To customize the scrim: paper-drawer-panel { --paper-drawer-panel-scrim: { background-color: red; }; } The following custom properties and mixins are available for styling: Custom property | Description | Default ----------------|-------------|---------- `--paper-drawer-panel-scrim-opacity` | Scrim opacity | 1 `--paper-drawer-panel-drawer-container` | Mixin applied to drawer container | {} `--paper-drawer-panel-left-drawer-container` | Mixin applied to container when it's in the left side | {} `--paper-drawer-panel-main-container` | Mixin applied to main container | {} `--paper-drawer-panel-right-drawer-container` | Mixin applied to container when it's in the right side | {} `--paper-drawer-panel-scrim` | Mixin applied to scrim | {} @group Paper elements @element paper-drawer-panel @demo demo/index.html @hero hero.svg --> <dom-module id="paper-drawer-panel"> <template> <style> :host { display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; } iron-selector > #drawer { position: absolute; top: 0; left: 0; height: 100%; background-color: white; -moz-box-sizing: border-box; box-sizing: border-box; @apply(--paper-drawer-panel-drawer-container); } .transition > #drawer { transition: -webkit-transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s; transition: transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s; } .left-drawer > #drawer { @apply(--paper-drawer-panel-left-drawer-container); } .right-drawer > #drawer { left: auto; right: 0; @apply(--paper-drawer-panel-right-drawer-container); } iron-selector > #main { position: absolute; top: 0; right: 0; bottom: 0; @apply(--paper-drawer-panel-main-container); } .transition > #main { transition: left ease-in-out 0.3s, padding ease-in-out 0.3s; } .right-drawer > #main { left: 0; } .right-drawer.transition > #main { transition: right ease-in-out 0.3s, padding ease-in-out 0.3s; } #main > ::content > [main] { height: 100%; } #drawer > ::content > [drawer] { height: 100%; } #scrim { position: absolute; top: 0; right: 0; bottom: 0; left: 0; visibility: hidden; opacity: 0; transition: opacity ease-in-out 0.38s, visibility ease-in-out 0.38s; background-color: rgba(0, 0, 0, 0.3); @apply(--paper-drawer-panel-scrim); } .narrow-layout > #drawer { will-change: transform; } .narrow-layout > #drawer.iron-selected { box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15); } .right-drawer.narrow-layout > #drawer.iron-selected { box-shadow: -2px 2px 4px rgba(0, 0, 0, 0.15); } .narrow-layout > #drawer > ::content > [drawer] { border: 0; } .left-drawer.narrow-layout > #drawer:not(.iron-selected) { -webkit-transform: translateX(-100%); transform: translateX(-100%); } .right-drawer.narrow-layout > #drawer:not(.iron-selected) { left: auto; visibility: hidden; -webkit-transform: translateX(100%); transform: translateX(100%); } .right-drawer.narrow-layout.dragging > #drawer:not(.iron-selected), .right-drawer.narrow-layout.peeking > #drawer:not(.iron-selected) { visibility: visible; } .narrow-layout > #main { padding: 0; } .right-drawer.narrow-layout > #main { left: 0; right: 0; } .narrow-layout > #main:not(.iron-selected) > #scrim, .dragging > #main > #scrim { visibility: visible; opacity: var(--paper-drawer-panel-scrim-opacity, 1); } .narrow-layout > #main > * { margin: 0; min-height: 100%; left: 0; right: 0; -moz-box-sizing: border-box; box-sizing: border-box; } iron-selector:not(.narrow-layout) #main ::content [paper-drawer-toggle] { display: none; } </style> <iron-media-query id="mq" on-query-matches-changed="_onQueryMatchesChanged" query="[[_computeMediaQuery(forceNarrow, responsiveWidth)]]"> </iron-media-query> <iron-selector attr-for-selected="id" class$="[[_computeIronSelectorClass(narrow, _transition, dragging, rightDrawer, peeking)]]" activate-event="" selected="[[selected]]"> <div id="main" style$="[[_computeMainStyle(narrow, rightDrawer, drawerWidth)]]" on-transitionend="_onMainTransitionEnd"> <content select="[main]"></content> <div id="scrim" on-tap="closeDrawer"></div> </div> <div id="drawer" style$="[[_computeDrawerStyle(drawerWidth)]]"> <content select="[drawer]"></content> </div> </iron-selector> </template> <script> (function() { 'use strict'; // this would be the only `paper-drawer-panel` in // the whole app that can be in `dragging` state var sharedPanel = null; function classNames(obj) { var classes = []; for (var key in obj) { if (obj.hasOwnProperty(key) && obj[key]) { classes.push(key); } } return classes.join(' '); } Polymer({ is: 'paper-drawer-panel', behaviors: [Polymer.IronResizableBehavior], /** * Fired when the narrow layout changes. * * @event paper-responsive-change {{narrow: boolean}} detail - * narrow: true if the panel is in narrow layout. */ /** * Fired when the a panel is selected. * * Listening for this event is an alternative to observing changes in the `selected` attribute. * This event is fired both when a panel is selected. * * @event iron-select {{item: Object}} detail - * item: The panel that the event refers to. */ /** * Fired when a panel is deselected. * * Listening for this event is an alternative to observing changes in the `selected` attribute. * This event is fired both when a panel is deselected. * * @event iron-deselect {{item: Object}} detail - * item: The panel that the event refers to. */ properties: { /** * The panel to be selected when `paper-drawer-panel` changes to narrow * layout. */ defaultSelected: { type: String, value: 'main' }, /** * If true, swipe from the edge is disabled. */ disableEdgeSwipe: { type: Boolean, value: false }, /** * If true, swipe to open/close the drawer is disabled. */ disableSwipe: { type: Boolean, value: false }, /** * Whether the user is dragging the drawer interactively. */ dragging: { type: Boolean, value: false, readOnly: true, notify: true }, /** * Width of the drawer panel. */ drawerWidth: { type: String, value: '256px' }, /** * How many pixels on the side of the screen are sensitive to edge * swipes and peek. */ edgeSwipeSensitivity: { type: Number, value: 30 }, /** * If true, ignore `responsiveWidth` setting and force the narrow layout. */ forceNarrow: { type: Boolean, value: false }, /** * Whether the browser has support for the transform CSS property. */ hasTransform: { type: Boolean, value: function() { return 'transform' in this.style; } }, /** * Whether the browser has support for the will-change CSS property. */ hasWillChange: { type: Boolean, value: function() { return 'willChange' in this.style; } }, /** * Returns true if the panel is in narrow layout. This is useful if you * need to show/hide elements based on the layout. */ narrow: { reflectToAttribute: true, type: Boolean, value: false, readOnly: true, notify: true }, /** * Whether the drawer is peeking out from the edge. */ peeking: { type: Boolean, value: false, readOnly: true, notify: true }, /** * Max-width when the panel changes to narrow layout. */ responsiveWidth: { type: String, value: '600px' }, /** * If true, position the drawer to the right. */ rightDrawer: { type: Boolean, value: false }, /** * The panel that is being selected. `drawer` for the drawer panel and * `main` for the main panel. */ selected: { reflectToAttribute: true, notify: true, type: String, value: null }, /** * The attribute on elements that should toggle the drawer on tap, also elements will * automatically be hidden in wide layout. */ drawerToggleAttribute: { type: String, value: 'paper-drawer-toggle' }, /** * Whether the transition is enabled. */ _transition: { type: Boolean, value: false }, }, listeners: { tap: '_onTap', track: '_onTrack', down: '_downHandler', up: '_upHandler' }, observers: [ '_forceNarrowChanged(forceNarrow, defaultSelected)' ], /** * Toggles the panel open and closed. * * @method togglePanel */ togglePanel: function() { if (this._isMainSelected()) { this.openDrawer(); } else { this.closeDrawer(); } }, /** * Opens the drawer. * * @method openDrawer */ openDrawer: function() { this.selected = 'drawer'; }, /** * Closes the drawer. * * @method closeDrawer */ closeDrawer: function() { this.selected = 'main'; }, ready: function() { // Avoid transition at the beginning e.g. page loads and enable // transitions only after the element is rendered and ready. this._transition = true; }, _onMainTransitionEnd: function (e) { if (e.currentTarget === this.$.main && (e.propertyName === 'left' || e.propertyName === 'right')) { this.notifyResize(); } }, _computeIronSelectorClass: function(narrow, transition, dragging, rightDrawer, peeking) { return classNames({ dragging: dragging, 'narrow-layout': narrow, 'right-drawer': rightDrawer, 'left-drawer': !rightDrawer, transition: transition, peeking: peeking }); }, _computeDrawerStyle: function(drawerWidth) { return 'width:' + drawerWidth + ';'; }, _computeMainStyle: function(narrow, rightDrawer, drawerWidth) { var style = ''; style += 'left:' + ((narrow || rightDrawer) ? '0' : drawerWidth) + ';'; if (rightDrawer) { style += 'right:' + (narrow ? '' : drawerWidth) + ';'; } return style; }, _computeMediaQuery: function(forceNarrow, responsiveWidth) { return forceNarrow ? '' : '(max-width: ' + responsiveWidth + ')'; }, _computeSwipeOverlayHidden: function(narrow, disableEdgeSwipe) { return !narrow || disableEdgeSwipe; }, _onTrack: function(event) { if (sharedPanel && this !== sharedPanel) { return; } switch (event.detail.state) { case 'start': this._trackStart(event); break; case 'track': this._trackX(event); break; case 'end': this._trackEnd(event); break; } }, _responsiveChange: function(narrow) { this._setNarrow(narrow); if (this.narrow) { this.selected = this.defaultSelected; } this.setScrollDirection(this._swipeAllowed() ? 'y' : 'all'); this.fire('paper-responsive-change', {narrow: this.narrow}); }, _onQueryMatchesChanged: function(event) { this._responsiveChange(event.detail.value); }, _forceNarrowChanged: function() { // set the narrow mode only if we reached the `responsiveWidth` this._responsiveChange(this.forceNarrow || this.$.mq.queryMatches); }, _swipeAllowed: function() { return this.narrow && !this.disableSwipe; }, _isMainSelected: function() { return this.selected === 'main'; }, _startEdgePeek: function() { this.width = this.$.drawer.offsetWidth; this._moveDrawer(this._translateXForDeltaX(this.rightDrawer ? -this.edgeSwipeSensitivity : this.edgeSwipeSensitivity)); this._setPeeking(true); }, _stopEdgePeek: function() { if (this.peeking) { this._setPeeking(false); this._moveDrawer(null); } }, _downHandler: function(event) { if (!this.dragging && this._isMainSelected() && this._isEdgeTouch(event) && !sharedPanel) { this._startEdgePeek(); // cancel selection event.preventDefault(); // grab this panel sharedPanel = this; } }, _upHandler: function() { this._stopEdgePeek(); // release the panel sharedPanel = null; }, _onTap: function(event) { var targetElement = Polymer.dom(event).localTarget; var isTargetToggleElement = targetElement && this.drawerToggleAttribute && targetElement.hasAttribute(this.drawerToggleAttribute); if (isTargetToggleElement) { this.togglePanel(); } }, _isEdgeTouch: function(event) { var x = event.detail.x; return !this.disableEdgeSwipe && this._swipeAllowed() && (this.rightDrawer ? x >= this.offsetWidth - this.edgeSwipeSensitivity : x <= this.edgeSwipeSensitivity); }, _trackStart: function(event) { if (this._swipeAllowed()) { sharedPanel = this; this._setDragging(true); if (this._isMainSelected()) { this._setDragging(this.peeking || this._isEdgeTouch(event)); } if (this.dragging) { this.width = this.$.drawer.offsetWidth; this._transition = false; } } }, _translateXForDeltaX: function(deltaX) { var isMain = this._isMainSelected(); if (this.rightDrawer) { return Math.max(0, isMain ? this.width + deltaX : deltaX); } else { return Math.min(0, isMain ? deltaX - this.width : deltaX); } }, _trackX: function(event) { if (this.dragging) { var dx = event.detail.dx; if (this.peeking) { if (Math.abs(dx) <= this.edgeSwipeSensitivity) { // Ignore trackx until we move past the edge peek. return; } this._setPeeking(false); } this._moveDrawer(this._translateXForDeltaX(dx)); } }, _trackEnd: function(event) { if (this.dragging) { var xDirection = event.detail.dx > 0; this._setDragging(false); this._transition = true; sharedPanel = null; this._moveDrawer(null); if (this.rightDrawer) { this[xDirection ? 'closeDrawer' : 'openDrawer'](); } else { this[xDirection ? 'openDrawer' : 'closeDrawer'](); } } }, _transformForTranslateX: function(translateX) { if (translateX === null) { return ''; } return this.hasWillChange ? 'translateX(' + translateX + 'px)' : 'translate3d(' + translateX + 'px, 0, 0)'; }, _moveDrawer: function(translateX) { this.transform(this._transformForTranslateX(translateX), this.$.drawer); } }); }()); </script> </dom-module>