diff --git a/apps/common/mobile/lib/controller/Search.jsx b/apps/common/mobile/lib/controller/Search.jsx new file mode 100644 index 0000000000..fa60859ecc --- /dev/null +++ b/apps/common/mobile/lib/controller/Search.jsx @@ -0,0 +1,13 @@ +import React from 'react'; +import SearchView, {SearchSettingsView} from '../view/Search' + + +const SearchController = props => { + const onSearchQuery = params => { + console.log('on search: ' + params); + }; + + return +}; + +export {SearchController, SearchView, SearchSettingsView}; diff --git a/apps/common/mobile/lib/view/Search.jsx b/apps/common/mobile/lib/view/Search.jsx new file mode 100644 index 0000000000..51bca9ca58 --- /dev/null +++ b/apps/common/mobile/lib/view/Search.jsx @@ -0,0 +1,226 @@ +import React, { Component } from 'react'; +import { Searchbar, Popover, Popup, View, Page, List, ListItem, Navbar, NavRight, Link } from 'framework7-react'; +import { Toggle } from 'framework7-react'; +import { f7 } from 'framework7-react'; +import { Dom7 } from 'framework7'; +import { Device } from '../../../../common/mobile/utils/device'; +import { observable } from "mobx"; +import { observer } from "mobx-react"; + +const searchOptions = observable({ + usereplace: false +}); + +const popoverStyle = { + height: '300px' +}; + +const SEARCH_BACKWARD = 'back'; +const SEARCH_FORWARD = 'next'; + +class SearchSettingsView extends Component { + constructor(props) { + super(props); + + this.state = { + useReplace: false, + caseSensitive: false, + markResults: false + }; + } + + onFindReplaceClick(action) { + searchOptions.usereplace = action == 'replace'; + this.setState({ + useReplace: searchOptions.usereplace + }); + + + if (this.onReplaceChecked) {} + } + + extraSearchOptions() { + } + + render() { + const show_popover = true; + const navbar = + + {!show_popover && + + Done + } + ; + const extra = this.extraSearchOptions(); + const content = + + + {navbar} + + this.onFindReplaceClick('find')} /> + this.onFindReplaceClick('replace')} /> + + { extra } + + ; + return ( + show_popover ? + {content} : + {content} + ) + } +} + +@observer +class SearchView extends Component { + constructor(props) { + super(props); + + $$(document).on('page:init', (e, page) => { + if ( page.name == 'home' ) { + this.searchbar = f7.searchbar.create({ + el: '.searchbar', + customSearch: true, + expandable: true, + backdrop: false, + on: { + search: (bar, curval, prevval) => { + }, + enable: this.onSearchbarShow.bind(this, true), + disable: this.onSearchbarShow.bind(this, false) + } + }); + + // function iOSVersion() { + // var ua = navigator.userAgent; + // var m; + // return (m = /(iPad|iPhone|iphone).*?(OS |os |OS\_)(\d+((_|\.)\d)?((_|\.)\d)?)/.exec(ua)) ? parseFloat(m[3]) : 0; + // } + + const $$ = Dom7; + const $editor = $$('#editor_sdk'); + if (false /*iOSVersion() < 13*/) { + // $editor.single('mousedown touchstart', _.bind(me.onEditorTouchStart, me)); + // $editor.single('mouseup touchend', _.bind(me.onEditorTouchEnd, me)); + } else { + // $editor.single('pointerdown', this.onEditorTouchStart, me)); + // $editor.single('pointerup', _.bind(me.onEditorTouchEnd, me)); + } + + $editor.on('pointerdown', this.onEditorTouchStart.bind(this)); + $editor.on('pointerup', this.onEditorTouchEnd.bind(this)); + } + }); + + this.onSettingsClick = this.onSettingsClick.bind(this); + this.onSearchClick = this.onSearchClick.bind(this); + } + + componentDidMount(){ + const $$ = Dom7; + this.$repalce = $$('#idx-replace-val'); + } + + onSettingsClick(e) { + if ( Device.phone ) { + // f7.popup.open('.settings-popup'); + } else f7.popover.open('#idx-search-settings', '#idx-btn-search-settings'); + } + + searchParams() { + let params = { + find: this.searchbar.query + }; + + if ( searchOptions.usereplace ) + params.replace = this.$replace.val(); + + return params; + } + + onSearchClick(action) { + if ( this.searchbar && this.searchbar.query) { + if ( this.props.onSearchQuery ) { + let params = this.searchParams(); + params.forward = action != SEARCH_BACKWARD; + + this.props.onSearchQuery(params); + } + } + } + + onSearchbarShow(isshowed, bar) { + if ( !isshowed ) { + this.$repalce.val(''); + } + } + + onEditorTouchStart(e) { + this.startPoint = this.pointerPosition(e); + } + + onEditorTouchEnd(e) { + const endPoint = this.pointerPosition(e); + + if ( this.searchbar.enabled ) { + const distance = (this.startPoint.x === undefined || this.startPoint.y === undefined) ? 0 : + Math.sqrt((endPoint.x -= this.startPoint.x) * endPoint.x + (endPoint.y -= this.startPoint.y) * endPoint.y); + + if ( distance < 1 ) { + this.searchbar.disable(); + } + } + } + + pointerPosition(e) { + let out = {x:0, y:0}; + if ( e.type == 'touchstart' || e.type == 'touchend' ) { + const touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; + out.x = touch.pageX; + out.y = touch.pageY; + } else + if ( e.type == 'mousedown' || e.type == 'mouseup' ) { + out.x = e.pageX; + out.y = e.pageY; + } + + return out; + } + + render() { + const usereplace = searchOptions.usereplace; + const hidden = {display: "none"}; + return ( +
+
+
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+ + ) + } +} + +export {SearchView as default, SearchView, SearchSettingsView}; diff --git a/apps/common/mobile/resources/less/common-ios.less b/apps/common/mobile/resources/less/common-ios.less index ee582bc7be..b153c61c51 100644 --- a/apps/common/mobile/resources/less/common-ios.less +++ b/apps/common/mobile/resources/less/common-ios.less @@ -153,14 +153,24 @@ } } - .popover li:last-child { - .segmented a { - &:first-child { - border-radius: var(--f7-button-border-radius) 0 0 var(--f7-button-border-radius); - } + .popover { + li:last-child { + .segmented a { + &:first-child { + border-radius: var(--f7-button-border-radius) 0 0 var(--f7-button-border-radius); + } - &:last-child { - border-radius: 0 var(--f7-button-border-radius) var(--f7-button-border-radius) 0; + &:last-child { + border-radius: 0 var(--f7-button-border-radius) var(--f7-button-border-radius) 0; + } + } + } + + .page-content { + > .list { + &:last-child { + margin-bottom: 30px; + } } } } @@ -366,8 +376,8 @@ } } - .dataview, #add-table, #add-shape { - &.page-content { + .dataview, #add-table, #add-shape, #add-slide { + &.page-content, .page-content { background-color: @white; } } diff --git a/apps/common/mobile/resources/less/common-material.less b/apps/common/mobile/resources/less/common-material.less index dab99d7c26..18af81ee12 100644 --- a/apps/common/mobile/resources/less/common-material.less +++ b/apps/common/mobile/resources/less/common-material.less @@ -165,10 +165,10 @@ } } &.inputs-list { - .item-input { + .item-input, .item-link { .item-inner { display: block; - .item-label { + .item-title, .item-label { width: 100%; font-size: 12px; } diff --git a/apps/common/mobile/resources/less/search.less b/apps/common/mobile/resources/less/search.less index ab694cea8d..21249b9414 100644 --- a/apps/common/mobile/resources/less/search.less +++ b/apps/common/mobile/resources/less/search.less @@ -13,4 +13,8 @@ .hairline(bottom, @statusBarBorderColor); } } + + .searchbar-input-wrap { + margin-right: 10px; + } } diff --git a/apps/common/mobile/resources/less/variables.less b/apps/common/mobile/resources/less/variables.less new file mode 100644 index 0000000000..2ba2c9402e --- /dev/null +++ b/apps/common/mobile/resources/less/variables.less @@ -0,0 +1,3 @@ + +@border-regular-control: #cbcbcb; +@text-normal: #000; diff --git a/apps/documenteditor/mobile/src/controller/Search.jsx b/apps/documenteditor/mobile/src/controller/Search.jsx index 6c662c5f3c..99ac30ccc7 100644 --- a/apps/documenteditor/mobile/src/controller/Search.jsx +++ b/apps/documenteditor/mobile/src/controller/Search.jsx @@ -1,8 +1,75 @@ import React from 'react'; -import SearchView, {SearchSettingsView} from '../view/Search' +import { List, ListItem, Toggle } from 'framework7-react'; +import { SearchController, SearchView, SearchSettingsView } from '../../../../common/mobile/lib/controller/Search'; +import { f7 } from 'framework7-react'; -const SearchController = props => { - return + +class SearchSettings extends SearchSettingsView { + constructor(props) { + super(props); + + this.onToggleMarkResults = this.onToggleMarkResults.bind(this); + } + + onToggleMarkResults(checked) { + const api = Common.EditorApi.get(); + api.asc_selectSearchingResults(checked); + } + + extraSearchOptions() { + const anc_markup = super.extraSearchOptions(); + + const markup = + + + + + + + ; + + return {...anc_markup, ...markup}; + } +} + +class DESearchView extends SearchView { + searchParams() { + let params = super.searchParams(); + + const checkboxCaseSensitive = f7.toggle.get('.toggle-case-sensitive'), + checkboxMarkResults = f7.toggle.get('.toggle-mark-results'); + const searchOptions = { + caseSensitive: checkboxCaseSensitive.checked, + highlight: checkboxMarkResults.checked + }; + + return {...params, ...searchOptions}; + } + + onSearchbarShow(isshowed, bar) { + super.onSearchbarShow(isshowed, bar); + + const api = Common.EditorApi.get(); + if ( isshowed ) { + const checkboxMarkResults = f7.toggle.get('.toggle-mark-results'); + api.asc_selectSearchingResults(checkboxMarkResults.checked); + } else api.asc_selectSearchingResults(false); + } +} + +const Search = props => { + const onSearchQuery = params => { + const api = Common.EditorApi.get(); + + if ( !params.replace ) { + if ( !api.asc_findText(params.find, params.forward, params.caseSensitive, params.highlight) ) { + f7.dialog.alert('there are no more results', e => { + }); + } + } + }; + + return }; -export {SearchController as Search, SearchSettingsView as SearchSettings}; +export {Search, SearchSettings} diff --git a/apps/documenteditor/mobile/src/view/Search.jsx b/apps/documenteditor/mobile/src/view/Search.jsx deleted file mode 100644 index c5c01ac652..0000000000 --- a/apps/documenteditor/mobile/src/view/Search.jsx +++ /dev/null @@ -1,116 +0,0 @@ -import React, { Component } from 'react'; -import { Searchbar, Popover, Popup, View, Page, List, ListItem, Navbar, NavRight, Link } from 'framework7-react'; -import { f7ready, f7 } from 'framework7-react'; -import { Device } from '../../../../common/mobile/utils/device'; - -const popoverStyle = { - height: '300px' -}; - -class SearchSettingsView extends Component { - constructor(props) { - super(props); - - this.state = { - useReplace: false, - caseSensitive: false, - markResults: false - }; - } - - onFindReplaceClick(action) { - this.setState({ - useReplace: action == 'replace' - }); - } - - render() { - const show_popover = true; - const navbar = - - {!show_popover && - - Done - } - ; - const content = - - - {navbar} - - this.onFindReplaceClick('find')}> - this.onFindReplaceClick('replace')}> - - - ; - return ( - show_popover ? - {content} : - {content} - ) - } -} - -class SearchView extends Component { - constructor(props) { - super(props); - - $$(document).on('page:init', (e, page) => { - if ( page.name == 'home' ) { - f7.searchbar.create({ - el: '.searchbar', - customSearch: true, - expandable: true, - backdrop: false, - on: { - search: (bar, curval, prevval) => { - console.log('on search results ' + curval); - } - } - }); - } - }); - - this.onSettingsClick = this.onSettingsClick.bind(this); - } - - componentDidMount(){ - } - - onSettingsClick(e) { - if ( Device.phone ) { - // f7.popup.open('.settings-popup'); - } else f7.popover.open('#idx-search-settings', '#idx-btn-search-settings'); - } - - render() { - return ( -
-
-
-
- - - -
-
- - - -
- - Cancel -
- - ) - } -} - -export {SearchView as default, SearchSettingsView}; diff --git a/apps/documenteditor/mobile/src/view/search.jsx b/apps/documenteditor/mobile/src/view/search.jsx deleted file mode 100644 index c5c01ac652..0000000000 --- a/apps/documenteditor/mobile/src/view/search.jsx +++ /dev/null @@ -1,116 +0,0 @@ -import React, { Component } from 'react'; -import { Searchbar, Popover, Popup, View, Page, List, ListItem, Navbar, NavRight, Link } from 'framework7-react'; -import { f7ready, f7 } from 'framework7-react'; -import { Device } from '../../../../common/mobile/utils/device'; - -const popoverStyle = { - height: '300px' -}; - -class SearchSettingsView extends Component { - constructor(props) { - super(props); - - this.state = { - useReplace: false, - caseSensitive: false, - markResults: false - }; - } - - onFindReplaceClick(action) { - this.setState({ - useReplace: action == 'replace' - }); - } - - render() { - const show_popover = true; - const navbar = - - {!show_popover && - - Done - } - ; - const content = - - - {navbar} - - this.onFindReplaceClick('find')}> - this.onFindReplaceClick('replace')}> - - - ; - return ( - show_popover ? - {content} : - {content} - ) - } -} - -class SearchView extends Component { - constructor(props) { - super(props); - - $$(document).on('page:init', (e, page) => { - if ( page.name == 'home' ) { - f7.searchbar.create({ - el: '.searchbar', - customSearch: true, - expandable: true, - backdrop: false, - on: { - search: (bar, curval, prevval) => { - console.log('on search results ' + curval); - } - } - }); - } - }); - - this.onSettingsClick = this.onSettingsClick.bind(this); - } - - componentDidMount(){ - } - - onSettingsClick(e) { - if ( Device.phone ) { - // f7.popup.open('.settings-popup'); - } else f7.popover.open('#idx-search-settings', '#idx-btn-search-settings'); - } - - render() { - return ( -
-
-
-
- - - -
-
- - - -
- - Cancel -
- - ) - } -} - -export {SearchView as default, SearchSettingsView}; diff --git a/apps/documenteditor/mobile/src/view/settings/ApplicationSettings.jsx b/apps/documenteditor/mobile/src/view/settings/ApplicationSettings.jsx index 9a3a800600..9561950344 100644 --- a/apps/documenteditor/mobile/src/view/settings/ApplicationSettings.jsx +++ b/apps/documenteditor/mobile/src/view/settings/ApplicationSettings.jsx @@ -39,8 +39,7 @@ const PageApplicationSettings = props => { onChange={() => changeMeasureSettings(2)}> - - {_t.textSpellcheck} + { store.changeSpellCheck(!isSpellChecking); @@ -50,8 +49,7 @@ const PageApplicationSettings = props => { - {/*ToDo: if (DisplayMode == "final" || DisplayMode == "original") {disabled} */} - {_t.textNoCharacters} + {/*ToDo: if (DisplayMode == "final" || DisplayMode == "original") {disabled} */} { store.changeNoCharacters(!isNonprintingCharacters); @@ -59,8 +57,7 @@ const PageApplicationSettings = props => { }} /> - {/*ToDo: if (DisplayMode == "final" || DisplayMode == "original") {disabled} */} - {_t.textHiddenTableBorders} + {/*ToDo: if (DisplayMode == "final" || DisplayMode == "original") {disabled} */} { store.changeShowTableEmptyLine(!isHiddenTableBorders); @@ -73,17 +70,15 @@ const PageApplicationSettings = props => { } {_t.textCommentsDisplay} - - {_t.textComments} - + { store.changeDisplayComments(!isComments); props.switchDisplayComments(!isComments); }} /> - - {_t.textResolvedComments} + { store.changeDisplayResolved(!isResolvedComments); diff --git a/apps/presentationeditor/mobile/locale/en.json b/apps/presentationeditor/mobile/locale/en.json index ea3358380b..7207ffc45a 100644 --- a/apps/presentationeditor/mobile/locale/en.json +++ b/apps/presentationeditor/mobile/locale/en.json @@ -77,7 +77,37 @@ "textSlide": "Slide", "textShape": "Shape", "textImage": "Image", - "textOther": "Other" + "textOther": "Other", + "textPictureFromLibrary": "Picture from Library", + "textPictureFromURL": "Picture from URL", + "textLinkSettings": "LinkSettings", + "textBack": "Back", + "textEmptyImgUrl": "You need to specify image URL.", + "txtNotUrl": "This field should be a URL in the format \"http://www.example.com\"", + "notcriticalErrorTitle": "Warning", + "textAddress": "Address", + "textImageURL": "Image URL", + "textInsertImage": "Insert Image", + "textTable": "Table", + "textComment": "Comment", + "textTableSize": "Table Size", + "textColumns": "Columns", + "textRows": "Rows", + "textCancel": "Cancel", + "textLink": "Link", + "textLinkType": "Link Type", + "textExternalLink": "External Link", + "textSlideInThisPresentation": "Slide in this Presentation", + "textLinkTo": "Link to", + "textNextSlide": "Next Slide", + "textPreviousSlide": "Previous Slide", + "textFirstSlide": "First Slide", + "textLastSlide": "Last Slide", + "textSlideNumber": "Slide Number", + "textDisplay": "Display", + "textDefault": "Selected text", + "textScreenTip": "Screen Tip", + "textInsert": "Insert" }, "Edit": { "textText": "Text", diff --git a/apps/presentationeditor/mobile/src/controller/Main.jsx b/apps/presentationeditor/mobile/src/controller/Main.jsx index 902dca3d77..ef2b7aa65d 100644 --- a/apps/presentationeditor/mobile/src/controller/Main.jsx +++ b/apps/presentationeditor/mobile/src/controller/Main.jsx @@ -5,7 +5,11 @@ import { f7 } from "framework7-react"; import { withTranslation } from 'react-i18next'; import CollaborationController from '../../../../common/mobile/lib/controller/Collaboration.jsx' +<<<<<<< HEAD @inject("storeFocusObjects", "storeAppOptions", "storePresentationInfo", "storePresentationSettings", "storeSlideSettings", "storeTextSettings", "storeTableSettings", "storeChartSettings") +======= +@inject("storeFocusObjects", "storeAppOptions", "storePresentationInfo", "storePresentationSettings", "storeSlideSettings", "storeTextSettings", "storeTableSettings", "storeLinkSettings") +>>>>>>> feature/mobile-apps-on-reactjs class MainController extends Component { constructor(props) { super(props) diff --git a/apps/presentationeditor/mobile/src/controller/add/AddImage.jsx b/apps/presentationeditor/mobile/src/controller/add/AddImage.jsx new file mode 100644 index 0000000000..4aafae09b1 --- /dev/null +++ b/apps/presentationeditor/mobile/src/controller/add/AddImage.jsx @@ -0,0 +1,59 @@ +import React, {Component} from 'react'; +import { f7 } from 'framework7-react'; +import {Device} from '../../../../../common/mobile/utils/device'; +import { withTranslation} from 'react-i18next'; + +import {AddImage} from '../../view/add/AddImage'; + +class AddImageController extends Component { + constructor (props) { + super(props); + this.onInsertByFile = this.onInsertByFile.bind(this); + this.onInsertByUrl = this.onInsertByUrl.bind(this); + } + + closeModal () { + if ( Device.phone ) { + f7.sheet.close('.add-popup', true); + } else { + f7.popover.close('#add-popover'); + } + } + + onInsertByFile () { + const api = Common.EditorApi.get(); + api.asc_addImage(); + this.closeModal(); + } + + onInsertByUrl (value) { + const { t } = this.props; + const _t = t("View.Add", { returnObjects: true }); + + const _value = value.replace(/ /g, ''); + + if (_value) { + if ((/((^https?)|(^ftp)):\/\/.+/i.test(_value))) { + this.closeModal(); + const api = Common.EditorApi.get(); + api.AddImageUrl(_value); + } else { + f7.dialog.alert(_t.txtNotUrl, _t.notcriticalErrorTitle); + } + } else { + f7.dialog.alert(_t.textEmptyImgUrl, _t.notcriticalErrorTitle); + } + } + + render () { + return ( + + ) + } +} + +const AddImageWithTranslation = withTranslation()(AddImageController); + +export {AddImageWithTranslation as AddImageController}; \ No newline at end of file diff --git a/apps/presentationeditor/mobile/src/controller/add/AddLink.jsx b/apps/presentationeditor/mobile/src/controller/add/AddLink.jsx new file mode 100644 index 0000000000..e9e9ba81a6 --- /dev/null +++ b/apps/presentationeditor/mobile/src/controller/add/AddLink.jsx @@ -0,0 +1,112 @@ +import React, {Component} from 'react'; +import { f7 } from 'framework7-react'; +import {Device} from '../../../../../common/mobile/utils/device'; +import { withTranslation} from 'react-i18next'; + +import {PageLink} from '../../view/add/AddLink'; + +class AddLinkController extends Component { + constructor (props) { + super(props); + this.onInsertLink = this.onInsertLink.bind(this); + this.getTextDisplay = this.getTextDisplay.bind(this); + + const api = Common.EditorApi.get(); + this.textDisplay = api.can_AddHyperlink(); + } + + closeModal () { + if ( Device.phone ) { + f7.sheet.close('.add-popup', true); + } else { + f7.popover.close('#add-popover'); + } + } + + onInsertLink (type, linkInfo) { + const api = Common.EditorApi.get(); + const { t } = this.props; + const _t = t("View.Add", { returnObjects: true }); + + const c_oHyperlinkType = { + InternalLink: 0, + WebLink: 1 + }; + const display = linkInfo.display; + const tip = linkInfo.tip; + const props = new Asc.CHyperlinkProperty(); + let def_display = ''; + + if (type == c_oHyperlinkType.WebLink) { + let url = linkInfo.url; + const urltype = api.asc_getUrlType(url.trim()); + const isEmail = (urltype == 2); + if (urltype < 1) { + f7.dialog.alert(_t.txtNotUrl, _t.notcriticalErrorTitle); + return; + } + + url = url.replace(/^\s+|\s+$/g, ''); + if (!/(((^https?)|(^ftp)):\/\/)|(^mailto:)/i.test(url)) + url = (isEmail ? 'mailto:' : 'http://' ) + url; + url = url.replace(new RegExp("%20", 'g'), " "); + + props.put_Value(url); + props.put_ToolTip(tip); + def_display = url; + } else { + let url = "ppaction://hlink"; + let slidetip = ''; + switch (linkInfo.linkTo) { + case 0: + url = url + "showjump?jump=nextslide"; + slidetip = _t.textNextSlide; + break; + case 1: + url = url + "showjump?jump=previousslide"; + slidetip = _t.textPrevSlide; + break; + case 2: + url = url + "showjump?jump=firstslide"; + slidetip = _t.textFirstSlide; + break; + case 3: + url = url + "showjump?jump=lastslide"; + slidetip = _t.textLastSlide; + break; + case 4: + url = url + "sldjumpslide" + linkInfo.numberTo; + slidetip = _t.textSlide + ' ' + (linkInfo.numberTo + 1); + break; + } + props.put_Value(url); + props.put_ToolTip(!tip ? slidetip : tip); + def_display = slidetip; + } + + if (!linkInfo.displayDisabled) { + props.put_Text(!display ? def_display : display); + } else + props.put_Text(null); + + api.add_Hyperlink(props); + + this.closeModal(); + } + + getTextDisplay () { + return this.textDisplay; + } + + render () { + return ( + + ) + } +} + +const AddLinkWithTranslation = withTranslation()(AddLinkController); + +export {AddLinkWithTranslation as AddLinkController}; \ No newline at end of file diff --git a/apps/presentationeditor/mobile/src/controller/add/AddOther.jsx b/apps/presentationeditor/mobile/src/controller/add/AddOther.jsx new file mode 100644 index 0000000000..e2838493e9 --- /dev/null +++ b/apps/presentationeditor/mobile/src/controller/add/AddOther.jsx @@ -0,0 +1,102 @@ +import React, {Component} from 'react'; +import { f7 } from 'framework7-react'; +import {Device} from '../../../../../common/mobile/utils/device'; +import { withTranslation} from 'react-i18next'; + +import {AddOther} from '../../view/add/AddOther'; + +class AddOtherController extends Component { + constructor (props) { + super(props); + this.onStyleClick = this.onStyleClick.bind(this); + this.initStyleTable = this.initStyleTable.bind(this); + + this.initTable = false; + } + + closeModal () { + if ( Device.phone ) { + f7.sheet.close('.add-popup', true); + } else { + f7.popover.close('#add-popover'); + } + } + + initStyleTable () { + if (!this.initTable) { + const api = Common.EditorApi.get(); + api.asc_GetDefaultTableStyles(); + this.initTable = true; + } + } + + onStyleClick (type) { + const api = Common.EditorApi.get(); + + this.closeModal(); + + const { t } = this.props; + const _t = t("View.Add", { returnObjects: true }); + + let picker; + + const dialog = f7.dialog.create({ + title: _t.textTableSize, + text: '', + content: + '
' + + '
' + + '
' + _t.textColumns + '
' + + '
' + _t.textRows + '
' + + '
' + + '
' + + '
', + buttons: [ + { + text: _t.textCancel + }, + { + text: 'OK', + bold: true, + onClick: function () { + const size = picker.value; + + api.put_Table(parseInt(size[0]), parseInt(size[1]), undefined, type.toString()); + } + } + ] + }).open(); + dialog.on('opened', () => { + picker = f7.picker.create({ + containerEl: document.getElementById('picker-table-size'), + cols: [ + { + textAlign: 'center', + width: '100%', + values: [1,2,3,4,5,6,7,8,9,10] + }, + { + textAlign: 'center', + width: '100%', + values: [1,2,3,4,5,6,7,8,9,10] + } + ], + toolbar: false, + rotateEffect: true, + value: [3, 3] + }); + }); + } + + render () { + return ( + + ) + } +} + +const AddOtherWithTranslation = withTranslation()(AddOtherController); + +export {AddOtherWithTranslation as AddOtherController}; \ No newline at end of file diff --git a/apps/presentationeditor/mobile/src/controller/add/AddShape.jsx b/apps/presentationeditor/mobile/src/controller/add/AddShape.jsx index bf649dc1d4..74d3cdd5df 100644 --- a/apps/presentationeditor/mobile/src/controller/add/AddShape.jsx +++ b/apps/presentationeditor/mobile/src/controller/add/AddShape.jsx @@ -3,15 +3,31 @@ import { f7 } from 'framework7-react'; import {Device} from '../../../../../common/mobile/utils/device'; import {observer, inject} from "mobx-react"; -import { AddShape } from '../../view/add/AddShape'; +import AddShape from '../../view/add/AddShape'; class AddShapeController extends Component { constructor (props) { super(props); + this.onShapeClick = this.onShapeClick.bind(this); } + + closeModal () { + if ( Device.phone ) { + f7.sheet.close('.add-popup', true); + } else { + f7.popover.close('#add-popover'); + } + } + + onShapeClick (type) { + const api = Common.EditorApi.get(); + api.AddShapeOnCurrentPage(type); + this.closeModal(); + } + render () { return ( - ) } diff --git a/apps/presentationeditor/mobile/src/controller/add/AddSlide.jsx b/apps/presentationeditor/mobile/src/controller/add/AddSlide.jsx index 5723596ce6..6c2cb706b4 100644 --- a/apps/presentationeditor/mobile/src/controller/add/AddSlide.jsx +++ b/apps/presentationeditor/mobile/src/controller/add/AddSlide.jsx @@ -3,15 +3,31 @@ import { f7 } from 'framework7-react'; import {Device} from '../../../../../common/mobile/utils/device'; import {observer, inject} from "mobx-react"; -import { AddSlide } from '../../view/add/AddSlide'; +import AddSlide from '../../view/add/AddSlide'; class AddSlideController extends Component { constructor (props) { super(props); + this.onSlideLayout = this.onSlideLayout.bind(this); } + + closeModal () { + if ( Device.phone ) { + f7.sheet.close('.add-popup', true); + } else { + f7.popover.close('#add-popover'); + } + } + + onSlideLayout (type) { + const api = Common.EditorApi.get(); + api.AddSlide(type); + this.closeModal(); + } + render () { return ( - ) } diff --git a/apps/presentationeditor/mobile/src/store/focusObjects.js b/apps/presentationeditor/mobile/src/store/focusObjects.js index 64e69df712..9a762cbc99 100644 --- a/apps/presentationeditor/mobile/src/store/focusObjects.js +++ b/apps/presentationeditor/mobile/src/store/focusObjects.js @@ -83,6 +83,16 @@ export class storeFocusObjects { } } + @computed get paragraphLocked() { + let _paragraphLocked = false; + for (let object of this._focusObjects) { + if (Asc.c_oAscTypeSelectElement.Paragraph == object.get_ObjectType()) { + _paragraphLocked = object.get_ObjectValue().get_Locked(); + } + } + return _paragraphLocked; + } + @computed get shapeObject() { const shapes = []; for (let object of this._focusObjects) { diff --git a/apps/presentationeditor/mobile/src/store/linkSettings.js b/apps/presentationeditor/mobile/src/store/linkSettings.js new file mode 100644 index 0000000000..9670a64667 --- /dev/null +++ b/apps/presentationeditor/mobile/src/store/linkSettings.js @@ -0,0 +1,8 @@ +import {action, observable, computed} from 'mobx'; + +export class storeLinkSettings { + @observable canAddLink; + @action canAddHyperlink (value) { + this.canAddLink = value; + } +} \ No newline at end of file diff --git a/apps/presentationeditor/mobile/src/store/slideSettings.js b/apps/presentationeditor/mobile/src/store/slideSettings.js index 0899b7bbcc..37ee3ec4c3 100644 --- a/apps/presentationeditor/mobile/src/store/slideSettings.js +++ b/apps/presentationeditor/mobile/src/store/slideSettings.js @@ -41,6 +41,24 @@ export class storeSlideSettings { @action addArrayLayouts(array) { this.arrayLayouts = array; } + @computed get slideLayouts () { + const layouts = []; + const columns = 2; + let row = -1; + this.arrayLayouts.forEach((item, index)=>{ + if (0 == index % columns) { + layouts.push([]); + row++ + } + layouts[row].push({ + type: item.getIndex(), + image: item.get_Image(), + width: item.get_Width(), + height: item.get_Height() + }); + }); + return layouts; + } @action changeSlideLayoutIndex(index) { this.slideLayoutIndex = index; diff --git a/apps/presentationeditor/mobile/src/store/tableSettings.js b/apps/presentationeditor/mobile/src/store/tableSettings.js index d36a09aea7..7608c5f3c5 100644 --- a/apps/presentationeditor/mobile/src/store/tableSettings.js +++ b/apps/presentationeditor/mobile/src/store/tableSettings.js @@ -141,5 +141,4 @@ export class storeTableSettings { border.put_Value(0); } } - } \ No newline at end of file diff --git a/apps/presentationeditor/mobile/src/view/add/Add.jsx b/apps/presentationeditor/mobile/src/view/add/Add.jsx index 07d98fa646..00f312662b 100644 --- a/apps/presentationeditor/mobile/src/view/add/Add.jsx +++ b/apps/presentationeditor/mobile/src/view/add/Add.jsx @@ -7,11 +7,36 @@ import {Device} from '../../../../../common/mobile/utils/device'; import AddSlideController from "../../controller/add/AddSlide"; import AddShapeController from "../../controller/add/AddShape"; -//import AddImageController from "../../controller/add/AddImage"; -//import AddOtherController from "../../controller/add/AddOther"; +import {AddImageController} from "../../controller/add/AddImage"; +import {PageImageLinkSettings} from "./AddImage"; +import {AddOtherController} from "../../controller/add/AddOther"; +import {PageAddTable} from "./AddOther"; +import {AddLinkController} from "../../controller/add/AddLink"; +import {PageTypeLink, PageLinkTo} from "./AddLink"; const routes = [ - + // Image + { + path: '/add-image-from-url/', + component: PageImageLinkSettings + }, + // Other + { + path: '/add-table/', + component: PageAddTable + }, + { + path: '/add-link/', + component: AddLinkController + }, + { + path: '/add-link-type/', + component: PageTypeLink + }, + { + path: '/add-link-to/', + component: PageLinkTo + } ]; const AddLayoutNavbar = ({ tabs, inPopover }) => { @@ -58,7 +83,7 @@ const AddTabs = props => { icon: 'icon-add-shape', component: }); - /*tabs.push({ + tabs.push({ caption: _t.textImage, id: 'add-image', icon: 'icon-add-image', @@ -69,7 +94,7 @@ const AddTabs = props => { id: 'add-other', icon: 'icon-add-other', component: - });*/ + }); return ( diff --git a/apps/presentationeditor/mobile/src/view/add/AddImage.jsx b/apps/presentationeditor/mobile/src/view/add/AddImage.jsx new file mode 100644 index 0000000000..f25c979df9 --- /dev/null +++ b/apps/presentationeditor/mobile/src/view/add/AddImage.jsx @@ -0,0 +1,49 @@ +import React, {useState} from 'react'; +import {observer, inject} from "mobx-react"; +import {List, ListItem, Page, Navbar, Icon, ListButton, ListInput, BlockTitle} from 'framework7-react'; +import { useTranslation } from 'react-i18next'; + +const PageLinkSettings = props => { + const { t } = useTranslation(); + const _t = t('View.Add', {returnObjects: true}); + const [stateValue, setValue] = useState(''); + return ( + + + {_t.textAddress} + + {setValue(event.target.value)}} + > + + + + {props.onInsertByUrl(stateValue)}}> + + + ) +}; + +const AddImage = props => { + const { t } = useTranslation(); + const _t = t('View.Add', {returnObjects: true}); + return ( + + {props.onInsertByFile()}}> + + + + + + + ) +}; + +export {AddImage, PageLinkSettings as PageImageLinkSettings}; \ No newline at end of file diff --git a/apps/presentationeditor/mobile/src/view/add/AddLink.jsx b/apps/presentationeditor/mobile/src/view/add/AddLink.jsx new file mode 100644 index 0000000000..567a23e843 --- /dev/null +++ b/apps/presentationeditor/mobile/src/view/add/AddLink.jsx @@ -0,0 +1,154 @@ +import React, {useState} from 'react'; +import {observer, inject} from "mobx-react"; +import {List, ListItem, Page, Navbar, Icon, ListButton, ListInput, Segmented, Button} from 'framework7-react'; +import { useTranslation } from 'react-i18next'; +import {Device} from "../../../../../common/mobile/utils/device"; + +const PageTypeLink = props => { + const { t } = useTranslation(); + const _t = t('View.Add', {returnObjects: true}); + const [typeLink, setTypeLink] = useState(props.curType); + return ( + + + + {setTypeLink(1); props.changeType(1);}}> + {setTypeLink(0); props.changeType(0);}}> + + + ) +}; + +const PageLinkTo = props => { + const isAndroid = Device.android; + const { t } = useTranslation(); + const _t = t('View.Add', {returnObjects: true}); + + const [stateTypeTo, setTypeTo] = useState(props.curTo); + const changeTypeTo = (type) => { + setTypeTo(type); + props.changeTo(type); + }; + + const [stateNumberTo, setNumberTo] = useState(0); + const changeNumber = (curNumber, isDecrement) => { + setTypeTo(4); + let value; + if (isDecrement) { + value = curNumber - 1; + } else { + value = curNumber + 1; + } + setNumberTo(value); + props.changeTo(4, value); + }; + return ( + + + + {changeTypeTo(0)}}> + {changeTypeTo(1)}}> + {changeTypeTo(2)}}> + {changeTypeTo(3)}}> + + {!isAndroid &&
{stateNumberTo + 1}
} +
+ + + {isAndroid && } + + +
+
+
+
+ ) +}; + +const PageLink = props => { + const { t } = useTranslation(); + const _t = t('View.Add', {returnObjects: true}); + + const [typeLink, setTypeLink] = useState(1); + const textType = typeLink === 1 ? _t.textExternalLink : _t.textSlideInThisPresentation; + const changeType = (newType) => { + setTypeLink(newType); + }; + + const [link, setLink] = useState(''); + + const [linkTo, setLinkTo] = useState(0); + const [displayTo, setDisplayTo] = useState(_t.textNextSlide); + const [numberTo, setNumberTo] = useState(0); + const changeTo = (type, number) => { + setLinkTo(type); + switch (type) { + case 0 : setDisplayTo(_t.textNextSlide); break; + case 1 : setDisplayTo(_t.textPreviousSlide); break; + case 2 : setDisplayTo(_t.textFirstSlide); break; + case 3 : setDisplayTo(_t.textLastSlide); break; + case 4 : setDisplayTo(`${_t.textSlide} ${number + 1}`); setNumberTo(number); break; + } + }; + + const display = props.getTextDisplay(); + const displayDisabled = display !== false && display === null; + const [stateDisplay, setDisplay] = useState(display !== false ? ((display !== null) ? display : _t.textDefault) : ""); + + const [screenTip, setScreenTip] = useState(''); + + return ( + + + + + {typeLink === 1 ? + {setLink(event.target.value)}} + /> : + + } + {setDisplay(event.target.value)}} + /> + {setScreenTip(event.target.value)}} + /> + + + { + props.onInsertLink(typeLink, (typeLink === 1 ? + {url: link, display: stateDisplay, tip: screenTip, displayDisabled: displayDisabled } : + {linkTo: linkTo, numberTo: numberTo, display: stateDisplay, tip: screenTip, displayDisabled: displayDisabled})); + }} + /> + + + ) +}; + +export {PageLink, + PageLinkTo, + PageTypeLink} \ No newline at end of file diff --git a/apps/presentationeditor/mobile/src/view/add/AddOther.jsx b/apps/presentationeditor/mobile/src/view/add/AddOther.jsx new file mode 100644 index 0000000000..e80478b4a2 --- /dev/null +++ b/apps/presentationeditor/mobile/src/view/add/AddOther.jsx @@ -0,0 +1,60 @@ +import React, {useState} from 'react'; +import {observer, inject} from "mobx-react"; +import {List, ListItem, Page, Navbar, Icon, ListButton, ListInput, BlockTitle, Segmented, Button} from 'framework7-react'; +import { useTranslation } from 'react-i18next'; +import {Device} from "../../../../../common/mobile/utils/device"; + +const PageTable = props => { + props.initStyleTable(); + const { t } = useTranslation(); + const _t = t('View.Add', {returnObjects: true}); + const storeTableSettings = props.storeTableSettings; + const styles = storeTableSettings.styles; + return ( + + +
+
    + {styles.map((style, index) => { + return ( +
  • {props.onStyleClick(style.templateId)}}> + +
  • + ) + })} +
+
+
+ ) +}; + +const AddOther = props => { + const { t } = useTranslation(); + const _t = t('View.Add', {returnObjects: true}); + const showInsertLink = props.storeLinkSettings.canAddLink && !props.storeFocusObjects.paragraphLocked; + return ( + + + + + + + + {showInsertLink && + + + + } + + ) +}; + +const PageAddTable = inject("storeTableSettings")(observer(PageTable)); +const AddOtherContainer = inject("storeFocusObjects", "storeLinkSettings")(observer(AddOther)); + +export {AddOtherContainer as AddOther, + PageAddTable}; \ No newline at end of file diff --git a/apps/presentationeditor/mobile/src/view/add/AddShape.jsx b/apps/presentationeditor/mobile/src/view/add/AddShape.jsx index bf408d896b..e5910762b5 100644 --- a/apps/presentationeditor/mobile/src/view/add/AddShape.jsx +++ b/apps/presentationeditor/mobile/src/view/add/AddShape.jsx @@ -5,11 +5,26 @@ import { useTranslation } from 'react-i18next'; import {Device} from '../../../../../common/mobile/utils/device'; const AddShape = props => { + const shapes = props.storeShapeSettings.getStyleGroups(); return ( - - - +
+ {shapes.map((row, indexRow) => { + return ( +
    + {row.map((shape, index) => { + return ( +
  • {props.onShapeClick(shape.type)}}> +
    +
    +
  • + ) + })} +
+ ) + })} +
) }; -export {AddShape}; \ No newline at end of file +export default inject("storeShapeSettings")(observer(AddShape)); \ No newline at end of file diff --git a/apps/presentationeditor/mobile/src/view/add/AddSlide.jsx b/apps/presentationeditor/mobile/src/view/add/AddSlide.jsx index 3a4daa85c3..96b50c2678 100644 --- a/apps/presentationeditor/mobile/src/view/add/AddSlide.jsx +++ b/apps/presentationeditor/mobile/src/view/add/AddSlide.jsx @@ -5,11 +5,24 @@ import { useTranslation } from 'react-i18next'; import {Device} from '../../../../../common/mobile/utils/device'; const AddSlide = props => { + const layouts = props.storeSlideSettings.slideLayouts; return ( - - - +
+ {layouts.map((row, rowIndex) => { + return ( +
    + {row.map((layout, index) => { + return ( +
  • {props.onSlideLayout(layout.type)}}> + +
  • + ) + })} +
+ ) + })} +
) }; -export {AddSlide}; \ No newline at end of file +export default inject("storeSlideSettings")(observer(AddSlide)); \ No newline at end of file diff --git a/apps/spreadsheeteditor/mobile/locale/en.json b/apps/spreadsheeteditor/mobile/locale/en.json index 4a18434f1c..f7c5fa5cd7 100644 --- a/apps/spreadsheeteditor/mobile/locale/en.json +++ b/apps/spreadsheeteditor/mobile/locale/en.json @@ -5,5 +5,18 @@ }, "textAnonymous": "Anonymous" } + }, + "View" : { + "Add" : { + "textChart": "Chart", + "textFormula": "Formula", + "textShape": "Shape", + "textOther": "Other" + }, + "Edit" : { + "textSelectObjectToEdit": "Select object to edit", + "textSettings": "Settings", + "textCell": "Cell" + } } } diff --git a/apps/spreadsheeteditor/mobile/src/controller/Main.jsx b/apps/spreadsheeteditor/mobile/src/controller/Main.jsx index 3aa5693cd9..0a28824c78 100644 --- a/apps/spreadsheeteditor/mobile/src/controller/Main.jsx +++ b/apps/spreadsheeteditor/mobile/src/controller/Main.jsx @@ -5,6 +5,7 @@ import { f7 } from 'framework7-react'; import { withTranslation } from 'react-i18next'; import CollaborationController from '../../../../common/mobile/lib/controller/Collaboration.jsx' +@inject("storeFocusObjects") class MainController extends Component { constructor(props) { super(props) @@ -202,6 +203,14 @@ class MainController extends Component { // me.api.asc_registerCallback('asc_onPrintUrl', _.bind(me.onPrintUrl, me)); // me.api.asc_registerCallback('asc_onDocumentName', _.bind(me.onDocumentName, me)); me.api.asc_registerCallback('asc_onEndAction', me._onLongActionEnd.bind(me)); + + const storeFocusObjects = this.props.storeFocusObjects; + this.api.asc_registerCallback('asc_onFocusObject', objects => { + storeFocusObjects.resetFocusObjects(objects); + }); + this.api.asc_registerCallback('asc_onSelectionChanged', cellInfo => { + storeFocusObjects.resetCellInfo(cellInfo); + }); } _onLongActionEnd(type, id) { diff --git a/apps/spreadsheeteditor/mobile/src/controller/add/AddChart.jsx b/apps/spreadsheeteditor/mobile/src/controller/add/AddChart.jsx new file mode 100644 index 0000000000..5e69d6d853 --- /dev/null +++ b/apps/spreadsheeteditor/mobile/src/controller/add/AddChart.jsx @@ -0,0 +1,28 @@ +import React, {Component} from 'react'; +import { f7 } from 'framework7-react'; +import {Device} from '../../../../../common/mobile/utils/device'; + +import AddChart from '../../view/add/AddChart'; + +class AddChartController extends Component { + constructor (props) { + super(props); + } + + closeModal () { + if ( Device.phone ) { + f7.sheet.close('.add-popup', true); + } else { + f7.popover.close('#add-popover'); + } + } + + render () { + return ( + + ) + } +} + +export default AddChartController; \ No newline at end of file diff --git a/apps/spreadsheeteditor/mobile/src/controller/add/AddFormula.jsx b/apps/spreadsheeteditor/mobile/src/controller/add/AddFormula.jsx new file mode 100644 index 0000000000..46959e5c29 --- /dev/null +++ b/apps/spreadsheeteditor/mobile/src/controller/add/AddFormula.jsx @@ -0,0 +1,28 @@ +import React, {Component} from 'react'; +import { f7 } from 'framework7-react'; +import {Device} from '../../../../../common/mobile/utils/device'; + +import AddFormula from '../../view/add/AddFormula'; + +class AddFormulaController extends Component { + constructor (props) { + super(props); + } + + closeModal () { + if ( Device.phone ) { + f7.sheet.close('.add-popup', true); + } else { + f7.popover.close('#add-popover'); + } + } + + render () { + return ( + + ) + } +} + +export default AddFormulaController; \ No newline at end of file diff --git a/apps/spreadsheeteditor/mobile/src/controller/edit/EditCell.jsx b/apps/spreadsheeteditor/mobile/src/controller/edit/EditCell.jsx new file mode 100644 index 0000000000..d74d69b64c --- /dev/null +++ b/apps/spreadsheeteditor/mobile/src/controller/edit/EditCell.jsx @@ -0,0 +1,20 @@ +import React, {Component} from 'react'; +import { f7 } from 'framework7-react'; +import {Device} from '../../../../../common/mobile/utils/device'; + +import EditCell from '../../view/edit/EditCell'; + +class EditCellController extends Component { + constructor (props) { + super(props); + } + + render () { + return ( + + ) + } +} + +export default EditCellController; \ No newline at end of file diff --git a/apps/spreadsheeteditor/mobile/src/less/icons-ios.less b/apps/spreadsheeteditor/mobile/src/less/icons-ios.less index 36cb30894f..ec61398a84 100644 --- a/apps/spreadsheeteditor/mobile/src/less/icons-ios.less +++ b/apps/spreadsheeteditor/mobile/src/less/icons-ios.less @@ -475,7 +475,7 @@ } } - .active { + .tab-link-active { i.icon { &.icon-add-chart { width: 24px; diff --git a/apps/spreadsheeteditor/mobile/src/page/main.jsx b/apps/spreadsheeteditor/mobile/src/page/main.jsx index 0d648d5223..5d51c19929 100644 --- a/apps/spreadsheeteditor/mobile/src/page/main.jsx +++ b/apps/spreadsheeteditor/mobile/src/page/main.jsx @@ -6,12 +6,15 @@ import Settings from '../view/settings/Settings'; import CollaborationView from '../../../../common/mobile/lib/view/Collaboration.jsx' import CellEditor from '../controller/CellEditor'; import Statusbar from '../controller/StatusBar' +import AddOptions from "../view/add/Add"; +import EditOptions from "../view/edit/Edit"; export default class MainPage extends Component { constructor(props) { super(props); this.state = { editOptionsVisible: false, + addOptionsVisible: false, settingsVisible: false, collaborationVisible: false, }; @@ -21,12 +24,12 @@ export default class MainPage extends Component { this.setState(state => { if ( opts == 'edit' ) return {editOptionsVisible: true}; - else - if ( opts == 'settings' ) + else if ( opts == 'add' ) + return {addOptionsVisible: true}; + else if ( opts == 'settings' ) return {settingsVisible: true}; - else - if ( opts == 'coauth' ) - return {collaborationVisible: true} + else if ( opts == 'coauth' ) + return {collaborationVisible: true}; }); }; @@ -35,12 +38,12 @@ export default class MainPage extends Component { await 1 && this.setState(state => { if ( opts == 'edit' ) return {editOptionsVisible: false}; - else - if ( opts == 'settings' ) + else if ( opts == 'add' ) + return {addOptionsVisible: false}; + else if ( opts == 'settings' ) return {settingsVisible: false}; - else - if ( opts == 'coauth' ) - return {collaborationVisible: false} + else if ( opts == 'coauth' ) + return {collaborationVisible: false}; }) })(); }; @@ -57,6 +60,7 @@ export default class MainPage extends Component { this.handleClickToOpenOptions('edit')}> + this.handleClickToOpenOptions('add')}> this.handleClickToOpenOptions('coauth')}> this.handleClickToOpenOptions('settings')}> @@ -64,10 +68,14 @@ export default class MainPage extends Component { {/* Page content */} - {/*{*/} - {/*!this.state.editOptionsVisible ? null :*/} - {/**/} - {/*}*/} + { + !this.state.editOptionsVisible ? null : + + } + { + !this.state.addOptionsVisible ? null : + + } { !this.state.settingsVisible ? null : diff --git a/apps/spreadsheeteditor/mobile/src/store/focusObjects.js b/apps/spreadsheeteditor/mobile/src/store/focusObjects.js new file mode 100644 index 0000000000..5d87693d9c --- /dev/null +++ b/apps/spreadsheeteditor/mobile/src/store/focusObjects.js @@ -0,0 +1,138 @@ +import {action, observable, computed} from 'mobx'; + +export class storeFocusObjects { + @observable focusOn = undefined; + + @observable _focusObjects = []; + + @action resetFocusObjects(objects) { + this.focusOn = 'obj'; + this._focusObjects = objects; + } + + @computed get objects() { + const _objects = []; + for (let object of this._focusObjects) { + const type = object.get_ObjectType(); + + if (Asc.c_oAscTypeSelectElement.Paragraph == type) { + _objects.push('text', 'paragraph'); + } else if (Asc.c_oAscTypeSelectElement.Table == type) { + _objects.push('table'); + } else if (Asc.c_oAscTypeSelectElement.Image == type) { + if (object.get_ObjectValue().get_ChartProperties()) { + _objects.push('chart'); + } else if (object.get_ObjectValue().get_ShapeProperties()) { + _objects.push('shape'); + } else { + _objects.push('image'); + } + } else if (Asc.c_oAscTypeSelectElement.Hyperlink == type) { + _objects.push('hyperlink'); + } + } + const resultArr = _objects.filter((value, index, self) => self.indexOf(value) === index); //get uniq array + // Exclude shapes if chart exist + if (resultArr.indexOf('chart') > -1) { + resultArr.splice(resultArr.indexOf('shape'), 1); + } + return resultArr; + } + + @observable _cellInfo; + + @action resetCellInfo (cellInfo) { + this.focusOn = 'cell'; + this._cellInfo = cellInfo; + } + + @computed get selections () { + const _selections = []; + + let isCell, isRow, isCol, isAll, isChart, isImage, isTextShape, isShape, isTextChart, + selType = this._cellInfo.asc_getSelectionType(), + isObjLocked = false; + + switch (selType) { + case Asc.c_oAscSelectionType.RangeCells: isCell = true; break; + case Asc.c_oAscSelectionType.RangeRow: isRow = true; break; + case Asc.c_oAscSelectionType.RangeCol: isCol = true; break; + case Asc.c_oAscSelectionType.RangeMax: isAll = true; break; + case Asc.c_oAscSelectionType.RangeImage: isImage = true; break; + case Asc.c_oAscSelectionType.RangeShape: isShape = true; break; + case Asc.c_oAscSelectionType.RangeChart: isChart = true; break; + case Asc.c_oAscSelectionType.RangeChartText:isTextChart = true; break; + case Asc.c_oAscSelectionType.RangeShapeText: isTextShape = true; break; + } + + if (isImage || isShape || isChart) { + isImage = isShape = isChart = false; + let has_chartprops = false; + let selectedObjects = Common.EditorApi.get().asc_getGraphicObjectProps(); + + for (let i = 0; i < selectedObjects.length; i++) { + if (selectedObjects[i].asc_getObjectType() == Asc.c_oAscTypeSelectElement.Image) { + const elValue = selectedObjects[i].asc_getObjectValue(); + isObjLocked = isObjLocked || elValue.asc_getLocked(); + const shapeProps = elValue.asc_getShapeProperties(); + + if (shapeProps) { + if (shapeProps.asc_getFromChart()) { + isChart = true; + } else { + isShape = true; + } + } else if (elValue.asc_getChartProperties()) { + isChart = true; + has_chartprops = true; + } else { + isImage = true; + } + } + } + } else if (isTextShape || isTextChart) { + const selectedObjects = this.api.asc_getGraphicObjectProps(); + let isEquation = false; + + for (var i = 0; i < selectedObjects.length; i++) { + const elType = selectedObjects[i].asc_getObjectType(); + if (elType == Asc.c_oAscTypeSelectElement.Image) { + const value = selectedObjects[i].asc_getObjectValue(); + isObjLocked = isObjLocked || value.asc_getLocked(); + } else if (elType == Asc.c_oAscTypeSelectElement.Paragraph) { + } else if (elType == Asc.c_oAscTypeSelectElement.Math) { + isEquation = true; + } + } + } + if (isChart || isTextChart) { + _selections.push('chart'); + + if (isTextChart) { + _selections.push('text'); + } + } else if ((isShape || isTextShape) && !isImage) { + _selections.push('shape'); + + if (isTextShape) { + _selections.push('text'); + } + } else if (isImage) { + _selections.push('image'); + + if (isShape) { + _selections.push('shape'); + } + } else { + _selections.push('cell'); + + if (this._cellInfo.asc_getHyperlink()) { + _selections.push('hyperlink'); + } + } + return _selections; + } + + + +} \ No newline at end of file diff --git a/apps/spreadsheeteditor/mobile/src/store/mainStore.js b/apps/spreadsheeteditor/mobile/src/store/mainStore.js index a54c22e561..850aea6f8e 100644 --- a/apps/spreadsheeteditor/mobile/src/store/mainStore.js +++ b/apps/spreadsheeteditor/mobile/src/store/mainStore.js @@ -1,6 +1,6 @@ // import {storeDocumentSettings} from './documentSettings'; -// import {storeFocusObjects} from "./focusObjects"; +import {storeFocusObjects} from "./focusObjects"; import {storeUsers} from '../../../../common/mobile/lib/store/users'; import {storeWorksheets} from './sheets'; // import {storeTextSettings} from "./textSettings"; @@ -11,7 +11,7 @@ import {storeWorksheets} from './sheets'; // import {storeChartSettings} from "./chartSettings"; export const stores = { - // storeFocusObjects: new storeFocusObjects(), + storeFocusObjects: new storeFocusObjects(), // storeDocumentSettings: new storeDocumentSettings(), users: new storeUsers(), sheets: new storeWorksheets() diff --git a/apps/spreadsheeteditor/mobile/src/view/add/Add.jsx b/apps/spreadsheeteditor/mobile/src/view/add/Add.jsx new file mode 100644 index 0000000000..619b8983ad --- /dev/null +++ b/apps/spreadsheeteditor/mobile/src/view/add/Add.jsx @@ -0,0 +1,122 @@ +import React, {Component, useEffect} from 'react'; +import {View,Page,Navbar,NavRight,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"; +import {Device} from '../../../../../common/mobile/utils/device'; + +import AddChartController from "../../controller/add/AddChart"; +import AddFormulaController from "../../controller/add/AddFormula"; +//import AddShapeController from "../../controller/add/AddShape"; +//import {AddOtherController} from "../../controller/add/AddOther"; + +const routes = [ +]; + +const AddLayoutNavbar = ({ tabs, inPopover }) => { + const isAndroid = Device.android; + return ( + +
+ {tabs.map((item, index) => + + + )} + {isAndroid && } +
+ { !inPopover && } +
+ ) +}; + +const AddLayoutContent = ({ tabs }) => { + return ( + + {tabs.map((item, index) => + + {item.component} + + )} + + ) +}; + +const AddTabs = props => { + const { t } = useTranslation(); + const _t = t('Add', {returnObjects: true}); + const tabs = []; + tabs.push({ + caption: _t.textChart, + id: 'add-chart', + icon: 'icon-add-chart', + component: + }); + tabs.push({ + caption: _t.textFormula, + id: 'add-formula', + icon: 'icon-add-formula', + component: + }); + /*tabs.push({ + caption: _t.textShape, + id: 'add-shape', + icon: 'icon-add-shape', + component: + }); + tabs.push({ + caption: _t.textOther, + id: 'add-other', + icon: 'icon-add-other', + component: + });*/ + return ( + + + + + + + ) +}; + +class AddView extends Component { + constructor(props) { + super(props); + + this.onoptionclick = this.onoptionclick.bind(this); + } + onoptionclick(page){ + f7.views.current.router.navigate(page); + } + render() { + const show_popover = this.props.usePopover; + return ( + show_popover ? + this.props.onclosed()}> + + : + this.props.onclosed()}> + + + ) + } +} + +const Add = props => { + useEffect(() => { + if ( Device.phone ) + f7.popup.open('.add-popup'); + else f7.popover.open('#add-popover', '#btn-add'); + + return () => { + // component will unmount + } + }); + const onviewclosed = () => { + if ( props.onclosed ) + props.onclosed(); + }; + return +}; + +export default Add; \ No newline at end of file diff --git a/apps/spreadsheeteditor/mobile/src/view/add/AddChart.jsx b/apps/spreadsheeteditor/mobile/src/view/add/AddChart.jsx new file mode 100644 index 0000000000..8eec1a782e --- /dev/null +++ b/apps/spreadsheeteditor/mobile/src/view/add/AddChart.jsx @@ -0,0 +1,12 @@ +import React, {Fragment, useState} from 'react'; +import {observer, inject} from "mobx-react"; + +const AddChart = props => { + return ( + + + + ) +}; + +export default AddChart; \ No newline at end of file diff --git a/apps/spreadsheeteditor/mobile/src/view/add/AddFormula.jsx b/apps/spreadsheeteditor/mobile/src/view/add/AddFormula.jsx new file mode 100644 index 0000000000..b8272b828e --- /dev/null +++ b/apps/spreadsheeteditor/mobile/src/view/add/AddFormula.jsx @@ -0,0 +1,12 @@ +import React, {Fragment, useState} from 'react'; +import {observer, inject} from "mobx-react"; + +const AddFormula = props => { + return ( + + + + ) +}; + +export default AddFormula; \ No newline at end of file diff --git a/apps/spreadsheeteditor/mobile/src/view/edit/Edit.jsx b/apps/spreadsheeteditor/mobile/src/view/edit/Edit.jsx new file mode 100644 index 0000000000..3ed9651ef2 --- /dev/null +++ b/apps/spreadsheeteditor/mobile/src/view/edit/Edit.jsx @@ -0,0 +1,138 @@ +import React, {useState, useEffect} from 'react'; +import {observer, inject} from "mobx-react"; +import { Popover, Sheet, View, Page, Navbar, NavRight, NavLeft, NavTitle, Tabs, Tab, Link } from 'framework7-react'; +import { f7 } from 'framework7-react'; +import { useTranslation } from 'react-i18next'; +import {Device} from '../../../../../common/mobile/utils/device'; + +import EditCellController from "../../controller/edit/EditCell"; + +const routes = [ +]; + +const EmptyEditLayout = () => { + const { t } = useTranslation(); + const _t = t('View.Edit', {returnObjects: true}); + return ( + +
+
+

{_t.textSelectObjectToEdit}

+
+
+
+ ) +}; + +const EditLayoutNavbar = ({ editors, inPopover }) => { + const isAndroid = Device.android; + const { t } = useTranslation(); + const _t = t('View.Edit', {returnObjects: true}); + return ( + + { + editors.length > 1 ? +
+ {editors.map((item, index) => {item.caption})} + {isAndroid && } +
: + { editors[0].caption } + } + { !inPopover && } +
+ ) +}; + +const EditLayoutContent = ({ editors }) => { + if (editors.length > 1) { + return ( + + {editors.map((item, index) => + + {item.component} + + )} + + ) + } else { + return ( + + {editors[0].component} + + ) + } +}; + +const EditTabs = props => { + const { t } = useTranslation(); + const _t = t('View.Edit', {returnObjects: true}); + + const store = props.storeFocusObjects; + const settings = !store.focusOn ? [] : (store.focusOn === 'obj' ? store.settings : store.selections); + let editors = []; + if (settings.length < 1) { + editors.push({ + caption: _t.textSettings, + component: + }); + } else { + if (settings.indexOf('cell') > -1) { + editors.push({ + caption: _t.textCell, + id: 'edit-text', + component: + }) + } + } + + return ( + + + + + + + + ) +}; + +const EditTabsContainer = inject("storeFocusObjects")(observer(EditTabs)); + +const EditView = props => { + const onOptionClick = (page) => { + $f7.views.current.router.navigate(page); + }; + const show_popover = props.usePopover; + return ( + show_popover ? + props.onClosed()}> + + : + props.onClosed()}> + + + ) +}; + +const EditOptions = props => { + useEffect(() => { + if ( Device.phone ) + f7.sheet.open('#edit-sheet'); + else f7.popover.open('#edit-popover', '#btn-edit'); + + return () => { + // component will unmount + } + }); + + const onviewclosed = () => { + if ( props.onclosed ) + props.onclosed(); + }; + + return ( + + ) +}; + +export default EditOptions; \ No newline at end of file diff --git a/apps/spreadsheeteditor/mobile/src/view/edit/EditCell.jsx b/apps/spreadsheeteditor/mobile/src/view/edit/EditCell.jsx new file mode 100644 index 0000000000..6299857112 --- /dev/null +++ b/apps/spreadsheeteditor/mobile/src/view/edit/EditCell.jsx @@ -0,0 +1,12 @@ +import React, {Fragment, useState} from 'react'; +import {observer, inject} from "mobx-react"; + +const EditCell = props => { + return ( + + + + ) +}; + +export default EditCell; \ No newline at end of file