[DE mobile] context menu moved to common

This commit is contained in:
Maxim Kadushkin 2021-02-10 00:01:42 +03:00
parent 1c3e77c22b
commit 89d297ea9c
9 changed files with 371 additions and 79 deletions

View file

@ -0,0 +1,157 @@
import React, { Component } from 'react';
import { f7 } from 'framework7-react';
import { Device } from '../../../../common/mobile/utils/device'
import ContextMenuView from '../view/ContextMenu';
class ContextMenuController extends Component {
constructor(props) {
super(props);
this.state = {
opened: false
};
this.onMenuClosed = this.onMenuClosed.bind(this);
this.onDocumentReady = this.onDocumentReady.bind(this);
this.onApiOpenContextMenu = this.onApiOpenContextMenu.bind(this);
this.onApiHideContextMenu = this.onApiHideContextMenu.bind(this);
}
onDocumentReady() {
this.$targetEl = $$('#idx-context-menu-target');
if ( !this.$targetEl.length ) {
// this.$targetEl = $$('<div id="idx-context-menu-target" style="position:absolute;width:15px;height:15px;background-color:green;z-index:1;"></div>');
this.$targetEl = $$('<div id="idx-context-menu-target" style="position:absolute;"></div>');
this.$targetEl.css({left: '-10000px', top: '-10000px'});
$$('#editor_sdk').append(this.$targetEl);
}
const api = Common.EditorApi.get();
api.asc_registerCallback('asc_onShowPopMenu', this.onApiOpenContextMenu);
api.asc_registerCallback('asc_onHidePopMenu', this.onApiHideContextMenu);
}
offsetPopoverTop(popover) {
var app = popover.app,
$el = popover.$el,
$targetEl = popover.$targetEl;
const width = $el.width(),
height = $el.height();
$el.removeClass('popover-on-left popover-on-right popover-on-top popover-on-bottom popover-on-middle').css({
left: '',
top: ''
});
let targetOffsetLeft, targetOffsetTop;
// var safeAreaTop = parseInt($('html').css('--f7-safe-area-top'), 10);
let safeAreaLeft = parseInt($('html').css('--f7-safe-area-left'), 10),
safeAreaRight = parseInt($('html').css('--f7-safe-area-right'), 10);
// if (Number.isNaN(safeAreaTop)) safeAreaTop = 0;
if (Number.isNaN(safeAreaLeft)) safeAreaLeft = 0;
if (Number.isNaN(safeAreaRight)) safeAreaRight = 0;
if ($targetEl && $targetEl.length > 0) {
let targetOffset = $targetEl.offset();
targetOffsetLeft = targetOffset.left - app.left;
targetOffsetTop = targetOffset.top - app.top;
let targetParentPage = $targetEl.parents('.page');
if (targetParentPage.length > 0) {
targetOffsetTop -= targetParentPage[0].scrollTop;
}
}
let position = 'top';
let top = targetOffsetTop - height - 10;
top = Math.max(8, Math.min(top, app.height - height - 8)); // Horizontal Position
let hPosition;
// if (targetOffsetLeft < app.width / 2) {
// hPosition = 'right';
// left = position === 'middle' ? targetOffsetLeft + targetWidth : targetOffsetLeft;
// } else {
// hPosition = 'left';
// left = position === 'middle' ? targetOffsetLeft - width : targetOffsetLeft + targetWidth - width;
// }
hPosition = 'middle';
let left = targetOffsetLeft - width / 2;
left = Math.max(8, Math.min(left, app.width - width - 8 - safeAreaRight), safeAreaLeft);
$el.addClass(`popover-on-${position} popover-on-${hPosition}`);
$el.css({top: `${top}px`,
left: `${left}px`});
}
onApiOpenContextMenu(x, y) {
if ( !this.state.opened ) {
this.$targetEl.css({left: `${x}px`, top: `${y}px`});
const popover = f7.popover.open('#idx-context-menu-popover', '#idx-context-menu-target');
if ( Device.android )
this.offsetPopoverTop(popover);
this.setState(state => {
return {opened: true}
});
}
}
onApiHideContextMenu() {
if ( this.state.opened ) {
f7.popover.close('#idx-context-menu-popover');
this.$targetEl.css({left: '-10000px', top: '-10000px'});
this.setState({opened: false});
}
}
onMenuClosed() {
// (async () => {
// await 1 && this.setState(state => {
// this.$targetEl.css({left: '-10000px', top: '-10000px'});
// return ({opened: false});
// });
// })();
}
onMenuItemClick(action) {
}
componentWillUnmount() {
Common.Notifications.off('document:ready', this.onDocumentReady);
const api = Common.EditorApi.get();
api.asc_unregisterCallback('asc_onShowPopMenu', this.onApiOpenContextMenu);
api.asc_unregisterCallback('asc_onHidePopMenu', this.onApiHideContextMenu);
}
componentDidMount() {
if ( !Common.EditorApi ) {
Common.Notifications.on({
'document:ready': this.onDocumentReady
});
} else {
this.onDocumentReady();
}
}
initMenuItems() {
return [];
}
render() {
return (
<ContextMenuView items={this.initMenuItems()} onMenuClosed={this.onMenuClosed} onMenuItemClick={this.onMenuItemClick} />
)
}
}
export default ContextMenuController;

