Merge branch 'feature/reactjs-container-component-with-mobx' into feature/mobile-apps-on-reactjs

# Conflicts:
#	apps/documenteditor/mobile/locale/en.json
#	apps/documenteditor/mobile/src/components/app.jsx
#	apps/documenteditor/mobile/src/components/settings/Settings.jsx
#	apps/documenteditor/mobile/src/components/settings/document-settings/DocumentSettings.jsx
#	apps/documenteditor/mobile/src/js/routes.js
#	apps/documenteditor/mobile/src/pages/home.jsx
#	apps/documenteditor/mobile/src/store/reducers/root.js
#	apps/documenteditor/mobile/src/store/store.js
This commit is contained in:
Maxim Kadushkin 2020-10-16 11:37:47 +03:00
commit b041083933
31 changed files with 1583 additions and 498 deletions

View file

@ -1,17 +1,17 @@
import React, { useState } from 'react'
import { resetUsers } from '../store/actions/actions.js'
import React, { Component } from 'react'
import Notifications from '../../utils/notifications.js'
import {observer, inject} from "mobx-react"
const Collaboration = () => {
const onChangeEditUsers = (users) => {
const store = Common.Store.get();
store.dispatch(resetUsers(Object.values(users)));
};
@inject('users')
class CollaborationController extends Component {
constructor(props){
super(props)
Common.Notifications.on('engineCreated', api => {
// this.api = api;
api.asc_registerCallback('asc_onAuthParticipantsChanged', onChangeEditUsers);
api.asc_registerCallback('asc_onParticipantsChanged', onChangeEditUsers);
api.asc_registerCallback('asc_onAuthParticipantsChanged', this.onChangeEditUsers.bind(this));
api.asc_registerCallback('asc_onParticipantsChanged', this.onChangeEditUsers.bind(this));
// this.api.asc_registerCallback('asc_onAddComment', _.bind(this.onApiAddComment, this));
// this.api.asc_registerCallback('asc_onAddComments', _.bind(this.onApiAddComments, this));
// this.api.asc_registerCallback('asc_onChangeCommentData', _.bind(this.onApiChangeCommentData, this));
@ -20,11 +20,16 @@ const Collaboration = () => {
// this.api.asc_registerCallback('asc_onShowComment', _.bind(this.apiShowComments, this));
// this.api.asc_registerCallback('asc_onHideComment', _.bind(this.apiHideComments, this));
});
return {
setApi(api) {
}
onChangeEditUsers(users) {
const storeUsers = this.props.users;
storeUsers.reset(users);
};
render() {
return null
}
};
export {Collaboration as CollaborationController}
export default CollaborationController;

View file

@ -1,8 +0,0 @@
export const RESET_USERS = 'RESET_USERS';
export const resetUsers = list => {
return {
type: RESET_USERS,
payload: list
}
};

View file

@ -1,11 +1,10 @@
import * as actionTypes from './actions/actions'
const usersReducer = (state = [], action) => {
if (action.type == actionTypes.RESET_USERS) {
return [...action.payload];
import {observable, action} from 'mobx';
export class storeUsers {
@observable users = []
@action reset(users) {
this.users = Object.values(users)
}
}
return state;
};
export default usersReducer

View file

@ -1,24 +1,34 @@
import React, { Component } from 'react';
import { useSelector } from 'react-redux';
import React, { Component, useEffect } from 'react';
import { observer, inject } from "mobx-react";
import { Popover, List, ListItem, Navbar, NavTitle, NavRight } from 'framework7-react';
import { Sheet, Toolbar, BlockTitle, Link, Page, View, Icon } from 'framework7-react';
import { f7 } from 'framework7-react';
import { withTranslation, useTranslation } from 'react-i18next';
const PageUsers = () => {
const { t } = useTranslation();
const userlist = useSelector(state => state.users);
@inject('users')
@observer
class PageUsers extends Component {
constructor(props){
super(props)
}
render() {
const { t } = this.props;
const userlist = this.props.users;
return (
<Page name="collab__users">
<Navbar title="Users" backLink="Back"></Navbar>
<BlockTitle>{t("Collaboration.textEditUser")}</BlockTitle>
<List className="coauth__list">
{userlist.map((model, i) => (
{userlist.users.map((model, i) => (
<ListItem title={model.asc_getUserName()} key={i}>
<Icon slot="media" icon="coauth__list__icon" style={{ backgroundColor:model.asc_getColor() }}></Icon>
<Icon slot="media" icon="coauth__list__icon"
style={{backgroundColor: model.asc_getColor()}}></Icon>
</ListItem>
))}
</List>
</Page>)
}
};
const PageCollaboration = () => {
@ -68,7 +78,7 @@ class CollaborationSheet extends Component {
}
render() {
return (
<Sheet className="collab__sheet" push>
<Sheet className="coauth__sheet" push onSheetClosed={e => this.props.onclosed()}>
<View>
<PageCollaboration />
</View>
@ -77,5 +87,25 @@ class CollaborationSheet extends Component {
}
}
const CollaborationView = props => {
useEffect(() => {
f7.sheet.open('.coauth__sheet');
return () => {
// component will unmount
}
});
const onviewclosed = () => {
if ( props.onclosed ) props.onclosed();
};
return (
<CollaborationSheet onclosed={onviewclosed} />
)
};
const pageusers = withTranslation()(PageUsers);
// export withTranslation()(CollaborationPopover);
export {CollaborationPopover, CollaborationSheet, PageCollaboration, PageUsers, }
export {CollaborationPopover, CollaborationSheet, PageCollaboration, pageusers as PageUsers}
export default CollaborationView;

View file

@ -0,0 +1,52 @@
.device-ios {
.popover__titled {
.popover-inner {
//border-radius: var(--f7-popover-border-radius);
> .view {
border-radius: var(--f7-popover-border-radius);
}
}
.navbar-bg {
//-webkit-backdrop-filter: none;
backdrop-filter: none;
}
.list:first-child {
li:first-child {
a {
border-radius: 0;
}
}
}
.list:last-child {
li:last-child {
a {
border-radius: 0;
}
&:after {
content: '';
position: absolute;
background-color: var(--f7-navbar-border-color, var(--f7-bars-border-color));
display: block;
//z-index: 15;
top: auto;
right: auto;
bottom: 0;
left: 0;
height: 1px;
width: 100%;
transform-origin: 50% 100%;
transform: scaleY(calc(1 / var(--f7-device-pixel-ratio)));
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
}
}
}
}

View file

@ -0,0 +1,12 @@
.device-android {
.popover__titled {
.list:last-child {
li:last-child {
a {
border-radius: 0;
}
}
}
}
}

View file

@ -0,0 +1,41 @@
import React from 'react';
import { f7 } from 'framework7-react';
class Device {
constructor(){
const ua = navigator.userAgent,
isMobile = /Mobile(\/|\s|;)/.test(ua);
this.isPhone = /(iPhone|iPod)/.test(ua) ||
(!/(Silk)/.test(ua) && (/(Android)/.test(ua) && (/(Android 2)/.test(ua) || isMobile))) ||
(/(BlackBerry|BB)/.test(ua) && isMobile) ||
/(Windows Phone)/.test(ua);
this.isTablet = !this.isPhone && (/iPad/.test(ua) || /Android/.test(ua) || /(RIM Tablet OS)/.test(ua) ||
(/MSIE 10/.test(ua) && /; Touch/.test(ua)));
}
get phone() {
return this.isPhone
}
get tablet() {
return this.isTablet
}
get sailfish() {
return /Sailfish/.test(navigator.userAgent) || /Jolla/.test(navigator.userAgent);
}
get android() {
return f7.device.android;
}
get ios() {
return f7.device.ios;
}
}
const device = new Device();
export {device as Device};

View file

@ -19,5 +19,40 @@
},
"Collaboration": {
"textEditUser": "Users who are editing the file:"
},
"Edit": {
"textClose": "Close",
"textBack": "Back",
"textText": "Text",
"textParagraph": "Paragraph",
"textTable": "Table",
"textFooter": "Footer",
"textHeader": "Header",
"textShape": "Shape",
"textImage": "Image",
"textChart": "Chart",
"textHyperlink": "Hyperlink",
"textSelectObjectToEdit": "Select object to edit",
"textSettings": "Settings",
"textFontColor": "Font Color",
"textHighlightColor": "Highlight Color",
"textAdditionalFormatting": "Additional Formatting",
"textAdditional": "Additional",
"textBullets": "Bullets",
"textNumbers": "Numbers",
"textLineSpacing": "Line Spacing",
"textFonts": "Fonts",
"textAuto": "Auto",
"textPt": "pt",
"textSize": "Size",
"textAuto": "Auto",
"textStrikethrough": "Strikethrough",
"textDoubleStrikethrough": "Double Strikethrough",
"textSuperscript": "Superscript",
"textSubscript": "Subscript",
"textSmallCaps": "Small Caps",
"textAllCaps": "All Caps",
"textLetterSpacing": "Letter Spacing",
"textNone": "None"
}
}

View file

@ -1,19 +1,15 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { App, Panel, View, Popup, Page, Navbar, NavRight, Link, Block, BlockTitle, List, ListItem } from 'framework7-react';
import i18n from '../js/i18n';
import {App,Panel,Views,View,Popup,Page,Navbar,Toolbar,NavRight,Link,Block,BlockTitle,List,ListItem,ListInput,ListButton,BlockFooter} from 'framework7-react';
import routes from '../js/routes';
import { initApi } from '../store/actions/actions';
import '../../../../common/Gateway.js';
import '../../../../common/main/lib/util/utils.js';
import { CollaborationController } from '../../../../common/mobile/lib/controller/Collaboration.jsx';
import Notifications from '../../../../common/mobile/utils/notifications.js'
import MainController from '../controller/Main';
class ComponentApp extends React.Component {
export default class extends React.Component {
constructor() {
super();
@ -23,20 +19,12 @@ class ComponentApp extends React.Component {
name: 'Desktop Editor', // App name
theme: 'auto', // Automatic theme detection
// App routes
routes: routes,
},
// Login screen demo data
username: '',
password: '',
}
Common.Notifications = new Notifications();
Common.Controllers = {};
Common.Controllers.Collaboration = new CollaborationController();
}
render() {
return (
@ -70,6 +58,7 @@ class ComponentApp extends React.Component {
{/* Your main view, should have "view-main" class */}
<View main className="safe-areas" url="/" />
<MainController ref="mainController" />
{/* Popup */}
<Popup id="my-popup">
@ -89,168 +78,12 @@ class ComponentApp extends React.Component {
</App>
)
}
alertLoginData() {
this.$f7.dialog.alert('Username: ' + this.state.username + '<br>Password: ' + this.state.password, () => {
this.$f7.loginScreen.close();
});
}
componentDidMount() {
this.$f7ready((f7) => {
// Call F7 APIs here
});
const script = document.createElement("script");
script.src = "../../../../sdkjs/develop/sdkjs/word/scripts.js";
script.async = true;
script.onload = () => {
let dep_scripts = ['../../../vendor/xregexp/xregexp-all-min.js',
'../../../vendor/sockjs/sockjs.min.js',
'../../../vendor/jszip/jszip.min.js',
'../../../vendor/jszip-utils/jszip-utils.min.js'];
dep_scripts.push(...sdk_scripts);
const promise_get_script = (scriptpath) => {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = scriptpath;
script.onload = () => {
console.log('loaded ' + scriptpath);
resolve('ok');
};
script.onerror = () => {
console.log('error ' + scriptpath);
reject('error');
};
document.body.appendChild(script);
});
};
const loadConfig = data => {
let me = this;
console.log('load config');
me.editorConfig = Object.assign({}, this.editorConfig, data.config);
me.appOptions.user = Common.Utils.fillUserInfo(me.editorConfig.user, me.editorConfig.lang, "Local.User"/*me.textAnonymous*/);
};
const loadDocument = data => {
this.permissions = {};
this.document = data.doc;
let docInfo = {};
if (data.doc) {
this.permissions = Object.assign(this.permissions, data.doc.permissions);
let _permissions = Object.assign({}, data.doc.permissions),
_user = new Asc.asc_CUserInfo();
_user.put_Id(this.appOptions.user.id);
_user.put_FullName(this.appOptions.user.fullname);
docInfo = new Asc.asc_CDocInfo();
docInfo.put_Id(data.doc.key);
docInfo.put_Url(data.doc.url);
docInfo.put_Title(data.doc.title);
docInfo.put_Format(data.doc.fileType);
docInfo.put_VKey(data.doc.vkey);
docInfo.put_Options(data.doc.options);
docInfo.put_UserInfo(_user);
docInfo.put_CallbackUrl(this.editorConfig.callbackUrl);
docInfo.put_Token(data.doc.token);
docInfo.put_Permissions(_permissions);
docInfo.put_EncryptedInfo(this.editorConfig.encryptionKeys);
// var enable = !this.editorConfig.customization || (this.editorConfig.customization.macros!==false);
// docInfo.asc_putIsEnabledMacroses(!!enable);
// enable = !this.editorConfig.customization || (this.editorConfig.customization.plugins!==false);
// docInfo.asc_putIsEnabledPlugins(!!enable);
// let type = /^(?:(pdf|djvu|xps))$/.exec(data.doc.fileType);
// if (type && typeof type[1] === 'string') {
// this.permissions.edit = this.permissions.review = false;
// }
}
this.api.asc_registerCallback('asc_onGetEditorPermissions', onEditorPermissions);
// this.api.asc_registerCallback('asc_onLicenseChanged', _.bind(this.onLicenseChanged, this));
// this.api.asc_registerCallback('asc_onRunAutostartMacroses', _.bind(this.onRunAutostartMacroses, this));
this.api.asc_setDocInfo(docInfo);
this.api.asc_getEditorPermissions(this.editorConfig.licenseUrl, this.editorConfig.customerId);
// Common.SharedSettings.set('document', data.doc);
// if (data.doc) {
// DE.getController('Toolbar').setDocumentTitle(data.doc.title);
// if (data.doc.info) {
// data.doc.info.author && console.log("Obsolete: The 'author' parameter of the document 'info' section is deprecated. Please use 'owner' instead.");
// data.doc.info.created && console.log("Obsolete: The 'created' parameter of the document 'info' section is deprecated. Please use 'uploaded' instead.");
// }
// }
};
const onEditorPermissions = params => {
let me = this;
const licType = params.asc_getLicenseType();
me.appOptions.canLicense = (licType === Asc.c_oLicenseResult.Success || licType === Asc.c_oLicenseResult.SuccessLimit);
// me.appOptions.canEdit = (me.permissions.edit !== false || me.permissions.review === true) && // can edit or review
// (me.editorConfig.canRequestEditRights || me.editorConfig.mode !== 'view') && // if mode=="view" -> canRequestEditRights must be defined
// (!me.appOptions.isReviewOnly || me.appOptions.canLicense) && // if isReviewOnly==true -> canLicense must be true
// me.isSupportEditFeature();
// me.appOptions.isEdit = me.appOptions.canLicense && me.appOptions.canEdit && me.editorConfig.mode !== 'view';
// me.api.asc_setViewMode(!me.appOptions.isEdit);
me.api.asc_setViewMode(false);
me.api.asc_LoadDocument();
me.api.Resize();
};
const _process_array = (array, fn) => {
let results = [];
return array.reduce(function(p, item) {
return p.then(function() {
return fn(item).then(function(data) {
results.push(data);
return results;
});
});
}, Promise.resolve());
};
_process_array(dep_scripts, promise_get_script)
.then ( result => {
this.api = new Asc.asc_docs_api({
'id-view' : 'editor_sdk',
'mobile' : true,
// 'translate': translate
});
this.appOptions = {};
Common.Gateway.on('init', loadConfig);
// Common.Gateway.on('showmessage', _.bind(me.onExternalMessage, me));
Common.Gateway.on('opendocument', loadDocument);
Common.Gateway.appReady();
Common.Notifications.trigger('engineCreated', this.api);
const { initApi } = this.props;
initApi(this.api);
}, error => {
console.log('promise failed ' + error);
});
};
script.onerror = () => {
console.log('error');
};
document.body.appendChild(script);
this.refs.mainController.initSdk();
}
}
const mapDispatchToProps = dispatch => bindActionCreators({
initApi
}, dispatch);
export default connect(undefined, mapDispatchToProps)(ComponentApp);

View file

@ -1,57 +1,162 @@
import React, {Component} from 'react';
import {
Page,
Navbar,
NavRight,
NavLeft,
Link,
Popup,
Tabs,
Tab
} from 'framework7-react';
import EditText from "./EditText";
import React, {useState, useEffect} from 'react';
import {observer, inject} from "mobx-react";
import { Page, Navbar, NavRight, NavLeft, NavTitle, Link, Sheet, Tabs, Tab, View } from 'framework7-react';
import { f7 } from 'framework7-react';
import { useTranslation } from 'react-i18next';
import EditTextController from "./controller/EditText";
import EditParagraph from "./EditParagraph";
export default class EditContainer extends Component {
constructor(props) {
super(props);
this.state = {
popupOpened: false,
};
}
render() {
const editors = ['text', 'paragraph'];//, 'table', 'header', 'shape', 'image', 'chart', 'hyperlink'];
const tabLinks = editors.map((item, index) =>
<Link key={"de-tablink-" + item} tabLink={"#" + item} tabLinkActive={index === 0}>{item}</Link>
);
const tabs = editors.map((item, index) =>
<Tab key={"de-tab-" + item} id={item} className="page-content" tabActive={index === 0}>
{item === 'text' && <EditText />}
{item === 'paragraph' && <EditParagraph />}
{/*{item === 'table' && <EditTable />}
{item === 'header' && <EditHeader />}
{item === 'shape' && <EditShape />}
{item === 'image' && <EditImage />}
{item === 'chart' && <EditChart />}
{item === 'hyperlink' && <EditHyperlink />}*/}
</Tab>
);
const EmptyEditLayout = () => {
const { t } = useTranslation();
return (
<Page>
<div className="content-block inset">
<div className="content-block-inner">
<p>{t("Edit.textSelectObjectToEdit")}</p>
</div>
</div>
</Page>
)
};
const EditLayoutNavbar = ({ editors }) => {
const { t } = useTranslation();
return (
<Popup className="edit-popup" opened={this.state.popupOpened} onPopupClosed={() => this.setState({popupOpened : false})}>
<Page pageContent={false}>
<Navbar>
{
editors.length > 1 ?
<NavLeft tabbar>
{tabLinks}
</NavLeft>
{editors.map((item, index) => <Link key={"de-link-" + item.id} tabLink={"#" + item.id} tabLinkActive={index === 0}>{item.caption}</Link>)}
</NavLeft> :
<NavTitle>{ editors[0].caption }</NavTitle>
}
<NavRight>
<Link popupClose=".edit-popup">Close</Link>
<Link sheetClose>{t("Edit.textClose")}</Link>
</NavRight>
</Navbar>
)
};
const EditLayoutContent = ({ editors }) => {
if (editors.length > 1) {
return (
<Tabs animated>
{tabs}
{editors.map((item, index) =>
<Tab key={"de-tab-" + item.id} id={item.id} className="page-content" tabActive={index === 0}>
{item.component}
</Tab>
)}
</Tabs>
)
} else {
return (
<Page>
{editors[0].component}
</Page>
</Popup>
)
}
};
const EditSheet = props => {
const { t } = useTranslation();
const settings = props.storeFocusObjects.settings;
const headerType = props.storeFocusObjects.headerType;
let editors = [];
if (settings.length < 1) {
editors.push({
caption: t("Edit.textSettings"),
component: <EmptyEditLayout />
});
} else {
if (settings.indexOf('text') > -1) {
editors.push({
caption: t("Edit.textText"),
id: 'edit-text',
component: <EditTextController />
})
}
if (settings.indexOf('paragraph') > -1) {
editors.push({
caption: t("Edit.textParagraph"),
id: 'edit-paragraph',
component: <EditParagraph />
})
}
/*if (settings.indexOf('table') > -1) {
editors.push({
caption: t("Edit.textTable"),
id: 'edit-table',
component: <EditTable />
})
}
if (settings.indexOf('header') > -1) {
editors.push({
caption: headerType==2 ? t("Edit.textFooter") : t("Edit.textHeader"),
id: 'edit-header',
component: <EditHeader />
})
}
if (settings.indexOf('shape') > -1) {
editors.push({
caption: t("Edit.textShape"),
id: 'edit-shape',
component: <EditShape />
})
}
if (settings.indexOf('image') > -1) {
editors.push({
caption: t("Edit.textImage"),
id: 'edit-image',
component: <EditImage />
})
}
if (settings.indexOf('chart') > -1) {
editors.push({
caption: t("Edit.textChart"),
id: 'edit-chart',
component: <EditChart />
})
}
if (settings.indexOf('hyperlink') > -1) {
editors.push({
caption: t("Edit.textHyperlink"),
id: 'edit-link',
component: <EditHyperlink />
})
}*/
}
return (
<Sheet className="edit__sheet" push onSheetClosed={e => props.onclosed()}>
<View>
<Page pageContent={false}>
<EditLayoutNavbar editors={editors} />
<EditLayoutContent editors={editors} />
</Page>
</View>
</Sheet>
)
};
const HOC_EditSheet = inject("storeFocusObjects")(observer(EditSheet));
const EditOptions = props => {
useEffect(() => {
f7.sheet.open('.edit__sheet');
return () => {
// component will unmount
}
});
const onsheetclosed = () => {
if ( props.onclosed ) props.onclosed();
};
return (
<HOC_EditSheet onclosed={onsheetclosed} />
)
};
export default EditOptions;

View file

@ -1,96 +1,301 @@
import React, {Component, Fragment} from 'react';
import {
List,
ListItem,
Icon,
Row,
Col,
Button
} from 'framework7-react';
import React, {Fragment, useState} from 'react';
import {observer, inject} from "mobx-react";
import {List, ListItem, Icon, Row, Col, Button, Page, Navbar, Segmented, BlockTitle} from 'framework7-react';
import { useTranslation } from 'react-i18next';
export default class EditText extends Component {
constructor(props) {
super(props);
const PageFonts = props => {
const { t } = useTranslation();
const storeTextSettings = props.storeTextSettings;
const size = storeTextSettings.fontSize;
const displaySize = typeof size === 'undefined' ? t('Edit.textAuto') : size + ' ' + t('Edit.textPt');
const curFontName = storeTextSettings.fontName;
const fonts = storeTextSettings.fontsArray;
const [vlFonts, setVlFonts] = useState({
vlData: {
items: [],
}
render() {
const textFontColor = "Font Color";
const textHighlightColor = "Highlight Color";
const textAdditionalFormatting = "Additional Formatting";
const textBullets = "Bullets";
const textNumbers = "Numbers";
const textLineSpacing = "Line Spacing";
});
const renderExternal = (vl, vlData) => {
setVlFonts((prevState) => {
let fonts = [...prevState.vlData.items];
fonts.splice(vlData.fromIndex, vlData.toIndex, ...vlData.items);
return {vlData: {
items: fonts,
}}
});
};
return (
<Page>
<Navbar title={t('Edit.textFonts')} backLink={t('Edit.textBack')} />
<List>
<ListItem title={t('Edit.textSize')}>
<div slot='after-start'>{displaySize}</div>
<div slot='after'>
<Segmented>
<Button outline className='decrement' onClick={() => {props.changeFontSize(size, true)}}> - </Button>
<Button outline className='increment' onClick={() => {props.changeFontSize(size, false)}}> + </Button>
</Segmented>
</div>
</ListItem>
</List>
<BlockTitle>{t('Edit.textFonts')}</BlockTitle>
<List virtualList virtualListParams={{
items: fonts,
renderExternal: renderExternal
}}>
<ul>
{vlFonts.vlData.items.map((item, index) => (
<ListItem
key={index}
radio
checked={curFontName === item.name}
title={item.name}
style={{fontFamily: `${item.name}`}}
onClick={() => {props.changeFontFamily(item.name)}}
></ListItem>
))}
</ul>
</List>
</Page>
)
};
const fontName = 'Arial';
const fontSize = '11pt';
const PageAdditionalFormatting = props => {
const { t } = useTranslation();
const storeTextSettings = props.storeTextSettings;
const storeFocusObjects = props.storeFocusObjects;
const paragraph = storeFocusObjects.paragraphObject;
const isStrikeout = paragraph.get_Strikeout();
const isDStrikeout = paragraph.get_DStrikeout();
const isSuperscript = storeTextSettings.isSuperscript;
const isSubscript = storeTextSettings.isSubscript;
const isSmallCaps = paragraph.get_SmallCaps();
const isAllCaps = paragraph.get_AllCaps();
const letterSpacing = Common.Utils.Metric.fnRecalcFromMM(paragraph.get_TextSpacing());
return(
<Page>
<Navbar title={t('Edit.textAdditional')} backLink={t('Edit.textBack')} />
<List>
<ListItem title={t('Edit.textStrikethrough')} radio checked={isStrikeout} onClick={() => {props.onAdditionalStrikethrough('strikeout', !isStrikeout)}}/>
<ListItem title={t('Edit.textDoubleStrikethrough')} radio checked={isDStrikeout} onClick={() => {props.onAdditionalStrikethrough('dbStrikeout', !isDStrikeout)}}/>
<ListItem title={t('Edit.textSuperscript')} radio checked={isSuperscript} onClick={() => {props.onAdditionalScript('superscript', !isSuperscript)}}/>
<ListItem title={t('Edit.textSubscript')} radio checked={isSubscript} onClick={() => {props.onAdditionalScript('subscript', !isSubscript)}}/>
<ListItem title={t('Edit.textSmallCaps')} radio checked={isSmallCaps} onClick={() => {props.onAdditionalCaps('small', !isSmallCaps)}}/>
<ListItem title={t('Edit.textAllCaps')} radio checked={isAllCaps} onClick={() => {props.onAdditionalCaps('all', !isAllCaps)}}/>
</List>
<List>
<ListItem title={t('Edit.textLetterSpacing')}>
<div slot='after-start'>{letterSpacing + ' ' + Common.Utils.Metric.getCurrentMetricName()}</div>
<div slot='after'>
<Segmented>
<Button outline className='decrement' onClick={() => {props.changeLetterSpacing(letterSpacing, true)}}> - </Button>
<Button outline className='increment' onClick={() => {props.changeLetterSpacing(letterSpacing, false)}}> + </Button>
</Segmented>
</div>
</ListItem>
</List>
</Page>
)
};
import bulletImg1 from '../../../resources/img/bullets/bullet-01.png';
import bulletImg2 from '../../../resources/img/bullets/bullet-02.png';
import bulletImg3 from '../../../resources/img/bullets/bullet-03.png';
import bulletImg4 from '../../../resources/img/bullets/bullet-04.png';
import bulletImg5 from '../../../resources/img/bullets/bullet-05.png';
import bulletImg6 from '../../../resources/img/bullets/bullet-06.png';
import bulletImg7 from '../../../resources/img/bullets/bullet-07.png';
const PageBullets = props => {
const { t } = useTranslation();
const bulletArrays = [
[
{type: -1, thumb: ''},
{type: 1, thumb: bulletImg1},
{type: 2, thumb: bulletImg2},
{type: 3, thumb: bulletImg3}
],
[
{type: 4, thumb: bulletImg4},
{type: 5, thumb: bulletImg5},
{type: 6, thumb: bulletImg6},
{type: 7, thumb: bulletImg7}
]
];
const storeTextSettings = props.storeTextSettings;
const typeBullets = storeTextSettings.typeBullets;
return(
<Page>
<Navbar title={t('Edit.textBullets')} backLink={t('Edit.textBack')} />
{bulletArrays.map((bullets, index) => (
<ul className="row" style={{listStyle: 'none'}} key={'bullets-' + index}>
{bullets.map((bullet) => (
<li key={'bullet-' + bullet.type} data-type={bullet.type} className={bullet.type === typeBullets ? 'active' : ''} onClick={() => {props.onBullet(bullet.type)}}>
{bullet.thumb.length < 1 ?
<div className="thumb" style={{position: 'relative'}}>
<label>{t('Edit.textNone')}</label>
</div> :
<div className="thumb" style={{height: '70px', width: '70px', backgroundImage: `url("${bullet.thumb}")`, backgroundRepeat: 'no-repeat', backgroundPosition: 'center'}}></div>
}
</li>
))}
</ul>
))}
</Page>
)
};
import numberImg1 from '../../../resources/img/numbers/number-01.png';
import numberImg2 from '../../../resources/img/numbers/number-02.png';
import numberImg3 from '../../../resources/img/numbers/number-03.png';
import numberImg4 from '../../../resources/img/numbers/number-04.png';
import numberImg5 from '../../../resources/img/numbers/number-05.png';
import numberImg6 from '../../../resources/img/numbers/number-06.png';
import numberImg7 from '../../../resources/img/numbers/number-07.png';
const PageNumbers = props => {
const { t } = useTranslation();
const numberArrays = [
[
{type: -1, thumb: ''},
{type: 4, thumb: numberImg1},
{type: 5, thumb: numberImg2},
{type: 6, thumb: numberImg3}
],
[
{type: 1, thumb: numberImg4},
{type: 2, thumb: numberImg5},
{type: 3, thumb: numberImg6},
{type: 7, thumb: numberImg7}
]
];
const storeTextSettings = props.storeTextSettings;
const typeNumbers = storeTextSettings.typeNumbers;
return(
<Page>
<Navbar title={t('Edit.textNumbers')} backLink={t('Edit.textBack')} />
{numberArrays.map((numbers, index) => (
<ul className="row" style={{listStyle: 'none'}} key={'numbers-' + index}>
{numbers.map((number) => (
<li key={'number-' + number.type} data-type={number.type} className={number.type === typeNumbers ? 'active' : ''} onClick={() => {props.onNumber(number.type)}}>
{number.thumb.length < 1 ?
<div className="thumb" style={{position: 'relative'}}>
<label>{t('Edit.textNone')}</label>
</div> :
<div className="thumb" style={{height: '70px', width: '70px', backgroundImage: `url("${number.thumb}")`, backgroundRepeat: 'no-repeat', backgroundPosition: 'center'}}></div>
}
</li>
))}
</ul>
))}
</Page>
)
};
const PageLineSpacing = props => {
const { t } = useTranslation();
const storeTextSettings = props.storeTextSettings;
const lineSpacing = storeTextSettings.lineSpacing;
return(
<Page>
<Navbar title={t('Edit.textLineSpacing')} backLink={t('Edit.textBack')} />
<List>
<ListItem radio checked={lineSpacing === 1.0} title={1.0} onClick={() => {props.onLineSpacing(1.0)}}></ListItem>
<ListItem radio checked={lineSpacing === 1.15} title={1.15} onClick={() => {props.onLineSpacing(1.15)}}></ListItem>
<ListItem radio checked={lineSpacing === 1.5} title={1.5} onClick={() => {props.onLineSpacing(1.5)}}></ListItem>
<ListItem radio checked={lineSpacing === 2.0} title={2.0} onClick={() => {props.onLineSpacing(2.0)}}></ListItem>
<ListItem radio checked={lineSpacing === 2.5} title={2.5} onClick={() => {props.onLineSpacing(2.5)}}></ListItem>
<ListItem radio checked={lineSpacing === 3.0} title={3.0} onClick={() => {props.onLineSpacing(3.0)}}></ListItem>
</List>
</Page>
)
};
const EditText = props => {
const { t } = useTranslation();
const storeTextSettings = props.storeTextSettings;
const fontName = storeTextSettings.fontName || t('Edit.textFonts');
const fontSize = storeTextSettings.fontSize;
const displaySize = typeof fontSize === 'undefined' ? t('Edit.textAuto') : fontSize + ' ' + t('Edit.textPt');
const isBold = storeTextSettings.isBold;
const isItalic = storeTextSettings.isItalic;
const isUnderline = storeTextSettings.isUnderline;
const isStrikethrough = storeTextSettings.Strikethrough;
const paragraphAlign = storeTextSettings.paragraphAlign;
return (
<Fragment>
<List>
<ListItem title={fontName} link="#" after={fontSize}></ListItem>
<ListItem title={fontName} link="/edit-text-fonts/" after={displaySize} routeProps={{
changeFontSize: props.changeFontSize,
changeFontFamily: props.changeFontFamily
}}/>
<ListItem>
<Row>
<Col>
<Button>Button</Button>
</Col>
<Col>
<Button>Button</Button>
</Col>
<Col>
<Button>Button</Button>
</Col>
<Col>
<Button>Button</Button>
</Col>
<a className={'button' + (isBold ? ' active' : '')} onClick={() => { props.toggleBold(!isBold)}}><b>B</b></a>
<a className={'button' + (isItalic ? ' active' : '')} onClick={() => {props.toggleItalic(!isItalic)}}><i>I</i></a>
<a className={'button' + (isUnderline ? ' active' : '')} onClick={() => {props.toggleUnderline(!isUnderline)}} style={{textDecoration: "underline"}}>U</a>
<a className={'button' + (isStrikethrough ? ' active' : '')} onClick={() => {props.toggleStrikethrough(!isStrikethrough)}} style={{textDecoration: "line-through"}}>S</a>
</Row>
</ListItem>
<ListItem title={textFontColor} link="#">
<ListItem title={t("Edit.textFontColor")} link="#">
<Icon slot="media" icon="icon-text-color"></Icon>
<span className="color-preview"></span>
</ListItem>
<ListItem title={textHighlightColor} link="#">
<ListItem title={t("Edit.textHighlightColor")} link="#">
<Icon slot="media" icon="icon-text-selection"></Icon>
</ListItem>
<ListItem title={textAdditionalFormatting} link="#">
<ListItem title={t("Edit.textAdditionalFormatting")} link="/edit-text-add-formatting/" routeProps={{
onAdditionalStrikethrough: props.onAdditionalStrikethrough,
onAdditionalCaps: props.onAdditionalCaps,
onAdditionalScript: props.onAdditionalScript,
changeLetterSpacing: props.changeLetterSpacing
}}>
<Icon slot="media" icon="icon-text-additional"></Icon>
</ListItem>
</List>
<List>
<ListItem>
<Row>
<Col>
<Button>Button</Button>
</Col>
<Col>
<Button>Button</Button>
</Col>
<Col>
<Button>Button</Button>
</Col>
<Col>
<Button>Button</Button>
</Col>
<a className={'button' + (paragraphAlign === 'left' ? ' active' : '')} onClick={() => {props.onParagraphAlign('left')}}>left</a>
<a className={'button' + (paragraphAlign === 'center' ? ' active' : '')} onClick={() => {props.onParagraphAlign('center')}}>center</a>
<a className={'button' + (paragraphAlign === 'right' ? ' active' : '')} onClick={() => {props.onParagraphAlign('right')}}>right</a>
<a className={'button' + (paragraphAlign === 'just' ? ' active' : '')} onClick={() => {props.onParagraphAlign('just')}}>just</a>
</Row>
</ListItem>
<ListItem>
<Row>
<Col>
<Button>Button</Button>
</Col>
<Col>
<Button>Button</Button>
</Col>
<a className='button' onClick={() => {props.onParagraphMove(true)}}>moveleft</a>
<a className='button' onClick={() => {props.onParagraphMove(false)}}>moveright</a>
</Row>
</ListItem>
<ListItem title={textBullets} link="#">
<ListItem title={t("Edit.textBullets")} link='/edit-text-bullets/' routeProps={{
onBullet: props.onBullet
}}>
<Icon slot="media" icon="icon-bullets"></Icon>
</ListItem>
<ListItem title={textNumbers} link="#">
<ListItem title={t("Edit.textNumbers")} link='/edit-text-numbers/' routeProps={{
onNumber: props.onNumber
}}>
<Icon slot="media" icon="icon-numbers"></Icon>
</ListItem>
<ListItem title={textLineSpacing} link="#">
<ListItem title={t("Edit.textLineSpacing")} link='/edit-text-line-spacing/' routeProps={{
onLineSpacing: props.onLineSpacing
}}>
<Icon slot="media" icon="icon-linespacing"></Icon>
</ListItem>
</List>
</Fragment>
)
}
};
const EditTextContainer = inject("storeTextSettings", "storeFocusObjects")(observer(EditText));
const PageFontsContainer = inject("storeTextSettings", "storeFocusObjects")(observer(PageFonts));
const PageAddFormattingContainer = inject("storeTextSettings", "storeFocusObjects")(observer(PageAdditionalFormatting));
const PageBulletsContainer = inject("storeTextSettings")(observer(PageBullets));
const PageNumbersContainer = inject("storeTextSettings")(observer(PageNumbers));
const PageLineSpacingContainer = inject("storeTextSettings")(observer(PageLineSpacing));
export {EditTextContainer as EditText,
PageFontsContainer as PageFonts,
PageAddFormattingContainer as PageAdditionalFormatting,
PageBulletsContainer as PageBullets,
PageNumbersContainer as PageNumbers,
PageLineSpacingContainer as PageLineSpacing};

View file

@ -0,0 +1,195 @@
import React, {Component} from 'react';
import { EditText } from '../EditText'
class EditTextController extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const api = Common.EditorApi.get();
api && api.UpdateInterfaceState();
}
changeFontSize(curSize, isDecrement) {
const api = Common.EditorApi.get();
if (api) {
let size = curSize;
if (isDecrement) {
typeof size === 'undefined' ? api.FontSizeOut() : size = Math.max(1, --size);
} else {
typeof size === 'undefined' ? api.FontSizeIn : size = Math.min(100, ++size);
}
if (typeof size !== 'undefined') {
api.put_TextPrFontSize(size);
}
}
}
changeFontFamily(name) {
const api = Common.EditorApi.get();
if (api && name) {
api.put_TextPrFontName(name);
}
}
toggleBold(value) {
const api = Common.EditorApi.get();
if (api) {
api.put_TextPrBold(value);
}
}
toggleItalic(value) {
const api = Common.EditorApi.get();
if (api) {
api.put_TextPrItalic(value);
}
}
toggleUnderline(value) {
const api = Common.EditorApi.get();
if (api) {
api.put_TextPrUnderline(value);
}
}
toggleStrikethrough(value) {
const api = Common.EditorApi.get();
if (api) {
api.put_TextPrStrikeout(value);
}
}
// Additional
onAdditionalStrikethrough(type, value) {
const api = Common.EditorApi.get();
if (api) {
if ('strikeout' === type) {
api.put_TextPrStrikeout(value);
} else {
api.put_TextPrDStrikeout(value);
}
}
}
onAdditionalCaps(type, value) {
const api = Common.EditorApi.get();
if (api) {
const paragraphProps = new Asc.asc_CParagraphProperty();
if ('small' === type) {
paragraphProps.put_AllCaps(false);
paragraphProps.put_SmallCaps(value);
} else {
paragraphProps.put_AllCaps(value);
paragraphProps.put_SmallCaps(false);
}
api.paraApply(paragraphProps);
}
}
onAdditionalScript(type, value) {
const api = Common.EditorApi.get();
if (api) {
if ('superscript' === type) {
api.put_TextPrBaseline(value ? 1 : 0);
} else {
api.put_TextPrBaseline(value ? 2 : 0);
}
}
}
changeLetterSpacing(curSpacing, isDecrement) {
const api = Common.EditorApi.get();
if (api) {
let spacing = curSpacing;
if (isDecrement) {
spacing = Math.max(-100, --spacing);
} else {
spacing = Math.min(100, ++spacing);
}
const properties = new Asc.asc_CParagraphProperty();
properties.put_TextSpacing(Common.Utils.Metric.fnRecalcToMM(spacing));
api.paraApply(properties);
}
}
onParagraphAlign(type) {
const api = Common.EditorApi.get();
if (api) {
let value;
switch (type) {
case 'just':
value = 3;
break;
case 'right':
value = 0;
break;
case 'center':
value = 2;
break;
default:
value = 1;
break;
}
api.put_PrAlign(value);
}
}
onParagraphMove(isLeft) {
const api = Common.EditorApi.get();
if (api) {
if (isLeft) {
api.DecreaseIndent();
} else {
api.IncreaseIndent();
}
}
}
onBullet(type) {
const api = Common.EditorApi.get();
if (api) {
api.put_ListType(0, parseInt(type));
}
}
onNumber(type) {
const api = Common.EditorApi.get();
if (api) {
api.put_ListType(1, parseInt(type));
}
}
onLineSpacing(value) {
const api = Common.EditorApi.get();
if (api) {
const LINERULE_AUTO = 1;
api.put_PrLineSpacing(LINERULE_AUTO, value);
}
}
render() {
return (
<EditText changeFontSize={this.changeFontSize}
changeFontFamily={this.changeFontFamily}
toggleBold={this.toggleBold}
toggleItalic={this.toggleItalic}
toggleUnderline={this.toggleUnderline}
toggleStrikethrough={this.toggleStrikethrough}
onAdditionalStrikethrough={this.onAdditionalStrikethrough}
onAdditionalCaps={this.onAdditionalCaps}
onAdditionalScript={this.onAdditionalScript}
changeLetterSpacing={this.changeLetterSpacing}
onParagraphAlign={this.onParagraphAlign}
onParagraphMove={this.onParagraphMove}
onBullet={this.onBullet}
onNumber={this.onNumber}
onLineSpacing={this.onLineSpacing}
/>
)
}
}
export default EditTextController;

View file

@ -1,39 +1,65 @@
import React, { Component } from 'react';
import { View, Page, Navbar, NavRight, Link, Popup, Icon, ListItem, List } from 'framework7-react';
import React, {Component, useEffect} from 'react';
import {View,Page,Navbar,NavRight,Link,Popup,Popover,Icon,ListItem,List} from 'framework7-react';
import { withTranslation } from 'react-i18next';
import {changePageOrient} from "../../store/actions/actions";
import {f7} from 'framework7-react';
import {Device} from '../../../../../common/mobile/utils/device'
class Settings extends Component {
constructor(props) {
super(props);
this.state = {
popupOpened: false,
import DocumentSettingsController from "../settings/controller/DocumentSettings.jsx";
import Margins from "../settings/document-settings/Margins.jsx";
import DocumentFormats from "../settings/document-settings/DocumentFormats.jsx";
const routes = [
{
path: '/',
component: 'TSettingsView'
},
{
path: '/document-settings/',
component: DocumentSettingsController,
},
{
path: '/margins/',
component: Margins,
},
{
path: '/document-formats/',
component: DocumentFormats,
},
];
const SettingsList = withTranslation()(props => {
const {t} = props;
const _t = t('ViewSettings', {returnObjects: true});
const navbar = <Navbar title={t('ViewSettings.textSettings')}>
{!props.inPopover && <NavRight><Link popupClose=".settings-popup">{t('ViewSettings.textDone')}</Link></NavRight>}
</Navbar>;
const onoptionclick = page => {
if ( props.onOptionClick )
props.onOptionClick(page)
};
}
render() {
const { t } = this.props;
const _trarr = t('ViewSettings', {returnObjects: true});
useEffect(() => {
});
return (
<Popup className="settings-popup" opened={this.state.popupOpened} onPopupClosed={() => this.setState({popupOpened : false})}>
<View>
<View style={props.style} stackPages={true} routes={routes}>
<Page>
<Navbar title={t('ViewSettings.textSettings')}>
<NavRight>
<Link popupClose=".settings-popup">{t('ViewSettings.textDone')}</Link>
</NavRight>
</Navbar>
{navbar}
<List>
<ListItem title={_trarr.textFindAndReplace}>
{!props.inPopover &&
<ListItem title={_t.textFindAndReplace}>
<Icon slot="media" icon="icon-search"></Icon>
</ListItem>
<ListItem title={_trarr.textDocumentSettings} link="/document-settings/">
}
<ListItem link="#" title={_t.textDocumentSettings} onClick={onoptionclick.bind(this, '/document-settings/')}>
<Icon slot="media" icon="icon-doc-setup"></Icon>
</ListItem>
<ListItem title={_trarr.textApplicationSettings} link="#">
<ListItem title={_t.textApplicationSettings} link="#">
<Icon slot="media" icon="icon-app-settings"></Icon>
</ListItem>
<ListItem title={_trarr.textDownload} link="#">
<ListItem title={_t.textDownload} link="#">
<Icon slot="media" icon="icon-download"></Icon>
</ListItem>
<ListItem title={t('ViewSettings.textPrint')}>
@ -51,9 +77,56 @@ class Settings extends Component {
</List>
</Page>
</View>
)
});
class SettingsView extends Component {
constructor(props) {
super(props)
this.onoptionclick = this.onoptionclick.bind(this);
}
onoptionclick(page){
this.$f7.views.current.router.navigate(page);
}
render() {
const show_popover = this.props.usePopover;
return (
show_popover ?
<Popover id="settings-popover" className="popover__titled" onPopoverClosed={() => this.props.onclosed()}>
<SettingsList inPopover={true} onOptionClick={this.onoptionclick} style={{height: '410px'}} />
</Popover> :
<Popup className="settings-popup" onPopupClosed={() => this.props.onclosed()}>
<SettingsList onOptionClick={this.onoptionclick} />
</Popup>
)
}
}
const Settings = props => {
useEffect(() => {
if ( Device.phone )
f7.popup.open('.settings-popup');
else f7.popover.open('#settings-popover', '#btn-settings');
return () => {
// component will unmount
}
});
const onviewclosed = () => {
if ( props.onclosed )
props.onclosed();
};
export default withTranslation()(Settings);
// if ( Device.phone ) {
// return <SettingsPopup onclosed={onviewclosed} />
// }
return <SettingsView usePopover={!Device.phone} onclosed={onviewclosed} />
};
export default Settings;

View file

@ -0,0 +1,23 @@
import React, {Component} from 'react';
import DocumentSettings from '../document-settings/DocumentSettings'
class DocumentSettingsController extends Component {
constructor(props) {
super(props);
}
onPageOrientation(value){
const api = Common.EditorApi.get();
api.change_PageOrient(value=='portrait');
}
render() {
return (
<DocumentSettings onPageOrientation={this.onPageOrientation} />
)
}
}
export default DocumentSettingsController;

View file

@ -1,27 +1,35 @@
import React from 'react';
import React, {Component} from 'react';
import {observer, inject} from "mobx-react";
import {Page,Navbar,List,ListItem,BlockTitle} from 'framework7-react';
import { useTranslation } from 'react-i18next';
const PageDocumentSettings = (props) => {
const { t } = useTranslation();
const DocumentSettings = props => {
const textDocumentSettings = "Document Settings";
const textBack = "Back";
const textOrientation = "Orientation";
const textPortrait = "Portrait";
const textLandscape = "Landscape";
const textFormat = "Format";
const textMargins = "Margins";
const format = "A4";
const formatSize = "21 cm x 29.7 cm";
const storeSettings = props.storeDocumentSettings;
return (
<Page>
<Navbar title={t('ViewSettings.textDocumentSettings')} backLink={t('ViewSettings.textBack')} />
<BlockTitle>{t('ViewSettings.textOrientation')}</BlockTitle>
<Navbar title={textDocumentSettings} backLink={textBack} />
<BlockTitle>{textOrientation}</BlockTitle>
<List>
<ListItem checkbox title={t('ViewSettings.textPortrait')} name="orientation-checkbox" checked={props.isPortrait} onClick={(e) => props.changePageOrient(true)}></ListItem>
<ListItem checkbox title={t('ViewSettings.textLandscape')} name="orientation-checkbox" checked={!props.isPortrait} onClick={(e) => props.changePageOrient(false)}></ListItem>
<ListItem radio title={textPortrait} name="orientation-checkbox" checked={storeSettings.isPortrait} onClick={e => props.onPageOrientation('portrait')}></ListItem>
<ListItem radio title={textLandscape} name="orientation-checkbox" checked={!storeSettings.isPortrait} onClick={e => props.onPageOrientation('landscape')}></ListItem>
</List>
<BlockTitle>{t('ViewSettings.textFormat')}</BlockTitle>
<BlockTitle>{textFormat}</BlockTitle>
<List mediaList>
<ListItem title={format} subtitle={formatSize} link="/document-formats/"></ListItem>
<ListItem checkbox title={t('ViewSettings.textMargins')} link="/margins/"></ListItem>
<ListItem checkbox title={textMargins} link="/margins/"></ListItem>
</List>
</Page>
)
};
export default PageDocumentSettings;
export default inject("storeDocumentSettings")(observer(DocumentSettings));

View file

@ -0,0 +1,228 @@
import React, {Component} from 'react'
import {inject} from "mobx-react";
import CollaborationController from '../../../../common/mobile/lib/controller/Collaboration.jsx'
@inject("storeDocumentSettings", "storeFocusObjects", "storeTextSettings")
class MainController extends Component {
constructor(props) {
super(props)
}
initSdk() {
const script = document.createElement("script");
script.src = "../../../../sdkjs/develop/sdkjs/word/scripts.js";
script.async = true;
script.onload = () => {
let dep_scripts = ['../../../vendor/xregexp/xregexp-all-min.js',
'../../../vendor/sockjs/sockjs.min.js',
'../../../vendor/jszip/jszip.min.js',
'../../../vendor/jszip-utils/jszip-utils.min.js'];
dep_scripts.push(...sdk_scripts);
const promise_get_script = (scriptpath) => {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = scriptpath;
script.onload = () => {
resolve('ok');
};
script.onerror = () => {
reject('error');
};
document.body.appendChild(script);
});
};
const loadConfig = data => {
let me = this;
console.log('load config');
me.editorConfig = Object.assign({}, this.editorConfig, data.config);
me.appOptions.user = Common.Utils.fillUserInfo(me.editorConfig.user, me.editorConfig.lang, "Local.User"/*me.textAnonymous*/);
};
const loadDocument = data => {
this.permissions = {};
this.document = data.doc;
let docInfo = {};
if (data.doc) {
this.permissions = Object.assign(this.permissions, data.doc.permissions);
let _permissions = Object.assign({}, data.doc.permissions),
_user = new Asc.asc_CUserInfo();
_user.put_Id(this.appOptions.user.id);
_user.put_FullName(this.appOptions.user.fullname);
docInfo = new Asc.asc_CDocInfo();
docInfo.put_Id(data.doc.key);
docInfo.put_Url(data.doc.url);
docInfo.put_Title(data.doc.title);
docInfo.put_Format(data.doc.fileType);
docInfo.put_VKey(data.doc.vkey);
docInfo.put_Options(data.doc.options);
docInfo.put_UserInfo(_user);
docInfo.put_CallbackUrl(this.editorConfig.callbackUrl);
docInfo.put_Token(data.doc.token);
docInfo.put_Permissions(_permissions);
docInfo.put_EncryptedInfo(this.editorConfig.encryptionKeys);
// var enable = !this.editorConfig.customization || (this.editorConfig.customization.macros!==false);
// docInfo.asc_putIsEnabledMacroses(!!enable);
// enable = !this.editorConfig.customization || (this.editorConfig.customization.plugins!==false);
// docInfo.asc_putIsEnabledPlugins(!!enable);
// let type = /^(?:(pdf|djvu|xps))$/.exec(data.doc.fileType);
// if (type && typeof type[1] === 'string') {
// this.permissions.edit = this.permissions.review = false;
// }
}
this.api.asc_registerCallback('asc_onGetEditorPermissions', onEditorPermissions);
// this.api.asc_registerCallback('asc_onLicenseChanged', _.bind(this.onLicenseChanged, this));
// this.api.asc_registerCallback('asc_onRunAutostartMacroses', _.bind(this.onRunAutostartMacroses, this));
this.api.asc_setDocInfo(docInfo);
this.api.asc_getEditorPermissions(this.editorConfig.licenseUrl, this.editorConfig.customerId);
// Common.SharedSettings.set('document', data.doc);
// if (data.doc) {
// DE.getController('Toolbar').setDocumentTitle(data.doc.title);
// if (data.doc.info) {
// data.doc.info.author && console.log("Obsolete: The 'author' parameter of the document 'info' section is deprecated. Please use 'owner' instead.");
// data.doc.info.created && console.log("Obsolete: The 'created' parameter of the document 'info' section is deprecated. Please use 'uploaded' instead.");
// }
// }
};
const onEditorPermissions = params => {
let me = this;
const licType = params.asc_getLicenseType();
me.appOptions.canLicense = (licType === Asc.c_oLicenseResult.Success || licType === Asc.c_oLicenseResult.SuccessLimit);
// me.appOptions.canEdit = (me.permissions.edit !== false || me.permissions.review === true) && // can edit or review
// (me.editorConfig.canRequestEditRights || me.editorConfig.mode !== 'view') && // if mode=="view" -> canRequestEditRights must be defined
// (!me.appOptions.isReviewOnly || me.appOptions.canLicense) && // if isReviewOnly==true -> canLicense must be true
// me.isSupportEditFeature();
// me.appOptions.isEdit = me.appOptions.canLicense && me.appOptions.canEdit && me.editorConfig.mode !== 'view';
// me.api.asc_setViewMode(!me.appOptions.isEdit);
me.api.asc_setViewMode(false);
me.api.asc_LoadDocument();
me.api.Resize();
};
const _process_array = (array, fn) => {
let results = [];
return array.reduce(function(p, item) {
return p.then(function() {
return fn(item).then(function(data) {
results.push(data);
return results;
});
});
}, Promise.resolve());
};
_process_array(dep_scripts, promise_get_script)
.then ( result => {
this.api = new Asc.asc_docs_api({
'id-view' : 'editor_sdk',
'mobile' : true,
// 'translate': translate
});
this.appOptions = {};
this.bindEvents();
Common.Gateway.on('init', loadConfig);
// Common.Gateway.on('showmessage', _.bind(me.onExternalMessage, me));
Common.Gateway.on('opendocument', loadDocument);
Common.Gateway.appReady();
Common.Notifications.trigger('engineCreated', this.api);
Common.EditorApi = {get: () => this.api};
}, error => {
console.log('promise failed ' + error);
});
};
script.onerror = () => {
console.log('error');
};
document.body.appendChild(script);
}
bindEvents() {
const storeSettings = this.props.storeDocumentSettings;
this.api.asc_registerCallback('asc_onPageOrient', isPortrait => {
storeSettings.isPortrait = isPortrait === true;
});
const storeFocusObjects = this.props.storeFocusObjects;
this.api.asc_registerCallback('asc_onFocusObject', objects => {
storeFocusObjects.resetFocusObjects(objects);
});
const storeTextSettings = this.props.storeTextSettings;
this.api.asc_registerCallback('asc_onInitEditorFonts', (fonts, select) => {
storeTextSettings.initEditorFonts(fonts, select);
});
this.api.asc_registerCallback('asc_onFontFamily', (font) => {
storeTextSettings.resetFontName(font);
});
this.api.asc_registerCallback('asc_onFontSize', (size) => {
storeTextSettings.resetFontSize(size);
});
this.api.asc_registerCallback('asc_onBold', (isBold) => {
storeTextSettings.resetIsBold(isBold);
});
this.api.asc_registerCallback('asc_onItalic', (isItalic) => {
storeTextSettings.resetIsItalic(isItalic);
});
this.api.asc_registerCallback('asc_onUnderline', (isUnderline) => {
storeTextSettings.resetIsUnderline(isUnderline);
});
this.api.asc_registerCallback('asc_onStrikeout', (isStrikeout) => {
storeTextSettings.resetIsStrikeout(isStrikeout);
});
this.api.asc_registerCallback('asc_onVerticalAlign', (typeBaseline) => {
storeTextSettings.resetTypeBaseline(typeBaseline);
});
this.api.asc_registerCallback('asc_onListType', (data) => {
let type = data.get_ListType();
let subtype = data.get_ListSubType();
storeTextSettings.resetListType(type);
switch (type) {
case 0:
storeTextSettings.resetBullets(subtype);
break;
case 1:
storeTextSettings.resetNumbers(subtype);
break;
}
});
this.api.asc_registerCallback('asc_onPrAlign', (align) => {
storeTextSettings.resetParagraphAlign(align);
});
this.api.asc_registerCallback('asc_onTextColor', (color) => {
storeTextSettings.resetTextColor(color);
});
this.api.asc_registerCallback('asc_onParaSpacingLine', (vc) => {
storeTextSettings.resetLineSpacing(vc);
});
this.api.asc_registerCallback('asc_onTextShd', (shd) => {
let color = shd.get_Color();
storeTextSettings.resetBackgroundColor(color);
});
}
render() {
return <CollaborationController />
}
}
export default MainController;

View file

@ -0,0 +1,3 @@
.device-ios {
}

View file

@ -0,0 +1,12 @@
// Colors
@themeColor: #446995; // (64,102,215)
@themeColorLight: #a2bdde;
@navBarIconColor: #fff;
.device-android {
--f7-navbar-bg-color: @themeColor;
--f7-navbar-link-color: @navBarIconColor;
--f7-navbar-text-color: @navBarIconColor;
}

View file

@ -1,5 +1,9 @@
@import '../../../../common/mobile/resources/less/collaboration.less';
@import '../../../../common/mobile/resources/less/common-ios.less';
@import '../../../../common/mobile/resources/less/common-material.less';
@import './app-material.less';
@import './app-ios.less';
/* Left Panel right border when it is visible by breakpoint */
.panel-left.panel-in-breakpoint:before {
@ -32,3 +36,7 @@
content: '';
z-index: 6000;
}
:root {
--f7-popover-width: 360px;
}

View file

@ -28,6 +28,11 @@
</head>
<body>
<script>
const isAndroid = /Android/.test(navigator.userAgent);
if ( isAndroid && navigator.platform == 'Win32' )
// Framework7 doesn't set Device.android flag when navigator.platform == 'Win32', change it for debug
navigator.__defineGetter__('platform', () => 'Win32Debug');
const getUrlParams = () => {
let e,
a = /\+/g, // Regex for replacing addition symbol with a space
@ -53,7 +58,7 @@
logo = /*params["headerlogo"] ? encodeUrlParam(params["headerlogo"]) : */null,
logoOO = null;
if (!logo) {
logoOO = /Android/.test(navigator.userAgent) ? "../../common/mobile/resources/img/header/header-logo-android.png" : "../../common/mobile/resources/img/header/header-logo-ios.png";
logoOO = isAndroid ? "../../common/mobile/resources/img/header/header-logo-android.png" : "../../common/mobile/resources/img/header/header-logo-ios.png";
}
window.frameEditorId = params["frameEditorId"];
@ -62,7 +67,7 @@
let brendpanel = document.getElementsByClassName('brendpanel')[0];
if (brendpanel) {
if (/Android/.test(navigator.userAgent)) {
if ( isAndroid ) {
brendpanel.classList.add('android');
}
brendpanel.classList.add('visible');
@ -74,7 +79,7 @@
elem.style.opacity = 1;
}
var placeholder = document.getElementsByClassName('placeholder')[0];
if (placeholder && /Android/.test(navigator.userAgent)) {
if (placeholder && isAndroid) {
placeholder.classList.add('android');
}
}

View file

@ -24,8 +24,8 @@ import App from '../components/app.jsx';
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';
import { Provider } from 'react-redux'
import { store } from '../store/store.js'
import { Provider } from 'mobx-react'
import { stores } from '../store/mainStore'
// Init F7 React Plugin
Framework7.use(Framework7React)
@ -33,7 +33,7 @@ Framework7.use(Framework7React)
// Mount React App
ReactDOM.render(
<I18nextProvider i18n={i18n}>
<Provider store={store}>
<Provider {...stores}>
<Suspense fallback="loading">
<App />
</Suspense>

View file

@ -1,8 +1,5 @@
import HomePage from '../pages/home.jsx';
import { DocumentSettings } from "../controllers/Settings";
import Margins from "../components/settings/document-settings/Margins.jsx";
import DocumentFormats from "../components/settings/document-settings/DocumentFormats.jsx";
import LeftPage1 from '../pages/left-page-1.jsx';
import LeftPage2 from '../pages/left-page-2.jsx';
@ -11,31 +8,39 @@ import RequestAndLoad from '../pages/request-and-load.jsx';
import { PageCollaboration, PageUsers } from '../../../../common/mobile/lib/view/Collaboration.jsx';
// Edit text
import { PageFonts, PageAdditionalFormatting, PageBullets, PageNumbers, PageLineSpacing } from "../components/edit/EditText";
var routes = [
{
path: '/',
component: HomePage,
},
//Edit text
{
path: '/document-settings/',
component: DocumentSettings
path: '/edit-text-fonts/',
component: PageFonts,
},
{
path: '/edit-text-add-formatting/',
component: PageAdditionalFormatting,
},
{
path: '/edit-text-bullets/',
component: PageBullets,
},
{
path: '/edit-text-numbers/',
component: PageNumbers,
},
{
path: '/edit-text-line-spacing/',
component: PageLineSpacing,
},
{
path: '/users/',
component: PageUsers
},
{
path: '/collab-main/',
component: PageCollaboration
},
{
path: '/margins/',
component: Margins,
},
{
path: '/document-formats/',
component: DocumentFormats,
},
{
path: '/left-page-1/',
component: LeftPage1,

View file

@ -1,32 +1,48 @@
import React, { Component } from 'react';
import {
Page,
View,
Navbar,
NavLeft,
NavTitle,
NavTitleLarge,
NavRight,
Link,
Toolbar,
Block,
BlockTitle,
List,
ListItem,
Row,
Col,
Button,
Icon, Popup
} from 'framework7-react';
import { Page, View, Navbar, NavLeft, NavRight, Link, Icon } from 'framework7-react';
import EditPopup from '../components/edit/Edit';
import SettingsPopup from '../components/settings/Settings';
import { CollaborationPopover, CollaborationSheet } from '../../../../common/mobile/lib/view/Collaboration.jsx';
import EditOptions from '../components/edit/Edit.jsx';
import Settings from '../components/settings/Settings.jsx';
import CollaborationView from '../../../../common/mobile/lib/view/Collaboration.jsx'
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
editOptionsVisible: false,
settingsVisible: false,
collaborationVisible: false,
};
}
handleClickToOpenOptions = opts => {
this.setState(state => {
if ( opts == 'edit' )
return {editOptionsVisible: true};
else
if ( opts == 'settings' )
return {settingsVisible: true};
else
if ( opts == 'coauth' )
return {collaborationVisible: true}
});
};
handleOptionsViewClosed = opts => {
(async () => {
await 1 && this.setState(state => {
if ( opts == 'edit' )
return {editOptionsVisible: false};
else
if ( opts == 'settings' )
return {settingsVisible: false};
else
if ( opts == 'coauth' )
return {collaborationVisible: false}
})
})();
};
render() {
console.log(this.$f7router)
return (
@ -36,22 +52,29 @@ export default class Home extends Component {
<div slot="before-inner" className="main-logo"><Icon icon="icon-logo"></Icon></div>
<NavLeft>
<Link>Undo</Link>
<Link>Redu</Link>
<Link>Redo</Link>
</NavLeft>
<NavRight>
<Link href={false} popupOpen=".edit-popup">Edit</Link>
{/*<Link href={false} popoverOpen=".collab__popover">Users</Link>*/}
<Link href={false} sheetOpen=".collab__sheet">Users</Link>
<Link href={false} popupOpen=".settings-popup">Settings</Link>
<Link href={false} onClick={e => this.handleClickToOpenOptions('edit')}>Edit</Link>
<Link href={false} onClick={e => this.handleClickToOpenOptions('coauth')}>Users</Link>
<Link href={false} onClick={e => this.handleClickToOpenOptions('settings')} id='btn-settings'>Settings</Link>
</NavRight>
</Navbar>
{/* Page content */}
<View id="editor_sdk">
</View>
<EditPopup />
<SettingsPopup />
<CollaborationPopover />
<CollaborationSheet />
{
!this.state.editOptionsVisible ? null :
<EditOptions onclosed={this.handleOptionsViewClosed.bind(this, 'edit')} />
}
{
!this.state.settingsVisible ? null :
<Settings onclosed={this.handleOptionsViewClosed.bind(this, 'settings')} />
}
{
!this.state.collaborationVisible ? null :
<CollaborationView onclosed={this.handleOptionsViewClosed.bind(this, 'coauth')} />
}
</Page>
)
}

View file

@ -0,0 +1,6 @@
import {action, observable} from 'mobx';
export class storeDocumentSettings {
@observable isPortrait = true
}

View file

@ -0,0 +1,60 @@
import {action, observable, computed} from 'mobx';
export class storeFocusObjects {
@observable _focusObjects = [];
@observable _headerType = 1;
@action resetFocusObjects (objects) {
this._focusObjects = objects;
}
@computed get settings() {
let _settings = [];
for (let object of this._focusObjects) {
let type = object.get_ObjectType();
if (Asc.c_oAscTypeSelectElement.Paragraph === type) {
_settings.push('text', 'paragraph');
} else if (Asc.c_oAscTypeSelectElement.Table === type) {
_settings.push('table');
} else if (Asc.c_oAscTypeSelectElement.Image === type) {
if (object.get_ObjectValue().get_ChartProperties()) {
// Exclude shapes if chart exist
let si = _settings.indexOf('shape');
si < 0 ? _settings.push('chart') : _settings.splice(si,1,'chart');
} else if (object.get_ObjectValue().get_ShapeProperties() && !_settings.includes('chart')) {
_settings.push('shape');
} else {
_settings.push('image');
}
} else if (Asc.c_oAscTypeSelectElement.Hyperlink === type) {
_settings.push('hyperlink');
} else if (Asc.c_oAscTypeSelectElement.Header === type) {
_settings.push('header');
}
}
return _settings.filter((value, index, self) => self.indexOf(value) === index);
}
@computed get headerType() {
for (let object of this._focusObjects) {
let type = object.get_ObjectType();
if (Asc.c_oAscTypeSelectElement.Header === type) {
return object.get_ObjectValue().get_Type();
}
}
return this._headerType;
}
@computed get paragraphObject() {
let paragraphs = [];
for (let object of this._focusObjects) {
if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Paragraph) {
paragraphs.push(object);
}
}
if (paragraphs.length > 0) {
let object = paragraphs[paragraphs.length - 1]; // get top
return object.get_ObjectValue();
} else {
return undefined;
}
}
}

View file

@ -0,0 +1,13 @@
import {storeDocumentSettings} from './documentSettings';
import {storeFocusObjects} from "./focusObjects";
import {storeUsers} from '../../../../common/mobile/lib/store/users';
import {storeTextSettings} from "./textSettings";
export const stores = {
storeFocusObjects: new storeFocusObjects(),
storeDocumentSettings: new storeDocumentSettings(),
users: new storeUsers(),
storeTextSettings: new storeTextSettings()
};

View file

@ -1,14 +0,0 @@
import { combineReducers } from 'redux';
import apiReducer from "./initApi";
import usersReducer from '../../../../../common/mobile/lib/store/users'
import settingsReducer from './settings'
export const initialState = {
users: []
};
export const rootReducer = combineReducers({
api: apiReducer,
users: usersReducer,
settings: settingsReducer
});

View file

@ -1,10 +0,0 @@
import { createStore, applyMiddleware } from 'redux';
import { rootReducer, initialState } from './reducers/root'
import thunk from 'redux-thunk';
const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
!window.Common && (window.Common = {});
Common.Store = { get: () => store };
export { store };

View file

@ -0,0 +1,132 @@
import {action, observable, computed} from 'mobx';
export class storeTextSettings {
@observable fontsArray = [];
@observable fontName = '';
@observable fontSize = undefined;
@observable isBold = false;
@observable isItalic = false;
@observable isUnderline = false;
@observable isStrikethrough = false;
@observable typeBaseline = undefined;
@observable listType = undefined;
@observable typeBullets = undefined;
@observable typeNumbers = undefined;
@observable paragraphAlign = undefined;
@observable textColor = undefined;
@observable lineSpacing = undefined;
@observable backgroundColor = undefined;
@action initEditorFonts (fonts, select) {
let array = [];
for (let font of fonts) {
let fontId = font.asc_getFontId();
array.push({
id : fontId,
name : font.asc_getFontName(),
//displayValue: font.asc_getFontName(),
imgidx : font.asc_getFontThumbnail(),
type : font.asc_getFontType()
});
}
this.fontsArray = array;
}
@action resetFontName (font) {
let name = (typeof font.get_Name) === "function" ? font.get_Name() : font.asc_getName();
this.fontName = name;
}
@action resetFontSize (size) {
this.fontSize = size;
}
@action resetIsBold (isBold) {
this.isBold = isBold;
}
@action resetIsItalic (isItalic) {
this.isItalic = isItalic;
}
@action resetIsUnderline (isUnderline) {
this.isUnderline = isUnderline;
}
@action resetIsStrikeout (isStrikethrough) {
this.isStrikethrough = isStrikethrough;
}
// vertical align
@action resetTypeBaseline (typeBaseline) {
this.typeBaseline = typeBaseline;
}
@computed get isSuperscript() {
return (this.typeBaseline === 1);
}
@computed get isSubscript() {
return (this.typeBaseline === 2);
}
// bullets
@action resetListType (type) {
this.listType = type;
}
@action resetBullets (type) {
this.typeBullets = type;
}
@action resetNumbers (type) {
this.typeNumbers = type;
}
@action resetParagraphAlign (align) {
let value;
switch (align) {
case 0:
value = 'right';
break;
case 1:
value = 'left';
break;
case 2:
value = 'center';
break;
case 3:
value = 'just';
break;
}
this.paragraphAlign = value;
}
@action resetTextColor (color) {
let value;
if (color) {
if (color.get_auto()) {
value = '000000';
} else {
if (color.get_type() == Asc.c_oAscColor.COLOR_TYPE_SCHEME) {
value = {
color: Common.Utils.ThemeColor.getHexColor(color.get_r(), color.get_g(), color.get_b()),
effectValue: color.get_value()
}
} else {
value = Common.Utils.ThemeColor.getHexColor(color.get_r(), color.get_g(), color.get_b());
}
}
}
this.textColor = value;
}
@action resetLineSpacing (vc) {
let line = (vc.get_Line() === null || vc.get_LineRule() === null || vc.get_LineRule() != 1) ? -1 : vc.get_Line();
this.lineSpacing = line;
}
@action resetBackgroundColor (color) {
let value;
if (color.get_type() == Asc.c_oAscColor.COLOR_TYPE_SCHEME) {
value = {
color: Common.Utils.ThemeColor.getHexColor(color.get_r(), color.get_g(), color.get_b()),
effectValue: color.get_value()
}
} else {
value = Common.Utils.ThemeColor.getHexColor(color.get_r(), color.get_g(), color.get_b());
}
this.backgroundColor = value;
}
}

View file

@ -8,5 +8,7 @@ module.exports = {
plugins: [
'@babel/plugin-transform-runtime',
'@babel/plugin-syntax-dynamic-import',
['@babel/plugin-proposal-decorators', {'legacy': true }],
['@babel/plugin-proposal-class-properties',{'loose':true}],
],
};

View file

@ -35,6 +35,8 @@
},
"devDependencies": {
"@babel/core": "^7.11.0",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/plugin-proposal-decorators": "^7.10.5",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.11.0",
"@babel/preset-env": "^7.11.0",
@ -52,6 +54,8 @@
"less": "^3.12.2",
"less-loader": "^6.2.0",
"mini-css-extract-plugin": "^0.9.0",
"mobx": "^5.15.7",
"mobx-react": "^6.3.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"ora": "^4.0.5",
"postcss-loader": "^3.0.0",