Skip to content

Instantly share code, notes, and snippets.

@camskene
Last active June 10, 2020 23:05
Show Gist options
  • Save camskene/e642301ea08c965a874e61fc009974fc to your computer and use it in GitHub Desktop.
Save camskene/e642301ea08c965a874e61fc009974fc to your computer and use it in GitHub Desktop.
menu hover open
import Controller from '@ember/controller';
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
export default class ApplicationController extends Controller {
@tracked isOpen = false;
openedAt;
shouldPreventClick = false;
shouldPreventClose = false;
didOpenOnMouseEnter = false;
@action
click(e) {
if (this.shouldPreventClick) {
return;
}
if (this.isOpen) {
this.closeMenu(e);
} else {
this.openMenu(e);
}
}
@action
mouseenter(e) {
this.openMenu(e);
this.didOpenOnMouseEnter = true;
}
@action
mouseleave(e) {
this.closeMenu(e);
}
@action
mousedown(e) {
this.preventClick(e);
if (this.shouldPreventClick) {
e.preventDefault(); // prevent element receiving focus
}
}
@action
openMenu(e) {
this.openedAt = Date.now();
this.isOpen = true;
}
@action
closeMenu(e) {
if (e.target.closest('#content')) {
return;
}
if (this.shouldPreventClose) {
return;
}
this.isOpen = false;
this.didOpenOnMouseEnter = false;
}
@action
preventClick(e) {
this.shouldPreventClick = false;
let numMsFromOpenedAt = Date.now() - this.openedAt;
let numMsToPreventClick = 500;
if (this.didOpenOnMouseEnter && numMsFromOpenedAt < numMsToPreventClick) {
this.shouldPreventClick = true;
}
}
@action
preventClose() {
this.shouldPreventClose = false;
document.addEventListener("mousemove", (ev) => {
this.distanceX = this.distanceY = this.speedX = this.speedY = 0;
this.speedX += Math.abs(ev.movementX);
this.speedY += Math.abs(ev.movementY);
this.distanceX += ev.movementX;
this.distanceY += ev.movementY;
}, false);
if (this.speedX > 8) {
this.shouldPreventClose = true;
}
console.log(`Speed X: ${this.speedX}px/s`);
console.log(`Speed Y: ${this.speedY}px/s`);
console.log(`Distance X: ${this.distanceX}px/s`);
console.log(`Distance Y: ${this.distanceY}px/s`);
console.log('-----');
}
speedX = 0;
speedY = 0;
distanceX = 0;
distanceY = 0;
@action
didInsertContent() {
document.addEventListener('mouseover', this.closeMenu);
}
@action
willDestroyContent() {
document.removeEventListener('mouseover', this.closeMenu);
document.removeEventListener('mousemove', () => {});
}
}
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
<button
class="bg-gray-600 text-white focus:bg-black"
{{on "mouseenter" this.mouseenter}}
{{on "mouseleave" this.preventClose}}
{{on "mousedown" this.mousedown}}
{{on "click" this.click}}
>
Menu
</button>
{{#if this.isOpen}}
<div
class="bg-gray-400"
id="content"
{{did-insert this.didInsertContent}}
{{will-destroy this.willDestroyContent}}
>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
</div>
{{/if}}
{
"version": "0.17.1",
"EmberENV": {
"FEATURES": {},
"_TEMPLATE_ONLY_GLIMMER_COMPONENTS": false,
"_APPLICATION_TEMPLATE_WRAPPER": true,
"_JQUERY_INTEGRATION": true
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js",
"ember": "3.18.1",
"ember-template-compiler": "3.18.1",
"ember-testing": "3.18.1"
},
"addons": {
"@glimmer/component": "1.0.0",
"@ember/render-modifiers": "1.0.2"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment