diff options
Diffstat (limited to 'src/client/views/ContextMenu.tsx')
-rw-r--r-- | src/client/views/ContextMenu.tsx | 101 |
1 files changed, 48 insertions, 53 deletions
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index adefc7e9c..8dcdd80e5 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,40 +1,43 @@ -import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, observable } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import './ContextMenu.scss'; -import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem'; -import { Utils } from '../../Utils'; +import * as React from 'react'; import { StrCast } from '../../fields/Types'; -import { Doc } from '../../fields/Doc'; import { SettingsManager } from '../util/SettingsManager'; +import './ContextMenu.scss'; +import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem'; +import { ObservableReactComponent } from './ObservableReactComponent'; @observer -export class ContextMenu extends React.Component { +export class ContextMenu extends ObservableReactComponent<{}> { static Instance: ContextMenu; - @observable private _items: Array<ContextMenuProps> = []; - @observable private _pageX: number = 0; - @observable private _pageY: number = 0; - @observable private _display: boolean = false; - @observable private _searchString: string = ''; - @observable private _showSearch: boolean = false; + private _ignoreUp = false; + private _reactionDisposer?: IReactionDisposer; + private _defaultPrefix: string = ''; + private _defaultItem: ((name: string) => void) | undefined; + private _onDisplay?: () => void = undefined; + + @observable.shallow _items: ContextMenuProps[] = []; + @observable _pageX: number = 0; + @observable _pageY: number = 0; + @observable _display: boolean = false; + @observable _searchString: string = ''; + @observable _showSearch: boolean = false; // afaik displaymenu can be called before all the items are added to the menu, so can't determine in displayMenu what the height of the menu will be - @observable private _yRelativeToTop: boolean = true; - @observable selectedIndex = -1; + @observable _yRelativeToTop: boolean = true; + @observable _selectedIndex = -1; - @observable private _width: number = 0; - @observable private _height: number = 0; + @observable _width: number = 0; + @observable _height: number = 0; - @observable private _mouseX: number = -1; - @observable private _mouseY: number = -1; - @observable private _shouldDisplay: boolean = false; - private _ignoreUp = false; - private _reactionDisposer?: IReactionDisposer; + @observable _mouseX: number = -1; + @observable _mouseY: number = -1; + @observable _shouldDisplay: boolean = false; - constructor(props: Readonly<{}>) { + constructor(props: any) { super(props); - + makeObservable(this); ContextMenu.Instance = this; } @@ -74,36 +77,27 @@ export class ContextMenu extends React.Component { this._reactionDisposer?.(); } - @action - componentDidMount = () => { + componentDidMount() { document.addEventListener('pointerdown', this.onPointerDown, true); document.addEventListener('pointerup', this.onPointerUp); - }; + } @action clearItems() { - this._items = []; + this._items.length = 0; this._defaultPrefix = ''; this._defaultItem = undefined; } - _defaultPrefix: string = ''; - _defaultItem: ((name: string) => void) | undefined; - - findByDescription = (target: string, toLowerCase = false) => { - return this._items.find(menuItem => { - let reference = menuItem.description; - toLowerCase && (reference = reference.toLowerCase()); - return reference === target; - }); - }; + findByDescription = (target: string, toLowerCase = false) => + this._items.find(menuItem => + (toLowerCase ? menuItem.description.toLowerCase() : menuItem.description) === target); // prettier-ignore @action addItem(item: ContextMenuProps) { - if (this._items.indexOf(item) === -1) { - this._items.push(item); - } + !this._items.includes(item) && this._items.push(item); } + @action moveAfter(item: ContextMenuProps, after?: ContextMenuProps) { const curInd = this._items.findIndex(i => i.description === item.description); @@ -111,6 +105,7 @@ export class ContextMenu extends React.Component { const afterInd = after && this.findByDescription(after.description) ? this._items.findIndex(i => i.description === after.description) : this._items.length; this._items.splice(afterInd, 0, item); } + @action setDefaultItem(prefix: string, item: (name: string) => void) { this._defaultPrefix = prefix; @@ -126,7 +121,6 @@ export class ContextMenu extends React.Component { return this._pageY + this._height > window.innerHeight - ContextMenu.buffer ? window.innerHeight - ContextMenu.buffer - this._height : Math.max(0, this._pageY); } - _onDisplay?: () => void = undefined; @action displayMenu = (x: number, y: number, initSearch = '', showSearch = false, onDisplay?: () => void) => { //maxX and maxY will change if the UI/font size changes, but will work for any amount @@ -201,7 +195,7 @@ export class ContextMenu extends React.Component { <div className="contextMenu-description">{value.join(' -> ')}</div> </div> ) : ( - <ContextMenuItem {...value} key={index + value.description} closeMenu={this.closeMenu} selected={index === this.selectedIndex} /> + <ContextMenuItem {...value} key={index + value.description} closeMenu={this.closeMenu} selected={index === this._selectedIndex} /> ) ); } @@ -211,7 +205,7 @@ export class ContextMenu extends React.Component { } render() { - return !this._display ? null : ( + return ( <div className="contextMenu-cont" ref={action((r: any) => { @@ -221,6 +215,7 @@ export class ContextMenu extends React.Component { } })} style={{ + display: this._display ? '' : 'none', left: this.pageX, ...(this._yRelativeToTop ? { top: this.pageY } : { bottom: this.pageY }), background: SettingsManager.userBackgroundColor, @@ -242,17 +237,17 @@ export class ContextMenu extends React.Component { @action onKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'ArrowDown') { - if (this.selectedIndex < this.flatItems.length - 1) { - this.selectedIndex++; + if (this._selectedIndex < this.flatItems.length - 1) { + this._selectedIndex++; } e.preventDefault(); } else if (e.key === 'ArrowUp') { - if (this.selectedIndex > 0) { - this.selectedIndex--; + if (this._selectedIndex > 0) { + this._selectedIndex--; } e.preventDefault(); } else if (e.key === 'Enter' || e.key === 'Tab') { - const item = this.flatItems[this.selectedIndex]; + const item = this.flatItems[this._selectedIndex]; if (item) { item.event({ x: this.pageX, y: this.pageY }); } else if (this._searchString.startsWith(this._defaultPrefix)) { @@ -268,12 +263,12 @@ export class ContextMenu extends React.Component { onChange = (e: React.ChangeEvent<HTMLInputElement>) => { this._searchString = e.target.value; if (!this._searchString) { - this.selectedIndex = -1; + this._selectedIndex = -1; } else { - if (this.selectedIndex === -1) { - this.selectedIndex = 0; + if (this._selectedIndex === -1) { + this._selectedIndex = 0; } else { - this.selectedIndex = Math.min(this.flatItems.length - 1, this.selectedIndex); + this._selectedIndex = Math.min(this.flatItems.length - 1, this._selectedIndex); } } }; |