View file

@ -0,0 +1,39 @@
import React, { Component } from 'react';
import { Popover, List, ListItem, ListButton, Link, Icon } from 'framework7-react';
import { f7 } from 'framework7-react';
class ContextMenuView extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
// f7.popover.open('#idx-context-menu-popover', '#idx-context-menu-target');
}
render() {
const buttons = this.props.items || {};
return (
<Popover id="idx-context-menu-popover"
className="document-menu"
backdrop={false}
closeByBackdropClick={false}
closeByOutsideClick={false}
onPopoverClosed={e => this.props.onMenuClosed()}
>
<List className="list-block">
{buttons.map((b, index) =>
!!b.text ?
<ListButton className="asd" title={b.text} key={index} onClick={e => this.props.onMenuItemClick(b.action)} /> :
<ListButton className="asd" title={b.text} key={index}>
<Icon slot="media" icon={`icon_mask ${b.icon}`} />
</ListButton>
)}
</List>
</Popover>
)
}
}
export default ContextMenuView;

View file

@ -0,0 +1,7 @@
@import './ios/contextmenu';
@import './material/contextmenu';
.document-menu {
width: auto;
}

View file

@ -0,0 +1,102 @@
.device-ios {
.document-menu {
@contextMenuBg: rgba(0, 0, 0, 0.9);
@modalHairlineColor: rgba(230, 230, 230, 0.9);
@modalButtonColor: rgba(200, 200, 200, 0.9);
background-color: @contextMenuBg;
border-radius: 8px;
.popover-angle {
&:after {
background: @contextMenuBg;
}
}
.list-block {
font-size: 14px;
white-space: pre;
&:first-child {
ul {
.hairline-remove(left);
//border-radius: 7px 0 0 7px;
}
li:first-child a {
//border-radius: 7px 0 0 7px;
border-radius: 0;
}
}
//&:last-child {
// ul {
// .hairline-remove(right);
// border-radius: 0 7px 7px 0;
// }
// li:last-child a{
// border-radius: 0 7px 7px 0;
// }
//}
//&:first-child:last-child {
// li:first-child:last-child a, ul:first-child:last-child {
// border-radius: 7px;
// }
//}
.item-link {
display: inline-block;
html:not(.watch-active-state) &:active, &.active-state {
//.transition(0ms);
background-color: #d9d9d9;
.item-inner {
.hairline-color(right, transparent);
}
}
html.phone & {
padding: 0 10px;
}
}
.list-button {
color: @white;
line-height: 36px;
.hairline(right, @modalHairlineColor);
}
// List items
li {
display: inline-flex;
}
// Last-childs
li {
&:last-child {
.list-button {
.hairline-remove(right);
}
}
&:last-child, &:last-child li:last-child {
.item-inner {
.hairline-remove(right);
}
}
li:last-child, &:last-child li {
.item-inner {
.hairline(right, @modalHairlineColor);
}
}
}
//.no-hairlines();
//.no-hairlines-between()
}
}
}

View file

@ -0,0 +1,32 @@
.md {
.document-menu {
//line-height: 1 !important;
.popover-inner {
overflow: hidden;
}
.list-block {
white-space: pre;
ul {
height: 48px;
}
li {
display: inline-block;
}
.list-button {
color: #212121;
}
//.item-link {
// html.phone & {
//padding: 0 10px;
//}
//}
}
}
}

View file

