[PE mobile] Add context menu
This commit is contained in:
parent
cb755596ff
commit
4b0ec3e352
|
@ -47,7 +47,7 @@ const ActionsWithExtraItems = ({items, onMenuItemClick, opened, onActionClosed})
|
|||
<ActionsGroup>
|
||||
{items.length > 0 && items.map((item, index)=>{
|
||||
return(
|
||||
<ActionsButton key={`act-${item.caption}`} onClick={() => {onMenuItemClick(item.event)}}>{item.caption}</ActionsButton>
|
||||
<ActionsButton key={`act-${index}`} onClick={() => {onMenuItemClick(item.event)}}>{item.caption}</ActionsButton>
|
||||
)
|
||||
})}
|
||||
</ActionsGroup>
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
}
|
||||
|
||||
.comment-list {
|
||||
.item-inner:after, li:last-child li .item-inner:after {
|
||||
ul:after, .item-inner:after, li:last-child li .item-inner:after {
|
||||
content: none;
|
||||
}
|
||||
.comment-header {
|
||||
|
|
|
@ -24,6 +24,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"ContextMenu": {
|
||||
"menuViewComment": "View Comment",
|
||||
"menuAddComment": "Add Comment",
|
||||
"menuDelete": "Delete",
|
||||
"menuEdit": "Edit",
|
||||
"menuAddLink": "Add Link",
|
||||
"menuOpenLink": "Open Link",
|
||||
"menuMore": "More",
|
||||
"menuCancel": "Cancel",
|
||||
"textCopyCutPasteActions": "Copy, Cut and Paste Actions",
|
||||
"errorCopyCutPaste": "Copy, cut and paste actions using the context menu will be performed within the current file only.",
|
||||
"textDoNotShowAgain": "Don't show again"
|
||||
},
|
||||
"View": {
|
||||
"Settings": {
|
||||
"textDone": "Done",
|
||||
|
@ -101,6 +114,7 @@
|
|||
"textColumns": "Columns",
|
||||
"textRows": "Rows",
|
||||
"textCancel": "Cancel",
|
||||
"textAddLink": "Add Link",
|
||||
"textLink": "Link",
|
||||
"textLinkType": "Link Type",
|
||||
"textExternalLink": "External Link",
|
||||
|
|
|
@ -4,6 +4,8 @@ import ReactDOM from 'react-dom';
|
|||
|
||||
// Import Framework7
|
||||
import Framework7 from 'framework7/lite-bundle';
|
||||
import { Dom7 } from 'framework7';
|
||||
window.$$ = Dom7;
|
||||
|
||||
// Import Framework7-React Plugin
|
||||
import Framework7React from 'framework7-react';
|
||||
|
|
312
apps/presentationeditor/mobile/src/controller/ContextMenu.jsx
Normal file
312
apps/presentationeditor/mobile/src/controller/ContextMenu.jsx
Normal file
|
@ -0,0 +1,312 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { f7 } from 'framework7-react';
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation} from 'react-i18next';
|
||||
import { LocalStorage } from '../../../../common/mobile/utils/LocalStorage';
|
||||
|
||||
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,
|
||||
users: stores.users,
|
||||
isDisconnected: stores.users.isDisconnected
|
||||
}))
|
||||
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);
|
||||
this.getUserName = this.getUserName.bind(this);
|
||||
}
|
||||
|
||||
static closeContextMenu() {
|
||||
f7.popover.close(idContextMenuElement, false);
|
||||
}
|
||||
|
||||
getUserName(id) {
|
||||
const user = this.props.users.searchUserByCurrentId(id);
|
||||
return Common.Utils.UserInfoParser.getParsedName(user.asc_getUserName());
|
||||
}
|
||||
|
||||
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() {
|
||||
// super.onMenuClosed();
|
||||
// }
|
||||
|
||||
onMenuItemClick(action) {
|
||||
super.onMenuItemClick(action);
|
||||
|
||||
const api = Common.EditorApi.get();
|
||||
switch (action) {
|
||||
case 'cut':
|
||||
if (!api.Cut() && !LocalStorage.getBool("pe-hide-copy-cut-paste-warning")) {
|
||||
this.showCopyCutPasteModal();
|
||||
}
|
||||
break;
|
||||
case 'copy':
|
||||
if (!api.Copy() && !LocalStorage.getBool("pe-hide-copy-cut-paste-warning")) {
|
||||
this.showCopyCutPasteModal();
|
||||
}
|
||||
break;
|
||||
case 'paste':
|
||||
if (!api.Paste() && !LocalStorage.getBool("pe-hide-copy-cut-paste-warning")) {
|
||||
this.showCopyCutPasteModal();
|
||||
}
|
||||
break;
|
||||
case 'addcomment':
|
||||
Common.Notifications.trigger('addcomment');
|
||||
break;
|
||||
case 'viewcomment':
|
||||
Common.Notifications.trigger('viewcomment');
|
||||
break;
|
||||
case 'delete':
|
||||
api.asc_Remove();
|
||||
break;
|
||||
case 'edit':
|
||||
setTimeout(() => {
|
||||
this.props.openOptions('edit');
|
||||
}, 0);
|
||||
break;
|
||||
case 'addlink':
|
||||
setTimeout(() => {
|
||||
this.props.openOptions('add', 'link');
|
||||
}, 400)
|
||||
break;
|
||||
case 'openlink':
|
||||
const stack = Common.EditorApi.get().getSelectedElements();
|
||||
let value;
|
||||
stack.forEach((item) => {
|
||||
if (item.get_ObjectType() == Asc.c_oAscTypeSelectElement.Hyperlink) {
|
||||
value = item.get_ObjectValue().get_Value();
|
||||
}
|
||||
});
|
||||
value && this.openLink(value);
|
||||
break;
|
||||
}
|
||||
|
||||
console.log("click context menu item: " + action);
|
||||
}
|
||||
|
||||
showCopyCutPasteModal() {
|
||||
const { t } = this.props;
|
||||
const _t = t("ContextMenu", { returnObjects: true });
|
||||
f7.dialog.create({
|
||||
title: _t.textCopyCutPasteActions,
|
||||
text: _t.errorCopyCutPaste,
|
||||
content: `<div class="checkbox-in-modal">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="checkbox-show" />
|
||||
<i class="icon-checkbox"></i>
|
||||
</label>
|
||||
<span class="right-text">${_t.textDoNotShowAgain}</span>
|
||||
</div>`,
|
||||
buttons: [{
|
||||
text: 'OK',
|
||||
onClick: () => {
|
||||
const dontShow = $$('input[name="checkbox-show"]').prop('checked');
|
||||
if (dontShow) LocalStorage.setItem("de-hide-copy-cut-paste-warning", 1);
|
||||
}
|
||||
}]
|
||||
}).open();
|
||||
}
|
||||
|
||||
openLink(url) {
|
||||
const api = Common.EditorApi.get();
|
||||
if (api.asc_getUrlType(url) > 0) {
|
||||
const newDocumentPage = window.open(url, '_blank');
|
||||
if (newDocumentPage) {
|
||||
newDocumentPage.focus();
|
||||
}
|
||||
} else {
|
||||
api.asc_GoToInternalHyperlink(url);
|
||||
}
|
||||
}
|
||||
|
||||
onDocumentReady() {
|
||||
super.onDocumentReady();
|
||||
|
||||
const api = Common.EditorApi.get();
|
||||
api.asc_registerCallback('asc_onShowComment', this.onApiShowComment);
|
||||
api.asc_registerCallback('asc_onHideComment', this.onApiHideComment);
|
||||
}
|
||||
|
||||
initMenuItems() {
|
||||
if ( !Common.EditorApi ) return [];
|
||||
|
||||
const { t } = this.props;
|
||||
const _t = t("ContextMenu", { returnObjects: true });
|
||||
|
||||
const { isEdit, canViewComments, canReview, isDisconnected } = this.props;
|
||||
|
||||
const api = Common.EditorApi.get();
|
||||
const stack = api.getSelectedElements();
|
||||
const canCopy = api.can_CopyCut();
|
||||
|
||||
let itemsIcon = [],
|
||||
itemsText = [];
|
||||
|
||||
let isText = false,
|
||||
isTable = false,
|
||||
isImage = false,
|
||||
isChart = false,
|
||||
isShape = false,
|
||||
isLink = false,
|
||||
isSlide = false,
|
||||
isObject = false;
|
||||
|
||||
stack.forEach(item => {
|
||||
const objectType = item.get_ObjectType(),
|
||||
objectValue = item.get_ObjectValue();
|
||||
|
||||
if (objectType == Asc.c_oAscTypeSelectElement.Paragraph) {
|
||||
isText = true;
|
||||
} else if (objectType == Asc.c_oAscTypeSelectElement.Image) {
|
||||
isImage = true;
|
||||
} else if (objectType == Asc.c_oAscTypeSelectElement.Chart) {
|
||||
isChart = true;
|
||||
} else if (objectType == Asc.c_oAscTypeSelectElement.Shape) {
|
||||
isShape = true;
|
||||
} else if (objectType == Asc.c_oAscTypeSelectElement.Table) {
|
||||
isTable = true;
|
||||
} else if (objectType == Asc.c_oAscTypeSelectElement.Hyperlink) {
|
||||
isLink = true;
|
||||
} else if (objectType == Asc.c_oAscTypeSelectElement.Slide) {
|
||||
isSlide = true;
|
||||
}
|
||||
});
|
||||
|
||||
isObject = isText || isImage || isChart || isShape || isTable;
|
||||
|
||||
if (canCopy && isObject) {
|
||||
itemsIcon.push({
|
||||
event: 'copy',
|
||||
icon: 'icon-copy'
|
||||
});
|
||||
}
|
||||
if (canViewComments && this.isComments && !isEdit) {
|
||||
itemsText.push({
|
||||
caption: _t.menuViewComment,
|
||||
event: 'viewcomment'
|
||||
});
|
||||
}
|
||||
|
||||
if ( stack.length > 0 ) {
|
||||
let topObject = stack[stack.length - 1],
|
||||
topObjectType = topObject.get_ObjectType(),
|
||||
topObjectValue = topObject.get_ObjectValue(),
|
||||
objectLocked = typeof topObjectValue.get_Locked === 'function' ? topObjectValue.get_Locked() : false;
|
||||
|
||||
!objectLocked && (objectLocked = typeof topObjectValue.get_LockDelete === 'function' ? topObjectValue.get_LockDelete() : false);
|
||||
|
||||
const swapItems = function(items, indexBefore, indexAfter) {
|
||||
items[indexAfter] = items.splice(indexBefore, 1, items[indexAfter])[0];
|
||||
};
|
||||
|
||||
if (!objectLocked && isEdit && !isDisconnected) {
|
||||
if (canCopy && isObject) {
|
||||
itemsIcon.push({
|
||||
event: 'cut',
|
||||
icon: 'icon-cut'
|
||||
});
|
||||
|
||||
// Swap 'Copy' and 'Cut'
|
||||
swapItems(itemsIcon, 0, 1);
|
||||
}
|
||||
|
||||
itemsIcon.push({
|
||||
event: 'paste',
|
||||
icon: 'icon-paste'
|
||||
});
|
||||
|
||||
if (isObject)
|
||||
itemsText.push({
|
||||
caption: _t.menuDelete,
|
||||
event: 'delete'
|
||||
});
|
||||
|
||||
itemsText.push({
|
||||
caption: _t.menuEdit,
|
||||
event: 'edit'
|
||||
});
|
||||
|
||||
if (!isLink && api.can_AddHyperlink() !== false) {
|
||||
itemsText.push({
|
||||
caption: _t.menuAddLink,
|
||||
event: 'addlink'
|
||||
});
|
||||
}
|
||||
|
||||
if (this.isComments && canViewComments) {
|
||||
itemsText.push({
|
||||
caption: _t.menuViewComment,
|
||||
event: 'viewcomment'
|
||||
});
|
||||
}
|
||||
|
||||
var hideAddComment = (isText && isChart) || api.can_AddQuotedComment() === false || !canViewComments;
|
||||
if (!hideAddComment) {
|
||||
itemsText.push({
|
||||
caption: _t.menuAddComment,
|
||||
event: 'addcomment'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isLink) {
|
||||
itemsText.push({
|
||||
caption: _t.menuOpenLink,
|
||||
event: 'openlink'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if ( Device.phone && itemsText.length > 2 ) {
|
||||
this.extraItems = itemsText.splice(2,itemsText.length, {
|
||||
caption: _t.menuMore,
|
||||
event: 'showActionSheet'
|
||||
});
|
||||
}
|
||||
|
||||
return itemsIcon.concat(itemsText);
|
||||
// return [{
|
||||
// caption: 'Edit',
|
||||
// event: 'edit'
|
||||
// }, {
|
||||
// caption: 'View',
|
||||
// event: 'view'
|
||||
// }, {
|
||||
// icon: 'icon-paste',
|
||||
// event: 'review'
|
||||
// }];
|
||||
}
|
||||
|
||||
initExtraItems () {
|
||||
return (this.extraItems && this.extraItems.length > 0 ? this.extraItems : []);
|
||||
}
|
||||
}
|
||||
|
||||
const _ContextMenu = withTranslation()(ContextMenu);
|
||||
_ContextMenu.closeContextMenu = ContextMenu.closeContextMenu;
|
||||
export { _ContextMenu as default };
|
|
@ -340,6 +340,8 @@ class MainController extends Component {
|
|||
|
||||
Common.Gateway.documentReady();
|
||||
f7.emit('resize');
|
||||
|
||||
Common.Notifications.trigger('document:ready');
|
||||
}
|
||||
|
||||
_onOpenDocumentProgress(progress) {
|
||||
|
|
|
@ -102,6 +102,7 @@ class AddLinkController extends Component {
|
|||
return (
|
||||
<PageLink onInsertLink={this.onInsertLink}
|
||||
getTextDisplay={this.getTextDisplay}
|
||||
noNavbar={this.props.noNavbar}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
@import '../../../../common/mobile/resources/less/dataview.less';
|
||||
@import '../../../../common/mobile/resources/less/about.less';
|
||||
@import '../../../../common/mobile/resources/less/search.less';
|
||||
@import '../../../../common/mobile/resources/less/contextmenu.less';
|
||||
@import '../../../../common/mobile/resources/less/comments.less';
|
||||
@import './app-material.less';
|
||||
@import './app-ios.less';
|
||||
@import './icons-ios.less';
|
||||
|
|
|
@ -7,6 +7,7 @@ import Settings from '../view/settings/Settings';
|
|||
import CollaborationView from '../../../../common/mobile/lib/view/collaboration/Collaboration.jsx';
|
||||
import { Device } from '../../../../common/mobile/utils/device';
|
||||
import { Search, SearchSettings } from '../controller/Search';
|
||||
import ContextMenu from '../controller/ContextMenu';
|
||||
|
||||
export default class MainPage extends Component {
|
||||
constructor(props) {
|
||||
|
@ -19,12 +20,17 @@ export default class MainPage extends Component {
|
|||
};
|
||||
}
|
||||
|
||||
handleClickToOpenOptions = opts => {
|
||||
handleClickToOpenOptions = (opts, showOpts) => {
|
||||
ContextMenu.closeContextMenu();
|
||||
|
||||
this.setState(state => {
|
||||
if ( opts == 'edit' )
|
||||
return {editOptionsVisible: true};
|
||||
else if ( opts == 'add' )
|
||||
return {addOptionsVisible: true};
|
||||
return {
|
||||
addOptionsVisible: true,
|
||||
addShowOptions: showOpts
|
||||
};
|
||||
else if ( opts == 'settings' )
|
||||
return {settingsVisible: true};
|
||||
else if ( opts == 'coauth' )
|
||||
|
@ -77,7 +83,7 @@ export default class MainPage extends Component {
|
|||
}
|
||||
{
|
||||
!this.state.addOptionsVisible ? null :
|
||||
<AddOptions onclosed={this.handleOptionsViewClosed.bind(this, 'add')} />
|
||||
<AddOptions onclosed={this.handleOptionsViewClosed.bind(this, 'add')} showOptions={this.state.addShowOptions} />
|
||||
}
|
||||
{
|
||||
!this.state.settingsVisible ? null :
|
||||
|
@ -87,6 +93,7 @@ export default class MainPage extends Component {
|
|||
!this.state.collaborationVisible ? null :
|
||||
<CollaborationView onclosed={this.handleOptionsViewClosed.bind(this, 'coauth')} />
|
||||
}
|
||||
<ContextMenu openOptions={this.handleClickToOpenOptions.bind(this)} />
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, {Component, useEffect} from 'react';
|
||||
import {View,Page,Navbar,NavRight,Link,Popup,Popover,Icon,Tabs,Tab} from 'framework7-react';
|
||||
import {View,Page,Navbar,NavRight, NavTitle, Link,Popup,Popover,Icon,Tabs,Tab} from 'framework7-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {f7} from 'framework7-react';
|
||||
import { observer, inject } from "mobx-react";
|
||||
|
@ -43,13 +43,16 @@ const AddLayoutNavbar = ({ tabs, inPopover }) => {
|
|||
const isAndroid = Device.android;
|
||||
return (
|
||||
<Navbar>
|
||||
<div className='tab-buttons tabbar'>
|
||||
{tabs.map((item, index) =>
|
||||
<Link key={"pe-link-" + item.id} tabLink={"#" + item.id} tabLinkActive={index === 0}>
|
||||
<Icon slot="media" icon={item.icon}></Icon>
|
||||
</Link>)}
|
||||
{isAndroid && <span className='tab-link-highlight' style={{width: 100 / tabs.lenght + '%'}}></span>}
|
||||
</div>
|
||||
{tabs.length > 1 ?
|
||||
<div className='tab-buttons tabbar'>
|
||||
{tabs.map((item, index) =>
|
||||
<Link key={"pe-link-" + item.id} tabLink={"#" + item.id} tabLinkActive={index === 0}>
|
||||
<Icon slot="media" icon={item.icon}></Icon>
|
||||
</Link>)}
|
||||
{isAndroid && <span className='tab-link-highlight' style={{width: 100 / tabs.lenght + '%'}}></span>}
|
||||
</div> :
|
||||
<NavTitle>{tabs[0].caption}</NavTitle>
|
||||
}
|
||||
{ !inPopover && <NavRight><Link icon='icon-expand-down' popupClose=".add-popup"></Link></NavRight> }
|
||||
</Navbar>
|
||||
)
|
||||
|
@ -69,32 +72,42 @@ const AddLayoutContent = ({ tabs }) => {
|
|||
|
||||
const AddTabs = props => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('Add', {returnObjects: true});
|
||||
const _t = t('View.Add', {returnObjects: true});
|
||||
const showPanels = props.showPanels;
|
||||
const tabs = [];
|
||||
tabs.push({
|
||||
caption: _t.textSlide,
|
||||
id: 'add-slide',
|
||||
icon: 'icon-add-slide',
|
||||
component: <AddSlideController />
|
||||
});
|
||||
tabs.push({
|
||||
caption: _t.textShape,
|
||||
id: 'add-shape',
|
||||
icon: 'icon-add-shape',
|
||||
component: <AddShapeController />
|
||||
});
|
||||
tabs.push({
|
||||
caption: _t.textImage,
|
||||
id: 'add-image',
|
||||
icon: 'icon-add-image',
|
||||
component: <AddImageController />
|
||||
});
|
||||
tabs.push({
|
||||
caption: _t.textOther,
|
||||
id: 'add-other',
|
||||
icon: 'icon-add-other',
|
||||
component: <AddOtherController />
|
||||
});
|
||||
if (!showPanels) {
|
||||
tabs.push({
|
||||
caption: _t.textSlide,
|
||||
id: 'add-slide',
|
||||
icon: 'icon-add-slide',
|
||||
component: <AddSlideController />
|
||||
});
|
||||
tabs.push({
|
||||
caption: _t.textShape,
|
||||
id: 'add-shape',
|
||||
icon: 'icon-add-shape',
|
||||
component: <AddShapeController/>
|
||||
});
|
||||
tabs.push({
|
||||
caption: _t.textImage,
|
||||
id: 'add-image',
|
||||
icon: 'icon-add-image',
|
||||
component: <AddImageController/>
|
||||
});
|
||||
tabs.push({
|
||||
caption: _t.textOther,
|
||||
id: 'add-other',
|
||||
icon: 'icon-add-other',
|
||||
component: <AddOtherController/>
|
||||
});
|
||||
}
|
||||
if (showPanels && showPanels === 'link') {
|
||||
tabs.push({
|
||||
caption: _t.textAddLink,
|
||||
id: 'add-link',
|
||||
component: <AddLinkController noNavbar={true}/>
|
||||
});
|
||||
}
|
||||
return (
|
||||
<View style={props.style} stackPages={true} routes={routes}>
|
||||
<Page pageContent={false}>
|
||||
|
@ -119,10 +132,10 @@ class AddView extends Component {
|
|||
return (
|
||||
show_popover ?
|
||||
<Popover id="add-popover" className="popover__titled" onPopoverClosed={() => this.props.onclosed()}>
|
||||
<AddTabs inPopover={true} onOptionClick={this.onoptionclick} style={{height: '410px'}} />
|
||||
<AddTabs inPopover={true} onOptionClick={this.onoptionclick} style={{height: '410px'}} showPanels={this.props.showPanels} />
|
||||
</Popover> :
|
||||
<Popup className="add-popup" onPopupClosed={() => this.props.onclosed()}>
|
||||
<AddTabs onOptionClick={this.onoptionclick} />
|
||||
<AddTabs onOptionClick={this.onoptionclick} showPanels={this.props.showPanels} />
|
||||
</Popup>
|
||||
)
|
||||
}
|
||||
|
@ -142,7 +155,7 @@ const Add = props => {
|
|||
if ( props.onclosed )
|
||||
props.onclosed();
|
||||
};
|
||||
return <AddView usePopover={!Device.phone} onclosed={onviewclosed} />
|
||||
return <AddView usePopover={!Device.phone} onclosed={onviewclosed} showPanels={props.showOptions} />
|
||||
};
|
||||
|
||||
export default Add;
|
|
@ -103,7 +103,7 @@ const PageLink = props => {
|
|||
|
||||
return (
|
||||
<Page>
|
||||
<Navbar title={_t.textLink} backLink={_t.textBack}/>
|
||||
{!props.noNavbar && <Navbar title={_t.textLink} backLink={_t.textBack}/>}
|
||||
<List inlineLabels className='inputs-list'>
|
||||
<ListItem link={'/add-link-type/'} title={_t.textLinkType} after={textType} routeProps={{
|
||||
changeType: changeType,
|
||||
|
|
Loading…
Reference in a new issue