From 5c53f958f80ae4058d8eabe37e6c37a4a4d2db02 Mon Sep 17 00:00:00 2001 From: Maxim Kadushkin Date: Wed, 3 Mar 2021 14:47:49 +0300 Subject: [PATCH] [DE mobile] context menu depends on selected objects --- .../mobile/lib/controller/ContextMenu.jsx | 13 +- apps/common/mobile/lib/view/ContextMenu.jsx | 6 +- .../mobile/src/controller/ContextMenu.jsx | 252 +++++++++++++++++- apps/documenteditor/mobile/src/page/main.jsx | 4 +- .../mobile/src/store/appOptions.js | 8 + 5 files changed, 262 insertions(+), 21 deletions(-) diff --git a/apps/common/mobile/lib/controller/ContextMenu.jsx b/apps/common/mobile/lib/controller/ContextMenu.jsx index ef0f50118..f25deba51 100644 --- a/apps/common/mobile/lib/controller/ContextMenu.jsx +++ b/apps/common/mobile/lib/controller/ContextMenu.jsx @@ -12,8 +12,10 @@ class ContextMenuController extends Component { this.state = { opened: false + , items: [] }; + this.onMenuItemClick = this.onMenuItemClick.bind(this); this.onMenuClosed = this.onMenuClosed.bind(this); this.onDocumentReady = this.onDocumentReady.bind(this); this.onApiOpenContextMenu = this.onApiOpenContextMenu.bind(this); @@ -94,6 +96,8 @@ class ContextMenuController extends Component { onApiOpenContextMenu(x, y) { if ( !this.state.opened ) { + this.setState({items: this.initMenuItems()}); + this.$targetEl.css({left: `${x}px`, top: `${y}px`}); const popover = f7.popover.open(idContextMenuElement, idCntextMenuTargetElement); @@ -108,7 +112,8 @@ class ContextMenuController extends Component { onApiHideContextMenu() { if ( this.state.opened ) { - f7.popover.close(idContextMenuElement); + $$(idContextMenuElement).hide(); + f7.popover.close(idContextMenuElement, false); this.$targetEl.css({left: '-10000px', top: '-10000px'}); this.setState({opened: false}); @@ -116,6 +121,9 @@ class ContextMenuController extends Component { } onMenuClosed() { + this.$targetEl.css({left: '-10000px', top: '-10000px'}); + this.setState({opened: false}); + // (async () => { // await 1 && this.setState(state => { // this.$targetEl.css({left: '-10000px', top: '-10000px'}); @@ -125,6 +133,7 @@ class ContextMenuController extends Component { } onMenuItemClick(action) { + this.onApiHideContextMenu(); } componentWillUnmount() { @@ -151,7 +160,7 @@ class ContextMenuController extends Component { render() { return ( - + ) } } diff --git a/apps/common/mobile/lib/view/ContextMenu.jsx b/apps/common/mobile/lib/view/ContextMenu.jsx index 954b15e5b..4bf1c2c61 100644 --- a/apps/common/mobile/lib/view/ContextMenu.jsx +++ b/apps/common/mobile/lib/view/ContextMenu.jsx @@ -26,9 +26,9 @@ class ContextMenuView extends Component { > {buttons.map((b, index) => - !!b.text ? - this.props.onMenuItemClick(b.action)} /> : - + !b.icon ? + this.props.onMenuItemClick(b.event)} /> : + )} diff --git a/apps/documenteditor/mobile/src/controller/ContextMenu.jsx b/apps/documenteditor/mobile/src/controller/ContextMenu.jsx index 23d5d362d..5a09e6f84 100644 --- a/apps/documenteditor/mobile/src/controller/ContextMenu.jsx +++ b/apps/documenteditor/mobile/src/controller/ContextMenu.jsx @@ -1,13 +1,43 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { f7 } from 'framework7-react'; +import { inject, observer } from "mobx-react"; import ContextMenuController from '../../../../common/mobile/lib/controller/ContextMenu'; import { idContextMenuElement } from '../../../../common/mobile/lib/view/ContextMenu'; +import { Device } from '../../../../common/mobile/utils/device'; +@inject ( stores => ({ + isEdit: stores.storeAppOptions.isEdit, + canViewComments: stores.storeAppOptions.canViewComments, + canReview: stores.storeAppOptions.canReview +})) class ContextMenu extends ContextMenuController { constructor(props) { super(props); // console.log('context menu controller created'); + this.onApiShowComment = this.onApiShowComment.bind(this); + this.onApiHideComment = this.onApiHideComment.bind(this); + } + + static closeContextMenu() { + f7.popover.close(idContextMenuElement, false); + } + + componentWillUnmount() { + super.componentWillUnmount(); + + const api = Common.EditorApi.get(); + api.asc_unregisterCallback('asc_onShowComment', this.onApiShowComment); + api.asc_unregisterCallback('asc_onHideComment', this.onApiHideComment); + } + + + onApiShowComment(comments) { + this.isComments = comments && comments.length > 0; + } + + onApiHideComment() { + this.isComments = false; } // onMenuClosed() { @@ -17,23 +47,217 @@ class ContextMenu extends ContextMenuController { onMenuItemClick(action) { super.onMenuItemClick(action); - console.log("on click item"); + console.log("click context menu item: " + action); + } + + onDocumentReady() { + super.onDocumentReady(); + + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onShowComment', this.onApiShowComment); + api.asc_registerCallback('asc_onHideComment', this.onApiHideComment); } initMenuItems() { - return [ - { - text: 'Edit', - action: 'edit' - }, { - text: 'View', - action: 'view' - }, { - icon: 'icon-paste', - action: 'review' + if ( !Common.EditorApi ) return []; + + const { isEdit, canViewComments, canReview } = this.props; + + const api = Common.EditorApi.get(); + const stack = api.getSelectedElements(); + const canCopy = api.can_CopyCut(); + + let itemsIcon = [], + itemsText = []; + + if ( canCopy ) { + itemsIcon.push({ + caption: /*me.menuCopy*/ 'Copy', + event: 'copy', + icon: 'icon-copy' + }); + } + + if ( canViewComments && this.isComments && isEdit ) { + itemsText.push({ + caption: /*me.menuViewComment*/'View Comment', + event: 'viewcomment' + }); + } + + let isText = false, + isTable = false, + isImage = false, + isChart = false, + isShape = false, + isLink = false, + lockedText = false, + lockedTable = false, + lockedImage = false, + lockedHeader = false; + + stack.forEach(item => { + const objectType = item.get_ObjectType(), + objectValue = item.get_ObjectValue(); + + if ( objectType == Asc.c_oAscTypeSelectElement.Header ) { + lockedHeader = objectValue.get_Locked(); + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Paragraph ) { + lockedText = objectValue.get_Locked(); + isText = true; + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Image ) { + lockedImage = objectValue.get_Locked(); + if ( objectValue && objectValue.get_ChartProperties() ) { + isChart = true; + } else + if ( objectValue && objectValue.get_ShapeProperties() ) { + isShape = true; + } else { + isImage = true; + } + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Table ) { + lockedTable = objectValue.get_Locked(); + isTable = true; + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Hyperlink ) { + isLink = true; } - ]; + }); + + if ( stack.length > 0 ) { + const swapItems = function(items, indexBefore, indexAfter) { + items[indexAfter] = items.splice(indexBefore, 1, items[indexAfter])[0]; + }; + + if ( isEdit && !this.isDisconnected ) { + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader && canCopy ) { + itemsIcon.push({ + // caption: me.menuCut, + event: 'cut', + icon: 'icon-cut' + }); + + // Swap 'Copy' and 'Cut' + swapItems(itemsIcon, 0, 1); + } + + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ) { + itemsIcon.push({ + // caption: me.menuPaste, + event: 'paste', + icon: 'icon-paste' + }); + } + + if ( isTable && api.CheckBeforeMergeCells() && !lockedTable && !lockedHeader) { + itemsText.push({ + caption: /*me.menuMerge*/'Merge', + event: 'merge' + }); + } + + if ( isTable && api.CheckBeforeSplitCells() && !lockedTable && !lockedHeader ) { + itemsText.push({ + caption: /*me.menuSplit*/'Split', + event: 'split' + }); + } + + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ) { + itemsText.push({ + caption: /*me.menuDelete*/'Delete', + event: 'delete' + }); + } + + if ( isTable && !lockedTable && !lockedText && !lockedHeader ) { + itemsText.push({ + caption: /*me.menuDeleteTable*/'Delete Table', + event: 'deletetable' + }); + } + + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ){ + itemsText.push({ + caption: /*me.menuEdit*/'Edit', + event: 'edit' + }); + } + + // if ( !_.isEmpty(api.can_AddHyperlink()) && !lockedHeader) { + // arrItems.push({ + // caption: me.menuAddLink, + // event: 'addlink' + // }); + // } + + if ( canReview ) { + if (false /*_inRevisionChange*/) { + itemsText.push({ + caption: /*me.menuReviewChange*/'Review Change', + event: 'reviewchange' + }); + } else { + itemsText.push({ + caption: /*me.menuReview*/'Review', + event: 'review' + }); + } + } + + if ( this.isComments && canViewComments ) { + itemsText.push({ + caption: /*me.menuViewComment*/'View Comment', + event: 'viewcomment' + }); + } + + const isObject = isShape || isChart || isImage || isTable; + const hideAddComment = !canViewComments || api.can_AddQuotedComment() === false || lockedText || lockedTable || lockedImage || lockedHeader || (!isText && isObject); + if ( !hideAddComment ) { + itemsText.push({ + caption: /*me.menuAddComment*/'Add Comment', + event: 'addcomment' + }); + } + } + } + + if ( isLink ) { + itemsText.push({ + caption: /*me.menuOpenLink*/'Open Link', + event: 'openlink' + }); + } + + if ( Device.phone && itemsText.length > 2 ) { + // _actionSheets = arrItems.slice(2); + // arrItems = arrItems.slice(0, 2); + // arrItems.push({ + // caption: me.menuMore, + // event: 'showActionSheet' + // }); + this.extraItems = itemsText.splice(2,itemsText.length, { + caption: /*me.menuMore*/'More', + event: 'showActionSheet' + }); + } + + return itemsIcon.concat(itemsText); + // return [{ + // caption: 'Edit', + // event: 'edit' + // }, { + // caption: 'View', + // event: 'view' + // }, { + // icon: 'icon-paste', + // event: 'review' + // }]; } } -export { ContextMenu, idContextMenuElement }; \ No newline at end of file +export { ContextMenu as default }; \ No newline at end of file diff --git a/apps/documenteditor/mobile/src/page/main.jsx b/apps/documenteditor/mobile/src/page/main.jsx index 8775dcdd7..be5a0685e 100644 --- a/apps/documenteditor/mobile/src/page/main.jsx +++ b/apps/documenteditor/mobile/src/page/main.jsx @@ -11,7 +11,7 @@ import Collaboration from '../../../../common/mobile/lib/view/collaboration/Coll import { AddCommentController } from '../../../../common/mobile/lib/controller/collaboration/Comments.jsx'; import { Device } from '../../../../common/mobile/utils/device' import { Search, SearchSettings } from '../controller/Search'; -import { ContextMenu, idContextMenuElement } from '../controller/ContextMenu'; +import ContextMenu from '../controller/ContextMenu'; export default class MainPage extends Component { constructor(props) { @@ -25,7 +25,7 @@ export default class MainPage extends Component { } handleClickToOpenOptions = opts => { - f7.popover.close(idContextMenuElement, false); + ContextMenu.closeContextMenu(); this.setState(state => { if ( opts == 'edit' ) diff --git a/apps/documenteditor/mobile/src/store/appOptions.js b/apps/documenteditor/mobile/src/store/appOptions.js index 5020a25a8..998753556 100644 --- a/apps/documenteditor/mobile/src/store/appOptions.js +++ b/apps/documenteditor/mobile/src/store/appOptions.js @@ -1,6 +1,14 @@ import {action, observable} from 'mobx'; export class storeAppOptions { + constructor() { + // makeObservable(this); + } + + @observable isEdit = false; + @observable canViewComments = false; + @observable canReview = false; + config = {}; @action setConfigOptions (config) { this.config = config;