@ -1,74 +1,38 @@
import React, { Component } from 'react';
import React from 'react';
import { f7 } from 'framework7-react';
import ContextMenuController from '../../../../common/mobile/lib/controller/ContextMenu';
import ContextMenuView from '../view/ContextMenu';
class ContextMenuController extends Component {
class ContextMenu extends ContextMenuController {
constructor(props) {
super(props);
this.state = {
pos: [-10000,-10000],
show: false
};
// console.log('context menu controller created');
}
this.onMenuClosed = this.onMenuClosed.bind(this);
// onMenuClosed() {
// super.onMenuClosed();
// }
Common.Notifications.on({
'engineCreated': api => {
api.asc_registerCallback('asc_onShowPopMenu', this.onApiShowPopMenu.bind(this));
api.asc_registerCallback('asc_onHidePopMenu', this.onApiHidePopMenu.bind(this));
},
'document:ready': () => {
this.$targetEl = $$('<div id="idx-context-menu-target" style="position:absolute;width:15px;height:15px;background-color:green;z-index:1;"></div>');
this.$targetEl.css({left: '-10000px', top: '-10000px'});
onMenuItemClick(action) {
super.onMenuItemClick(action);
$$('#editor_sdk').append(this.$targetEl);
console.log("on click item");
}
initMenuItems() {
return [
{
text: 'Edit',
action: 'edit'
}, {
text: 'View',
action: 'view'
}, {
icon: 'icon-paste',
action: 'review'
}
});
console.log('context menu controller created');
}
onApiShowPopMenu(x, y) {
console.log('show context menu ' + [x,y]);
this.$targetEl.css({left: `${x}px`, top: `${y}px`});
this.setState({
pos: [x,y],
show: true });
}
onApiHidePopMenu() {
// console.log('hide context menu');
if ( this.state.show ) {
f7.popover.close('#idx-context-menu-popover');
}
}
onMenuClosed() {
(async () => {
await 1 && this.setState(state => {
this.$targetEl.css({left: '-10000px', top: '-10000px'});
return ({pos: [-10000, -10000], show: false});
});
})();
}
componentWillUnmount() {
console.log('context menu controller will be unmounted');
}
componentDidMount() {
console.log('context menu controller did mount');
}
render() {
return (
!this.state.show ? null :
<ContextMenuView onMenuClosed={this.onMenuClosed} />)
];
}
}
export { ContextMenuController as ContextMenu };
export { ContextMenu };

View file

@ -7,8 +7,10 @@
@import '../../../../common/mobile/resources/less/common.less';
@import '../../../../common/mobile/resources/less/common-ios.less';
@import '../../../../common/mobile/resources/less/common-material.less';
@import '../../../../common/mobile/resources/less/icons.less';
@import '../../../../common/mobile/resources/less/dataview.less';
@import '../../../../common/mobile/resources/less/search.less';
@import '../../../../common/mobile/resources/less/contextmenu.less';
@import './app-material.less';
@import './app-ios.less';
@import './icons-ios.less';

View file

@ -90,7 +90,7 @@ export default class MainPage extends Component {
!this.state.collaborationVisible ? null :
<Collaboration onclosed={this.handleOptionsViewClosed.bind(this, 'coauth')} />
}
{/*<ContextMenu />*/}
<ContextMenu />
</Page>
)
}

View file

@ -2,29 +2,18 @@ import React, { Component } from 'react';
import { Popover, List, ListItem, ListButton, Link, Icon } from 'framework7-react';
import { f7 } from 'framework7-react';
const buttons = [
{
text: 'Edit',
action: 'edit'
}, {
text: 'View',
action: 'view'
}, {
icon: 'icon-paste',
action: 'review'
}
];
class ContextMenuView extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
f7.popover.open('#idx-context-menu-popover', '#idx-context-menu-target');
// f7.popover.open('#idx-context-menu-popover', '#idx-context-menu-target');
}
render() {
const buttons = this.props.items || {};
return (
<Popover id="idx-context-menu-popover"
className="document-menu"
@ -36,9 +25,9 @@ class ContextMenuView extends Component {
<List className="list-block">
{buttons.map((b, index) =>
!!b.text ?
<ListButton className="asd" title={b.text} key={index} /> :
<ListButton className="asd" title={b.text} key={index} onClick={e => this.props.onMenuItemClick(b.action)} /> :
<ListButton className="asd" title={b.text} key={index}>
<Icon slot="media" icon={b.icon} />
<Icon slot="media" icon={`icon_mask ${b.icon}`} />
</ListButton>
)}
</List>