DRY context menu placement algorithms
This commit is contained in:
parent
1c4d89f2d7
commit
2eddb6ca01
5 changed files with 36 additions and 63 deletions
|
@ -559,6 +559,14 @@ MenuItemRadio.propTypes = {
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Placement method for <ContextMenu /> to position context menu to right of elementRect with chevronOffset
|
||||||
|
export const toRightOf = (elementRect, chevronOffset=12) => {
|
||||||
|
const left = elementRect.right + window.pageXOffset + 3;
|
||||||
|
let top = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
|
||||||
|
top = top - (chevronOffset + 8); // where 8 is half the height of the chevron
|
||||||
|
return {left, top};
|
||||||
|
};
|
||||||
|
|
||||||
export function createMenu(ElementClass, props, hasBackground=true) {
|
export function createMenu(ElementClass, props, hasBackground=true) {
|
||||||
const closeMenu = function(...args) {
|
const closeMenu = function(...args) {
|
||||||
ReactDOM.unmountComponentAtNode(getOrCreateContainer());
|
ReactDOM.unmountComponentAtNode(getOrCreateContainer());
|
||||||
|
|
|
@ -29,7 +29,7 @@ import * as FormattingUtils from '../../../utils/FormattingUtils';
|
||||||
import FlairStore from '../../../stores/FlairStore';
|
import FlairStore from '../../../stores/FlairStore';
|
||||||
import GroupStore from '../../../stores/GroupStore';
|
import GroupStore from '../../../stores/GroupStore';
|
||||||
import TagOrderStore from '../../../stores/TagOrderStore';
|
import TagOrderStore from '../../../stores/TagOrderStore';
|
||||||
import {ContextMenu} from "../../structures/ContextualMenu";
|
import {ContextMenu, toRightOf} from "../../structures/ContextualMenu";
|
||||||
|
|
||||||
// A class for a child of TagPanel (possibly wrapped in a DNDTagTile) that represents
|
// A class for a child of TagPanel (possibly wrapped in a DNDTagTile) that represents
|
||||||
// a thing to click on for the user to filter the visible rooms in the RoomList to:
|
// a thing to click on for the user to filter the visible rooms in the RoomList to:
|
||||||
|
@ -176,16 +176,9 @@ export default createReactClass({
|
||||||
let contextMenu;
|
let contextMenu;
|
||||||
if (this.state.menuDisplayed) {
|
if (this.state.menuDisplayed) {
|
||||||
const elementRect = this._contextMenuButton.current.getBoundingClientRect();
|
const elementRect = this._contextMenuButton.current.getBoundingClientRect();
|
||||||
|
|
||||||
// The window X and Y offsets are to adjust position when zoomed in to page
|
|
||||||
const left = elementRect.right + window.pageXOffset + 3;
|
|
||||||
const chevronOffset = 12;
|
|
||||||
let top = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
|
|
||||||
top = top - (chevronOffset + 8); // where 8 is half the height of the chevron
|
|
||||||
|
|
||||||
const TagTileContextMenu = sdk.getComponent('context_menus.TagTileContextMenu');
|
const TagTileContextMenu = sdk.getComponent('context_menus.TagTileContextMenu');
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<ContextMenu props={{ left, top, chevronOffset }} onFinished={this.closeMenu}>
|
<ContextMenu props={toRightOf(elementRect)} onFinished={this.closeMenu}>
|
||||||
<TagTileContextMenu tag={this.props.tag} onFinished={this.closeMenu} />
|
<TagTileContextMenu tag={this.props.tag} onFinished={this.closeMenu} />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,7 +24,7 @@ import sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher';
|
import dis from '../../../dispatcher';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
import {ContextMenu} from "../../structures/ContextualMenu";
|
import {ContextMenu, toRightOf} from "../../structures/ContextualMenu";
|
||||||
|
|
||||||
export default createReactClass({
|
export default createReactClass({
|
||||||
displayName: 'GroupInviteTile',
|
displayName: 'GroupInviteTile',
|
||||||
|
@ -144,15 +144,9 @@ export default createReactClass({
|
||||||
let contextMenu;
|
let contextMenu;
|
||||||
if (this.state.menuDisplayed) {
|
if (this.state.menuDisplayed) {
|
||||||
const elementRect = this._contextMenuButton.current.getBoundingClientRect();
|
const elementRect = this._contextMenuButton.current.getBoundingClientRect();
|
||||||
// The window X and Y offsets are to adjust position when zoomed in to page
|
|
||||||
const left = elementRect.right + window.pageXOffset + 3;
|
|
||||||
const chevronOffset = 12;
|
|
||||||
let top = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
|
|
||||||
top = top - (chevronOffset + 8); // where 8 is half the height of the chevron
|
|
||||||
|
|
||||||
const GroupInviteTileContextMenu = sdk.getComponent('context_menus.GroupInviteTileContextMenu');
|
const GroupInviteTileContextMenu = sdk.getComponent('context_menus.GroupInviteTileContextMenu');
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<ContextMenu props={{ left, top, chevronOffset }} onFinished={this.closeMenu}>
|
<ContextMenu props={toRightOf(elementRect)} onFinished={this.closeMenu}>
|
||||||
<GroupInviteTileContextMenu group={this.props.group} onFinished={this.closeMenu} />
|
<GroupInviteTileContextMenu group={this.props.group} onFinished={this.closeMenu} />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
);
|
);
|
||||||
|
|
|
@ -27,6 +27,26 @@ import {ContextMenu} from '../../structures/ContextualMenu';
|
||||||
import { isContentActionable, canEditContent } from '../../../utils/EventUtils';
|
import { isContentActionable, canEditContent } from '../../../utils/EventUtils';
|
||||||
import {RoomContext} from "../../structures/RoomView";
|
import {RoomContext} from "../../structures/RoomView";
|
||||||
|
|
||||||
|
const contextMenuProps = (elementRect) => {
|
||||||
|
const menuOptions = {
|
||||||
|
chevronFace: "none",
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttonRight = elementRect.right + window.pageXOffset;
|
||||||
|
const buttonBottom = elementRect.bottom + window.pageYOffset;
|
||||||
|
const buttonTop = elementRect.top + window.pageYOffset;
|
||||||
|
// Align the right edge of the menu to the right edge of the button
|
||||||
|
menuOptions.right = window.innerWidth - buttonRight;
|
||||||
|
// Align the menu vertically on whichever side of the button has more space available.
|
||||||
|
if (buttonBottom < window.innerHeight / 2) {
|
||||||
|
menuOptions.top = buttonBottom;
|
||||||
|
} else {
|
||||||
|
menuOptions.bottom = window.innerHeight - buttonTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
return menuOptions;
|
||||||
|
};
|
||||||
|
|
||||||
const useContextMenu = () => {
|
const useContextMenu = () => {
|
||||||
const _button = useRef(null);
|
const _button = useRef(null);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
@ -65,26 +85,8 @@ const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFo
|
||||||
e2eInfoCallback = onCryptoClick;
|
e2eInfoCallback = onCryptoClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
const menuOptions = {
|
|
||||||
chevronFace: "none",
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttonRect = _button.current.getBoundingClientRect();
|
const buttonRect = _button.current.getBoundingClientRect();
|
||||||
// The window X and Y offsets are to adjust position when zoomed in to page
|
contextMenu = <ContextMenu props={contextMenuProps(buttonRect)} onFinished={closeMenu}>
|
||||||
const buttonRight = buttonRect.right + window.pageXOffset;
|
|
||||||
const buttonBottom = buttonRect.bottom + window.pageYOffset;
|
|
||||||
const buttonTop = buttonRect.top + window.pageYOffset;
|
|
||||||
// Align the right edge of the menu to the right edge of the button
|
|
||||||
menuOptions.right = window.innerWidth - buttonRight;
|
|
||||||
// Align the menu vertically on whichever side of the button has more
|
|
||||||
// space available.
|
|
||||||
if (buttonBottom < window.innerHeight / 2) {
|
|
||||||
menuOptions.top = buttonBottom;
|
|
||||||
} else {
|
|
||||||
menuOptions.bottom = window.innerHeight - buttonTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
contextMenu = <ContextMenu props={menuOptions} onFinished={closeMenu}>
|
|
||||||
<MessageContextMenu
|
<MessageContextMenu
|
||||||
mxEvent={mxEvent}
|
mxEvent={mxEvent}
|
||||||
permalinkCreator={permalinkCreator}
|
permalinkCreator={permalinkCreator}
|
||||||
|
@ -116,27 +118,9 @@ const ReactButton = ({mxEvent, reactions}) => {
|
||||||
|
|
||||||
let contextMenu;
|
let contextMenu;
|
||||||
if (menuDisplayed) {
|
if (menuDisplayed) {
|
||||||
const menuOptions = {
|
|
||||||
chevronFace: "none",
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttonRect = _button.current.getBoundingClientRect();
|
const buttonRect = _button.current.getBoundingClientRect();
|
||||||
// The window X and Y offsets are to adjust position when zoomed in to page
|
|
||||||
const buttonRight = buttonRect.right + window.pageXOffset;
|
|
||||||
const buttonBottom = buttonRect.bottom + window.pageYOffset;
|
|
||||||
const buttonTop = buttonRect.top + window.pageYOffset;
|
|
||||||
// Align the right edge of the menu to the right edge of the button
|
|
||||||
menuOptions.right = window.innerWidth - buttonRight;
|
|
||||||
// Align the menu vertically on whichever side of the button has more
|
|
||||||
// space available.
|
|
||||||
if (buttonBottom < window.innerHeight / 2) {
|
|
||||||
menuOptions.top = buttonBottom;
|
|
||||||
} else {
|
|
||||||
menuOptions.bottom = window.innerHeight - buttonTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ReactionPicker = sdk.getComponent('emojipicker.ReactionPicker');
|
const ReactionPicker = sdk.getComponent('emojipicker.ReactionPicker');
|
||||||
contextMenu = <ContextMenu props={menuOptions} onFinished={closeMenu}>
|
contextMenu = <ContextMenu props={contextMenuProps(buttonRect)} onFinished={closeMenu}>
|
||||||
<ReactionPicker mxEvent={mxEvent} reactions={reactions} onFinished={closeMenu} />
|
<ReactionPicker mxEvent={mxEvent} reactions={reactions} onFinished={closeMenu} />
|
||||||
</ContextMenu>;
|
</ContextMenu>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import dis from '../../../dispatcher';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import DMRoomMap from '../../../utils/DMRoomMap';
|
import DMRoomMap from '../../../utils/DMRoomMap';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import {ContextMenu} from '../../structures/ContextualMenu';
|
import {ContextMenu, toRightOf} from '../../structures/ContextualMenu';
|
||||||
import * as RoomNotifs from '../../../RoomNotifs';
|
import * as RoomNotifs from '../../../RoomNotifs';
|
||||||
import * as FormattingUtils from '../../../utils/FormattingUtils';
|
import * as FormattingUtils from '../../../utils/FormattingUtils';
|
||||||
import ActiveRoomObserver from '../../../ActiveRoomObserver';
|
import ActiveRoomObserver from '../../../ActiveRoomObserver';
|
||||||
|
@ -379,15 +379,9 @@ module.exports = createReactClass({
|
||||||
let contextMenu;
|
let contextMenu;
|
||||||
if (this.state.menuDisplayed) {
|
if (this.state.menuDisplayed) {
|
||||||
const elementRect = this._contextMenuButton.current.getBoundingClientRect();
|
const elementRect = this._contextMenuButton.current.getBoundingClientRect();
|
||||||
// The window X and Y offsets are to adjust position when zoomed in to page
|
|
||||||
const left = elementRect.right + window.pageXOffset + 3;
|
|
||||||
const chevronOffset = 12;
|
|
||||||
let top = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
|
|
||||||
top = top - (chevronOffset + 8); // where 8 is half the height of the chevron
|
|
||||||
|
|
||||||
const RoomTileContextMenu = sdk.getComponent('context_menus.RoomTileContextMenu');
|
const RoomTileContextMenu = sdk.getComponent('context_menus.RoomTileContextMenu');
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<ContextMenu props={{ left, top, chevronOffset }} onFinished={this.closeMenu}>
|
<ContextMenu props={toRightOf(elementRect)} onFinished={this.closeMenu}>
|
||||||
<RoomTileContextMenu room={this.props.room} onFinished={this.closeMenu} />
|
<RoomTileContextMenu room={this.props.room} onFinished={this.closeMenu} />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue