mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-21 00:59:29 +00:00
Supprt right-click context menu
This commit is contained in:
parent
56717b84b3
commit
ca4fef2db0
8 changed files with 157 additions and 81 deletions
|
|
@ -1,6 +1,8 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import ContextMenuWrapper from "./ContextMenuWrapper";
|
||||
import MainContextMenu from "./MainWindow/MainContextMenu";
|
||||
import WindowManager from "./WindowManager";
|
||||
import MainWindow from "./MainWindow";
|
||||
import PlaylistWindow from "./PlaylistWindow";
|
||||
|
|
@ -51,7 +53,9 @@ const App = ({
|
|||
return (
|
||||
<div role="application" id="webamp">
|
||||
<Skin />
|
||||
<WindowManager windows={windows} container={container} />
|
||||
<ContextMenuWrapper Contents={MainContextMenu}>
|
||||
<WindowManager windows={windows} container={container} />
|
||||
</ContextMenuWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ Node.propTypes = {
|
|||
label: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
class ContextMenu extends React.Component {
|
||||
export class ContextMenu extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
children,
|
||||
|
|
@ -87,71 +87,3 @@ class ContextMenu extends React.Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ContextMenuTarget extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { selected: false };
|
||||
this._handleHandleClick = this._handleHandleClick.bind(this);
|
||||
this._handleGlobalClick = this._handleGlobalClick.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("click", this._handleGlobalClick);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener("click", this._handleGlobalClick);
|
||||
}
|
||||
|
||||
_handleHandleClick() {
|
||||
this.setState({ selected: !this.state.selected });
|
||||
}
|
||||
|
||||
_handleGlobalClick(e) {
|
||||
if (
|
||||
this.state.selected &&
|
||||
// Not sure how, but it's possible for this to get called when handleNode is null/undefined.
|
||||
// https://sentry.io/share/issue/2066cd79f21e4f279791319f4d2ea35d/
|
||||
this.handleNode &&
|
||||
!this.handleNode.contains(e.target)
|
||||
) {
|
||||
this.setState({ selected: false });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { handle, children, top, bottom, ...passThroughProps } = this.props;
|
||||
const rect = this.handleNode
|
||||
? this.handleNode.getBoundingClientRect()
|
||||
: { top: 0, left: 0 };
|
||||
return (
|
||||
<div {...passThroughProps}>
|
||||
<div
|
||||
className="handle"
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
ref={node => (this.handleNode = node)}
|
||||
onClick={this._handleHandleClick}
|
||||
>
|
||||
{handle}
|
||||
</div>
|
||||
<ContextMenu
|
||||
selected={this.state.selected}
|
||||
offsetTop={rect.top}
|
||||
offsetLeft={rect.left}
|
||||
top={top}
|
||||
bottom={bottom}
|
||||
>
|
||||
{children}
|
||||
</ContextMenu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ContextMenuTarget.propTypes = {
|
||||
children: PropTypes.any.isRequired,
|
||||
handle: PropTypes.any.isRequired,
|
||||
top: PropTypes.bool,
|
||||
bottom: PropTypes.bool
|
||||
};
|
||||
|
|
|
|||
71
js/components/ContextMenuTarget.js
Normal file
71
js/components/ContextMenuTarget.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { ContextMenu } from "./ContextMenu";
|
||||
|
||||
export default class ContextMenuTarget extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { selected: false };
|
||||
this._handleHandleClick = this._handleHandleClick.bind(this);
|
||||
this._handleGlobalClick = this._handleGlobalClick.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener("click", this._handleGlobalClick);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener("click", this._handleGlobalClick);
|
||||
}
|
||||
|
||||
_handleHandleClick() {
|
||||
this.setState({ selected: !this.state.selected });
|
||||
}
|
||||
|
||||
_handleGlobalClick(e) {
|
||||
if (
|
||||
this.state.selected &&
|
||||
// Not sure how, but it's possible for this to get called when handleNode is null/undefined.
|
||||
// https://sentry.io/share/issue/2066cd79f21e4f279791319f4d2ea35d/
|
||||
this.handleNode &&
|
||||
!this.handleNode.contains(e.target)
|
||||
) {
|
||||
this.setState({ selected: false });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { handle, children, top, bottom, ...passThroughProps } = this.props;
|
||||
const rect = this.handleNode
|
||||
? this.handleNode.getBoundingClientRect()
|
||||
: { top: 0, left: 0 };
|
||||
return (
|
||||
<div {...passThroughProps}>
|
||||
<div
|
||||
className="handle"
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
ref={node => (this.handleNode = node)}
|
||||
onClick={this._handleHandleClick}
|
||||
>
|
||||
{handle}
|
||||
</div>
|
||||
<ContextMenu
|
||||
selected={this.state.selected}
|
||||
offsetTop={rect.top}
|
||||
offsetLeft={rect.left}
|
||||
top={top}
|
||||
bottom={bottom}
|
||||
>
|
||||
{children}
|
||||
</ContextMenu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ContextMenuTarget.propTypes = {
|
||||
children: PropTypes.any.isRequired,
|
||||
handle: PropTypes.any.isRequired,
|
||||
top: PropTypes.bool,
|
||||
bottom: PropTypes.bool
|
||||
};
|
||||
64
js/components/ContextMenuWrapper.js
Normal file
64
js/components/ContextMenuWrapper.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { ContextMenu } from "./ContextMenu";
|
||||
|
||||
export default class ContextMenuWraper extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selected: false,
|
||||
offsetTop: null,
|
||||
offsetLeft: null
|
||||
};
|
||||
this._handleRightClick = this._handleRightClick.bind(this);
|
||||
this._handleGlobalClick = this._handleGlobalClick.bind(this);
|
||||
}
|
||||
|
||||
_handleGlobalClick() {
|
||||
this.setState({
|
||||
selected: false,
|
||||
offsetTop: null,
|
||||
offsetLeft: null
|
||||
});
|
||||
document.removeEventListener("click", this._handleGlobalClick);
|
||||
}
|
||||
|
||||
_handleRightClick(e) {
|
||||
const { pageX, pageY } = e;
|
||||
this.setState({
|
||||
selected: true,
|
||||
offsetTop: pageY,
|
||||
offsetLeft: pageX
|
||||
});
|
||||
// Even if you right click multiple times before closeing,
|
||||
// we should only end up with one global listener.
|
||||
document.addEventListener("click", this._handleGlobalClick);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, Contents, ...passThroughProps } = this.props;
|
||||
return (
|
||||
<div
|
||||
onContextMenu={this._handleRightClick}
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
{...passThroughProps}
|
||||
>
|
||||
<ContextMenu
|
||||
selected={this.state.selected}
|
||||
offsetTop={this.state.offsetTop}
|
||||
offsetLeft={this.state.offsetLeft}
|
||||
>
|
||||
<Contents />
|
||||
</ContextMenu>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ContextMenuWraper.propTypes = {
|
||||
children: PropTypes.any.isRequired,
|
||||
Contents: PropTypes.any.isRequired
|
||||
};
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { openEqfFileDialog, downloadPreset } from "../../actionCreators";
|
||||
import { ContextMenuTarget, Node } from "../ContextMenu";
|
||||
import { Node } from "../ContextMenu";
|
||||
import ContextMenuTarget from "../ContextMenuTarget";
|
||||
|
||||
const PresetsContextMenu = props => (
|
||||
<ContextMenuTarget top id="presets-context" handle={<div id="presets" />}>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import ClickedDiv from "../ClickedDiv";
|
||||
import {
|
||||
close,
|
||||
setSkinFromUrl,
|
||||
|
|
@ -16,14 +15,10 @@ import {
|
|||
} from "../../actionTypes";
|
||||
import { getGenWindows } from "../../selectors";
|
||||
import { LOAD_STYLE } from "../../constants";
|
||||
import { ContextMenuTarget, Hr, Node, Parent, LinkNode } from "../ContextMenu";
|
||||
import { Hr, Node, Parent, LinkNode } from "../ContextMenu";
|
||||
|
||||
const MainContextMenu = props => (
|
||||
<ContextMenuTarget
|
||||
id="option-context"
|
||||
bottom
|
||||
handle={<ClickedDiv id="option" title="Winamp Menu" />}
|
||||
>
|
||||
<React.Fragment>
|
||||
<LinkNode
|
||||
href="https://github.com/captbaritone/webamp"
|
||||
target="_blank"
|
||||
|
|
@ -90,7 +85,7 @@ const MainContextMenu = props => (
|
|||
</Parent>
|
||||
<Hr />
|
||||
<Node onClick={props.close} label="Exit" />
|
||||
</ContextMenuTarget>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import DropTarget from "../DropTarget";
|
|||
import MiniTime from "../MiniTime";
|
||||
|
||||
import { SET_FOCUSED_WINDOW } from "../../actionTypes";
|
||||
import ClickedDiv from "../ClickedDiv";
|
||||
import ContextMenuTarget from "../ContextMenuTarget";
|
||||
import ActionButtons from "./ActionButtons";
|
||||
import MainBalance from "./MainBalance";
|
||||
import Close from "./Close";
|
||||
|
|
@ -90,7 +92,13 @@ export class MainWindow extends React.Component {
|
|||
className="selected title-bard draggable"
|
||||
onDoubleClick={this.props.toggleMainWindowShadeMode}
|
||||
>
|
||||
<MainContextMenu filePickers={filePickers} />
|
||||
<ContextMenuTarget
|
||||
id="option-context"
|
||||
bottom
|
||||
handle={<ClickedDiv id="option" title="Winamp Menu" />}
|
||||
>
|
||||
<MainContextMenu filePickers={filePickers} />
|
||||
</ContextMenuTarget>
|
||||
{mainShade && <MiniTime />}
|
||||
<Minimize />
|
||||
<Shade />
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import {
|
|||
downloadHtmlPlaylist
|
||||
} from "../../actionCreators";
|
||||
|
||||
import { ContextMenuTarget, Hr, Node } from "../ContextMenu";
|
||||
import { Hr, Node } from "../ContextMenu";
|
||||
import ContextMenuTarget from "../ContextMenuTarget";
|
||||
import PlaylistMenu from "./PlaylistMenu";
|
||||
|
||||
/* eslint-disable no-alert */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue