livebook/assets/js/menu/index.js

88 lines
2.5 KiB
JavaScript
Raw Normal View History

import { getAttributeOrDefault, parseBoolean } from "../lib/attribute";
/**
* A hook controlling a toggleable menu.
*
* Configuration:
*
* * `data-primary` - a boolean indicating whether to open on the primary
* click or the secondary click (like right mouse button click). Defaults to `true`.
*
* The element should have two children:
*
* * one annotated with `data-toggle` being a clickable element
*
* * one annotated with `data-content` with menu content
*/
const Menu = {
mounted() {
this.props = getProps(this);
const toggleElement = this.el.querySelector("[data-toggle]");
if (!toggleElement) {
throw new Error("Menu must have a child with data-toggle attribute");
}
const contentElement = this.el.querySelector("[data-content]");
if (!contentElement) {
throw new Error("Menu must have a child with data-content attribute");
}
if (this.props.primary) {
toggleElement.addEventListener("click", (event) => {
if (this.el.hasAttribute("data-js-open")) {
this.el.removeAttribute("data-js-open");
} else {
this.el.setAttribute("data-js-open", "true");
// Postpone callback registration until the current click finishes bubbling.
setTimeout(() => {
document.addEventListener(
"click",
(event) => {
this.el.removeAttribute("data-js-open");
},
{ once: true }
);
}, 0);
}
});
} else {
toggleElement.addEventListener("contextmenu", (event) => {
event.preventDefault();
if (this.el.hasAttribute("data-js-open")) {
this.el.removeAttribute("data-js-open");
} else {
this.el.setAttribute("data-js-open", "true");
// Postpone callback registration until the current click finishes bubbling.
setTimeout(() => {
const handler = (event) => {
this.el.removeAttribute("data-js-open");
document.removeEventListener("click", handler);
document.removeEventListener("contextmenu", handler);
};
document.addEventListener("click", handler);
document.addEventListener("contextmenu", handler);
}, 0);
}
});
}
},
};
function getProps(hook) {
return {
primary: getAttributeOrDefault(
hook.el,
"data-primary",
"true",
parseBoolean
),
};
}
export default Menu;