diff options
Diffstat (limited to 'src/client/views/ContextMenu.tsx')
-rw-r--r-- | src/client/views/ContextMenu.tsx | 134 |
1 files changed, 38 insertions, 96 deletions
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index f5654446d..fca6a7203 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -5,20 +5,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { DivHeight, DivWidth, returnFalse, setupMoveUpEvents } from '../../ClientUtils'; +import { DivHeight, DivWidth } from '../../ClientUtils'; import { SnappingManager } from '../util/SnappingManager'; import './ContextMenu.scss'; -import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem'; +import { ContextMenuItem, ContextMenuProps } from './ContextMenuItem'; import { ObservableReactComponent } from './ObservableReactComponent'; -import { ColorResult, SketchPicker } from 'react-color'; -import { DocumentView } from './nodes/DocumentView'; -import { Doc } from '../../fields/Doc'; -import { undoable } from '../util/UndoManager'; -import { NumCast, StrCast } from '../../fields/Types'; -import { emptyFunction } from '../../Utils'; @observer -export class ContextMenu extends ObservableReactComponent<{}> { +export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean }> { // eslint-disable-next-line no-use-before-define static Instance: ContextMenu; @@ -45,11 +39,7 @@ export class ContextMenu extends ObservableReactComponent<{}> { @observable _mouseY: number = -1; @observable _shouldDisplay: boolean = false; - @observable _displayBorderMenu: boolean = false; - @observable _selectorWidth: number = 0; - @observable _widthMinMax: {min: string, max: string} = {min: '1', max: '100'}; - - constructor(props: any) { + constructor(props: object) { super(props); makeObservable(this); ContextMenu.Instance = this; @@ -126,10 +116,6 @@ export class ContextMenu extends ObservableReactComponent<{}> { this._defaultItem = item; } - @action setColorPickerDisplay = (display: boolean) => { - this._displayBorderMenu = display; - } - static readonly buffer = 20; get pageX() { return this._pageX + this._width > window.innerWidth - ContextMenu.buffer ? window.innerWidth - ContextMenu.buffer - this._width : Math.max(0, this._pageX); @@ -162,24 +148,24 @@ export class ContextMenu extends ObservableReactComponent<{}> { return wasOpen; }; - @computed get filteredItems(): (OriginalMenuProps | string[])[] { + @computed get filteredItems(): (ContextMenuProps | string[])[] { const searchString = this._searchString.toLowerCase().split(' '); const matches = (descriptions: string[]) => searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s))); - const flattenItems = (items: ContextMenuProps[], groupFunc: (groupName: any) => string[]) => { - let eles: (OriginalMenuProps | string[])[] = []; + const flattenItems = (items: ContextMenuProps[], groupFunc: (groupName: string) => string[]) => { + let eles: (ContextMenuProps | string[])[] = []; - const leaves: OriginalMenuProps[] = []; + const leaves: ContextMenuProps[] = []; items.forEach(item => { const { description } = item; const path = groupFunc(description); - if ('subitems' in item) { + if (item.subitems) { const children = flattenItems(item.subitems, name => [...groupFunc(description), name]); if (children.length || matches(path)) { eles.push(path); eles = eles.concat(children); } } else if (matches(path)) { - leaves.push(item); + leaves.push(item as ContextMenuProps); } }); @@ -190,13 +176,13 @@ export class ContextMenu extends ObservableReactComponent<{}> { return flattenItems(this._items.slice(), name => [name]); } - @computed get flatItems(): OriginalMenuProps[] { - return this.filteredItems.filter(item => !Array.isArray(item)) as OriginalMenuProps[]; + @computed get flatItems(): ContextMenuProps[] { + return this.filteredItems.filter(item => !Array.isArray(item)) as ContextMenuProps[]; } @computed get menuItems() { if (!this._searchString) { - return this._items.map((item, ind) => <ContextMenuItem key={item.description + ind} {...item} noexpand={this.itemsNeedSearch ? true : (item as any).noexpand} closeMenu={this.closeMenu} />); + return this._items.map((item, ind) => <ContextMenuItem key={item.description + ind} {...item} noexpand={this.itemsNeedSearch ? true : item.noexpand} closeMenu={this.closeMenu} />); } return this.filteredItems.map((value, index) => Array.isArray(value) ? ( @@ -215,86 +201,41 @@ export class ContextMenu extends ObservableReactComponent<{}> { } @computed get itemsNeedSearch() { - return this._showSearch ? 1 : this._items.reduce((p, mi) => p + ((mi as any).noexpand ? 1 : (mi as any).subitems?.length || 1), 0) > 15; + return this._showSearch ? 1 : this._items.reduce((p, mi) => p + (mi.noexpand ? 1 : mi.subitems?.length || 1), 0) > 15; } _searchRef = React.createRef<HTMLInputElement>(); // bcz: we shouldn't need this, since we set autoFocus on the <input> tag, but for some reason we do... - get colorPicker() { - const doc = DocumentView.Selected().lastElement().Document; - - return ( - <div className='contextMenu-borderMenu' style={{position: 'absolute', left: this.pageX, - ...(this._yRelativeToTop ? { top: Math.max(0, this.pageY) } : { bottom: this.pageY }),}}> - <div className='top-bar'> - <button className='close-menu' onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, - undoable(clickEv => { - clickEv.stopPropagation(); - clickEv.preventDefault(); - this.setColorPickerDisplay(false); - }, 'close border menu'))}> - <FontAwesomeIcon icon='minus'/> - </button> - </div> - <SketchPicker - onChange={undoable( - action((col: ColorResult) => doc.borderColor = col.hex), - 'set stroke color property' - )} - presetColors={[]} - color={StrCast(doc.borderColor)} - /> - <div className='bottom-box'> - <input className='max-min-selector' defaultValue={this._widthMinMax.min} onChange={e => this._widthMinMax.max = e.target.value}/> - <input className='width-selector' type="range" min={this._widthMinMax.min} max={this._widthMinMax.max} value={this._selectorWidth} id="myRange" onChange={e => runInAction(() => {doc.borderWidth = e.target.value; this._selectorWidth = Number(e.target.value)})}/> - <input className='max-min-selector' defaultValue={this._widthMinMax.max} onChange={e => this._widthMinMax.max = e.target.value}/> - </div> - </div> - ); - } - render() { this.itemsNeedSearch && setTimeout(() => this._searchRef.current?.focus()); return ( - <div> - {this._displayBorderMenu ? this.colorPicker : null} - <div - className="contextMenu-cont" - ref={action((r: any) => { + <div + className="contextMenu-cont" + ref={r => + runInAction(() => { if (r) { this._width = DivWidth(r); this._height = DivHeight(r); } this._searchRef.current?.focus(); - })} - style={{ - display: this._display ? '' : 'none', - left: this.pageX, - ...(this._yRelativeToTop ? { top: Math.max(0, this.pageY) } : { bottom: this.pageY }), - background: SnappingManager.userBackgroundColor, - color: SnappingManager.userColor, - }}> - {!this.itemsNeedSearch ? null : ( - <span className="search-icon"> - <span className="icon-background"> - <FontAwesomeIcon icon="search" size="lg" /> - </span> - <input - ref={this._searchRef} - style={{ color: 'black' }} - className="contextMenu-item contextMenu-description search" - type="text" - placeholder="Filter Menu..." - value={this._searchString} - onKeyDown={this.onKeyDown} - onChange={this.onChange} - // eslint-disable-next-line jsx-a11y/no-autofocus - autoFocus - /> + }) + } + style={{ + display: this._display ? '' : 'none', + left: this.pageX, + ...(this._yRelativeToTop ? { top: Math.max(0, this.pageY) } : { bottom: this.pageY }), + background: SnappingManager.userBackgroundColor, + color: SnappingManager.userColor, + }}> + {!this.itemsNeedSearch ? null : ( + <span className="contextMenu-search"> + <span className="contextMenu-searchIcon"> + <FontAwesomeIcon icon="search" size="lg" /> </span> - )} - {this.menuItems} - </div> + <input ref={this._searchRef} style={{ color: 'black' }} className="contextMenu-searchInput" type="text" placeholder="Filter Menu..." value={this._searchString} onKeyDown={this.onKeyDown} onChange={this.onChange} autoFocus /> + </span> + )} + {this.menuItems} </div> ); } @@ -313,12 +254,13 @@ export class ContextMenu extends ObservableReactComponent<{}> { e.preventDefault(); } else if (e.key === 'Enter' || e.key === 'Tab') { const item = this.flatItems[this._selectedIndex]; - if (item) { + if (item.event) { item.event({ x: this.pageX, y: this.pageY }); } else { // if (this._searchString.startsWith(this._defaultPrefix)) { this._defaultItem?.(this._searchString.substring(this._defaultPrefix.length)); } + this.closeMenu(); e.preventDefault(); e.stopPropagation(); } @@ -335,4 +277,4 @@ export class ContextMenu extends ObservableReactComponent<{}> { this._selectedIndex = Math.min(this.flatItems.length - 1, this._selectedIndex); } }; -} +}
\ No newline at end of file |