From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../components/ContextMenu/ContextMenu.jsx | 176 +++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx (limited to 'browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx') diff --git a/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx b/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx new file mode 100644 index 0000000000..5ea6a57f71 --- /dev/null +++ b/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx @@ -0,0 +1,176 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import React from "react"; +import { connect } from "react-redux"; + +export class ContextMenu extends React.PureComponent { + constructor(props) { + super(props); + this.hideContext = this.hideContext.bind(this); + this.onShow = this.onShow.bind(this); + this.onClick = this.onClick.bind(this); + } + + hideContext() { + this.props.onUpdate(false); + } + + onShow() { + if (this.props.onShow) { + this.props.onShow(); + } + } + + componentDidMount() { + this.onShow(); + setTimeout(() => { + global.addEventListener("click", this.hideContext); + }, 0); + } + + componentWillUnmount() { + global.removeEventListener("click", this.hideContext); + } + + onClick(event) { + // Eat all clicks on the context menu so they don't bubble up to window. + // This prevents the context menu from closing when clicking disabled items + // or the separators. + event.stopPropagation(); + } + + render() { + // Disabling focus on the menu span allows the first tab to focus on the first menu item instead of the wrapper. + return ( + // eslint-disable-next-line jsx-a11y/interactive-supports-focus + +
    + {this.props.options.map((option, i) => + option.type === "separator" ? ( +
  • + ) : ( + option.type !== "empty" && ( + + ) + ) + )} +
+
+ ); + } +} + +export class _ContextMenuItem extends React.PureComponent { + constructor(props) { + super(props); + this.onClick = this.onClick.bind(this); + this.onKeyDown = this.onKeyDown.bind(this); + this.onKeyUp = this.onKeyUp.bind(this); + this.focusFirst = this.focusFirst.bind(this); + } + + onClick(event) { + this.props.hideContext(); + this.props.option.onClick(event); + } + + // Focus the first menu item if the menu was accessed via the keyboard. + focusFirst(button) { + if (this.props.keyboardAccess && button) { + button.focus(); + } + } + + // This selects the correct node based on the key pressed + focusSibling(target, key) { + const parent = target.parentNode; + const closestSiblingSelector = + key === "ArrowUp" ? "previousSibling" : "nextSibling"; + if (!parent[closestSiblingSelector]) { + return; + } + if (parent[closestSiblingSelector].firstElementChild) { + parent[closestSiblingSelector].firstElementChild.focus(); + } else { + parent[closestSiblingSelector][ + closestSiblingSelector + ].firstElementChild.focus(); + } + } + + onKeyDown(event) { + const { option } = this.props; + switch (event.key) { + case "Tab": + // tab goes down in context menu, shift + tab goes up in context menu + // if we're on the last item, one more tab will close the context menu + // similarly, if we're on the first item, one more shift + tab will close it + if ( + (event.shiftKey && option.first) || + (!event.shiftKey && option.last) + ) { + this.props.hideContext(); + } + break; + case "ArrowUp": + case "ArrowDown": + event.preventDefault(); + this.focusSibling(event.target, event.key); + break; + case "Enter": + case " ": + event.preventDefault(); + this.props.hideContext(); + option.onClick(); + break; + case "Escape": + this.props.hideContext(); + break; + } + } + + // Prevents the default behavior of spacebar + // scrolling the page & auto-triggering buttons. + onKeyUp(event) { + if (event.key === " ") { + event.preventDefault(); + } + } + + render() { + const { option } = this.props; + return ( +
  • + +
  • + ); + } +} + +export const ContextMenuItem = connect(state => ({ + Prefs: state.Prefs, +}))(_ContextMenuItem); -- cgit v1.2.3