Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
pdgago committed May 5, 2017
1 parent 40c7a4d commit 3e7c6c2
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 314 deletions.
6 changes: 3 additions & 3 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@

<script src="../../webcomponentsjs/webcomponents-lite.js"></script>

<link rel="import" href="../dropdown-menu-container.html">
<link rel="import" href="../dropdown-menu.html">

<style>
</style>
</head>
<body>

<dropdown-menu-container>
<dropdown-menu>
<button>Open</button>

<template>
<button>Menu 1</button>
<button>Menu 2</button>
<button>Menu 3</button>
</template>
</dropdown-menu-container>
</dropdown-menu>

</body>
</html>
91 changes: 0 additions & 91 deletions dropdown-menu-container.html

This file was deleted.

265 changes: 265 additions & 0 deletions dropdown-menu-overlay.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
<link rel="import" href="../polymer/polymer-element.html">
<link rel="import" href="../overlay-container/overlay-mixin.html">

<!--
By default this element center the dropdown in the middle of the screen.
-->
<dom-module id="dropdown-menu-overlay">
<template>
<style>
:host {
align-items: center;
bottom: 0;
display: flex;
flex-direction: column;
justify-content: center;
left: 0;
margin: auto;
position: fixed;
right: 0;
top: 0;
}

/* content box */
#content {
background: white;
border-radius: 2px;
box-shadow: 0 1px 20px 2px rgba(0, 0, 0, 0.2);
min-width: 180px;
position: absolute;
will-change: transform;
}

:host([small-screen]) #content {
width: 80%;
min-width: auto;
will-change: initial;
}

#content ::slotted(button),
#content ::slotted(a) {
background: none;
border: 0;
color: initial;
cursor: pointer;
display: block;
font-size: 16px;
outline: 0;
overflow: hidden;
padding: 14px 20px 14px;
position: relative;
text-align: left;
text-decoration: none;
width: 100%;
}
</style>

<div id="content">
<slot></slot>
</div>

</template>

<script>

class DropdownMenuOverlay extends OverlayMixin(Polymer.Element) {

static get is() {return 'dropdown-menu-overlay';}

static get properties() {
return {

noCancelOnOutsideClick: {
type: Boolean,
value: false
},

noCancelOnButtonClick: {
type: Boolean,
value: false
},

smallScreen: {
type: Boolean,
reflectToAttribute: true,
value: false
},

/**
* The element that should be used to position the element. If not set, it will
* be centered on the screen.
* @type {!Element}
*/
positionTarget: Object,

// The element to fit `this` into.
fitTarget: {
type: Object,
value: window
},

_dropdownContainer: Object

};
}

constructor() {
super();
this._mqListener = this._mqListener.bind(this);
}

connectedCallback() {
super.connectedCallback();

requestAnimationFrame(_ => {
this.addEventListener('click', this._onClick.bind(this));
this._toggleMediaQueryListeners(true);
this.smallScreen = this._mq.matches;
});
}

disconnectedCallback() {
super.disconnectedCallback();
this._toggleMediaQueryListeners(false);
}

__openedChanged(opened, old) {
super.__openedChanged(opened, old);

if (opened) {
this._overlayOpened();
} else if (!old) {
this._overlayClosed();
}
}

_toggleMediaQueryListeners(enable) {
if (enable) {
this._mq = window.matchMedia('(max-width: 640px)');
this._mq.addListener(this._mqListener);
} else if (this._mq) {
this._mq.removeListener(this._mqListener);
}
}

_mqListener(event) {
this.smallScreen = event.matches;
}

_onClick(event) {
event.preventDefault();
event.stopPropagation();
const localName = event.target.localName;

if (!this.noCancelOnOutsideClick &&
(localName === 'dropdown-menu-overlay' || localName === 'button' || localName === 'a')) {
this.opened = false;
}
}

_overlayOpened() {
if (this.smallScreen || !this.positionTarget) {
this.style.background = 'rgba(0,0,0, 0.52)';

this._animation = this.animate([
{opacity: 0},
{opacity: 1}
], {
duration: 300,
easing: 'cubic-bezier(0.165, 0.84, 0.44, 1)'
});
} else {
this._positionize();

this._animation = this.$.content.animate([
{transform: 'scale(0.3, 0) translate(20px, -10px)'},
{transform: 'scale(1, 1) translate(0, 0)'}
], {
duration: 240,
easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
});
}

this._animation.onfinish = _ => {
this.dispatchEvent(new CustomEvent('finish-open-dropdown-animation'));
this._animation = null;
};
}

_overlayClosed() {
this.style.display = '';

this._animation = this.animate([
{opacity: 1},
{opacity: 0}
], {
duration: 240,
easing: 'cubic-bezier(0.645, 0.045, 0.355, 1)'
});

this._animation.onfinish = _ => {
this.remove();

Object.assign(this.$.content.style, {
top: '',
bottom: '',
left: '',
right: '',
transformOrigin: ''
});

this.style.background = '';
};
}

/**
* Position the content div to the positionTarget.
* top/right align have priority.
*/
_positionize() {
const rect = this.$.content.getBoundingClientRect();
const fitRect = this._getNormalizedRect(this.fitTarget);
const positionRect = this.positionTarget.getBoundingClientRect();

let top, bottom, right, left, transformOrigin;

if (fitRect.height - positionRect.bottom >= rect.height) {
top = positionRect.top + 'px';
transformOrigin = 'top';
} else {
bottom = 0 + 'px';
transformOrigin = 'bottom';
}

if (positionRect.right >= rect.width) {
right = fitRect.width - positionRect.right + 'px';
transformOrigin += ' right';
} else {
left = positionRect.left + 'px';
transformOrigin += ' left';
}

Object.assign(this.$.content.style,
{top, bottom, right, left, transformOrigin});
}

_getNormalizedRect(target) {
if (target === document.documentElement || target === window) {
return {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight,
right: window.innerWidth,
bottom: window.innerHeight
};
}
return target.getBoundingClientRect();
}

}

customElements.define(DropdownMenuOverlay.is, DropdownMenuOverlay);

</script>
</dom-module>
Loading

0 comments on commit 3e7c6c2

Please sign in to comment.