Skip to content
419 lines (348 sloc) 11.5 KB
<!--
Copyright (c) 2014 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
-->
<!--
`core-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`, `core-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:
<core-drawer-panel>
<div drawer> Drawer panel... </div>
<div main> Main panel... </div>
</core-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 `core-header-panel`.
Example:
<core-drawer-panel>
<core-header-panel drawer>
<core-toolbar></core-toolbar>
<div> Drawer content... </div>
</core-header-panel>
<core-header-panel main>
<core-toolbar></core-toolbar>
<div> Main content... </div>
</core-header-panel>
</core-drawer-panel>
An element that should toggle the drawer will automatically do so if it's
given the `core-drawer-toggle` attribute. Also this element will automatically
be hidden in wide layout.
Example:
<core-drawer-panel>
<core-header-panel drawer>
<core-toolbar>
<div>Application</div>
</core-toolbar>
<div> Drawer content... </div>
</core-header-panel>
<core-header-panel main>
<core-toolbar>
<core-icon-button icon="menu" core-drawer-toggle></core-icon-button>
<div>Title</div>
</core-toolbar>
<div> Main content... </div>
</core-header-panel>
</core-drawer-panel>
To position the drawer to the right, add `rightDrawer` attribute.
<core-drawer-panel rightDrawer>
<div drawer> Drawer panel... </div>
<div main> Main panel... </div>
</core-drawer-panel>
@group Polymer Core Elements
@element core-drawer-panel
@homepage github.io
-->
<link rel="import" href="../core-media-query/core-media-query.html">
<link rel="import" href="../core-selector/core-selector.html">
<polymer-element name="core-drawer-panel" touch-action="auto">
<template>
<link rel="stylesheet" href="core-drawer-panel.css">
<core-media-query query="max-width: {{forceNarrow ? '' : responsiveWidth}}" queryMatches="{{queryMatches}}"></core-media-query>
<core-selector class="{{ {'narrow-layout' : narrow, transition : transition, dragging : dragging, 'right-drawer': rightDrawer} | tokenList }}" valueattr="id" selected="{{selected}}">
<div id="main" _style="left: {{ narrow || rightDrawer ? '0' : drawerWidth }}; right: {{ rightDrawer ? (narrow ? '' : drawerWidth) : '' }};">
<content select="[main]"></content>
<div id="scrim" on-tap="{{togglePanel}}"></div>
<div id="edgeSwipeOverlay" hidden?="{{!narrow || disableEdgeSwipe}}"></div>
</div>
<div id="drawer" _style="width: {{ drawerWidth }}">
<content select="[drawer]"></content>
</div>
</core-selector>
</template>
<script>
Polymer('core-drawer-panel', {
/**
* Fired when the narrow layout changes.
*
* @event core-responsive-change
* @param {Object} detail
* @param {boolean} detail.narrow true if the panel is in narrow layout.
*/
/**
* Fired when the selected panel changes.
*
* Listening for this event is an alternative to observing changes in the `selected` attribute.
* This event is fired both when a panel is selected and deselected.
* The `isSelected` detail property contains the selection state.
*
* @event core-select
* @param {Object} detail
* @param {boolean} detail.isSelected true for selection and false for deselection
* @param {Object} detail.item the panel that the event refers to
*/
publish: {
/**
* Width of the drawer panel.
*
* @attribute drawerWidth
* @type string
* @default '256px'
*/
drawerWidth: '256px',
/**
* Max-width when the panel changes to narrow layout.
*
* @attribute responsiveWidth
* @type string
* @default '640px'
*/
responsiveWidth: '640px',
/**
* The panel that is being selected. `drawer` for the drawer panel and
* `main` for the main panel.
*
* @attribute selected
* @type string
* @default null
*/
selected: {value: null, reflect: true},
/**
* The panel to be selected when `core-drawer-panel` changes to narrow
* layout.
*
* @attribute defaultSelected
* @type string
* @default 'main'
*/
defaultSelected: 'main',
/**
* Returns true if the panel is in narrow layout. This is useful if you
* need to show/hide elements based on the layout.
*
* @attribute narrow
* @type boolean
* @default false
*/
narrow: {value: false, reflect: true},
/**
* If true, position the drawer to the right.
*
* @attribute rightDrawer
* @type boolean
* @default false
*/
rightDrawer: false,
/**
* If true, swipe to open/close the drawer is disabled.
*
* @attribute disableSwipe
* @type boolean
* @default false
*/
disableSwipe: false,
/**
* If true, ignore `responsiveWidth` setting and force the narrow layout.
*
* @attribute forceNarrow
* @type boolean
* @default false
*/
forceNarrow: false,
/**
* If true, swipe from the edge is disabled.
*
* @attribute disableEdgeSwipe
* @type boolean
* @default false
*/
disableEdgeSwipe: false
},
eventDelegates: {
trackstart: 'trackStart',
trackx: 'trackx',
trackend: 'trackEnd',
down: 'downHandler',
up: 'upHandler',
tap: 'tapHandler'
},
// Whether the transition is enabled.
transition: false,
// How many pixels on the side of the screen are sensitive to edge swipes and peek.
edgeSwipeSensitivity: 15,
// Whether the drawer is peeking out from the edge.
peeking: false,
// Whether the user is dragging the drawer interactively.
dragging: false,
// Whether the browser has support for the transform CSS property.
hasTransform: true,
// Whether the browser has support for the will-change CSS property.
hasWillChange: true,
// The attribute on elements that should toggle the drawer on tap, also
// elements will automatically be hidden in wide layout.
toggleAttribute: 'core-drawer-toggle',
created: function() {
this.hasTransform = 'transform' in this.style;
this.hasWillChange = 'willChange' in this.style;
},
domReady: function() {
// to avoid transition at the beginning e.g. page loads
// NOTE: domReady is already raf delayed and delaying another frame
// ensures a layout has occurred.
this.async(function() {
this.transition = true;
});
},
/**
* Toggles the panel open and closed.
*
* @method togglePanel
*/
togglePanel: function() {
this.selected = this.isMainSelected() ? 'drawer' : 'main';
},
/**
* Opens the drawer.
*
* @method openDrawer
*/
openDrawer: function() {
this.selected = 'drawer';
},
/**
* Closes the drawer.
*
* @method closeDrawer
*/
closeDrawer: function() {
this.selected = 'main';
},
queryMatchesChanged: function() {
this.narrow = this.queryMatches || this.forceNarrow;
if (this.narrow) {
this.selected = this.defaultSelected;
}
this.setAttribute('touch-action', this.swipeAllowed() ? 'pan-y' : '');
this.fire('core-responsive-change', {narrow: this.narrow});
},
forceNarrowChanged: function() {
this.queryMatchesChanged();
},
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.peeking = true;
},
stopEdgePeak: function() {
if (this.peeking) {
this.peeking = false;
this.moveDrawer(null);
}
},
downHandler: function(e) {
if (!this.dragging && this.isMainSelected() && this.isEdgeTouch(e)) {
this.startEdgePeek();
}
},
upHandler: function(e) {
this.stopEdgePeak();
},
tapHandler: function(e) {
if (e.target && this.toggleAttribute &&
e.target.hasAttribute(this.toggleAttribute)) {
this.togglePanel();
}
},
isEdgeTouch: function(e) {
return !this.disableEdgeSwipe && this.swipeAllowed() &&
(this.rightDrawer ?
e.pageX >= this.offsetWidth - this.edgeSwipeSensitivity :
e.pageX <= this.edgeSwipeSensitivity);
},
trackStart : function(e) {
if (this.swipeAllowed()) {
this.dragging = true;
if (this.isMainSelected()) {
this.dragging = this.peeking || this.isEdgeTouch(e);
}
if (this.dragging) {
this.width = this.$.drawer.offsetWidth;
this.transition = false;
e.preventTap();
}
}
},
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(e) {
if (this.dragging) {
if (this.peeking) {
if (Math.abs(e.dx) <= this.edgeSwipeSensitivity) {
return; // Ignore trackx until we move past the edge peek.
}
this.peeking = false;
}
this.moveDrawer(this.translateXForDeltaX(e.dx));
}
},
trackEnd : function(e) {
if (this.dragging) {
this.dragging = false;
this.transition = true;
this.moveDrawer(null);
if (this.rightDrawer) {
this.selected = e.xDirection > 0 ? 'main' : 'drawer';
} else {
this.selected = e.xDirection > 0 ? 'drawer' : 'main';
}
}
},
transformForTranslateX: function(translateX) {
if (translateX === null) {
return '';
}
return this.hasWillChange ? 'translateX(' + translateX + 'px)' :
'translate3d(' + translateX + 'px, 0, 0)';
},
moveDrawer: function(translateX) {
var s = this.$.drawer.style;
if (this.hasTransform) {
s.transform = this.transformForTranslateX(translateX);
} else {
s.webkitTransform = this.transformForTranslateX(translateX);
}
}
});
</script>
</polymer-element>
Jump to Line
Something went wrong with that request. Please try again.