summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/components/dropdown_menu_item.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/web/static/src/js/components/dropdown_menu_item.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/web/static/src/js/components/dropdown_menu_item.js')
-rw-r--r--addons/web/static/src/js/components/dropdown_menu_item.js102
1 files changed, 102 insertions, 0 deletions
diff --git a/addons/web/static/src/js/components/dropdown_menu_item.js b/addons/web/static/src/js/components/dropdown_menu_item.js
new file mode 100644
index 00000000..bcc54ab4
--- /dev/null
+++ b/addons/web/static/src/js/components/dropdown_menu_item.js
@@ -0,0 +1,102 @@
+odoo.define('web.DropdownMenuItem', function (require) {
+ "use strict";
+
+ const { useListener } = require('web.custom_hooks');
+
+ const { Component, hooks } = owl;
+ const { useExternalListener, useRef, useState } = hooks;
+
+ /**
+ * Dropdown menu item
+ *
+ * Generic component instantiated by a dropdown menu (@see DropdownMenu) in
+ * the absence of `Component` and `props` keys in a given item object.
+ *
+ * In its simplest form, a dropdown menu item will be given a description (optional,
+ * but highly recommended) and will trigger a 'select-item' when clicked on.
+ * Additionaly it can receive the following props:
+ * - isActive: will add a `checked` symbol on the left side of the item
+ * - removable: will add a `remove` trash icon on the right side of the item.
+ * when clicked, will trigger a 'remove-item' event.
+ * - options: will change the behaviour of the item ; instead of triggering
+ * an event, the item will act as a nested dropdown menu and display
+ * its given options. These will have the same definition as another
+ * dropdown item but cannot have options of their own.
+ *
+ * It is recommended to extend this class when defining a Component which will
+ * be put inside of a dropdown menu (@see CustomFilterItem as example).
+ * @extends Component
+ */
+ class DropdownMenuItem extends Component {
+ constructor() {
+ super(...arguments);
+
+ this.canBeOpened = Boolean(this.props.options && this.props.options.length);
+
+ this.fallbackFocusRef = useRef('fallback-focus');
+ this.state = useState({ open: false });
+
+ useExternalListener(window, 'click', this._onWindowClick);
+ useListener('keydown', this._onKeydown);
+ }
+
+ //---------------------------------------------------------------------
+ // Handlers
+ //---------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {KeyboardEvent} ev
+ */
+ _onKeydown(ev) {
+ if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) {
+ return;
+ }
+ switch (ev.key) {
+ case 'ArrowLeft':
+ if (this.canBeOpened && this.state.open) {
+ ev.preventDefault();
+ if (this.fallbackFocusRef.el) {
+ this.fallbackFocusRef.el.focus();
+ }
+ this.state.open = false;
+ }
+ break;
+ case 'ArrowRight':
+ if (this.canBeOpened && !this.state.open) {
+ ev.preventDefault();
+ this.state.open = true;
+ }
+ break;
+ case 'Escape':
+ ev.target.blur();
+ if (this.canBeOpened && this.state.open) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ if (this.fallbackFocusRef.el) {
+ this.fallbackFocusRef.el.focus();
+ }
+ this.state.open = false;
+ }
+ }
+ }
+
+ /**
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onWindowClick(ev) {
+ if (
+ this.state.open &&
+ !this.el.contains(ev.target) &&
+ !this.el.contains(document.activeElement)
+ ) {
+ this.state.open = false;
+ }
+ }
+ }
+
+ DropdownMenuItem.template = 'web.DropdownMenuItem';
+
+ return DropdownMenuItem;
+});