Merge pull request #1569 from ONLYOFFICE/feature/table-contents
Feature/table contents
This commit is contained in:
commit
cb326bee30
|
@ -18,6 +18,7 @@
|
|||
--f7-navbar-text-color: @fill-white;
|
||||
--f7-navbar-height: 56px;
|
||||
|
||||
--f7-list-bg-color: @background-primary;
|
||||
--f7-subnavbar-bg-color: @toolbar-background;
|
||||
--f7-subnavbar-link-color: @toolbar-icons;
|
||||
--f7-subnavbar-text-color: @fill-white;
|
||||
|
@ -84,7 +85,6 @@
|
|||
--f7-input-placeholder-color: @text-secondary;
|
||||
--f7-label-text-color: @text-normal;
|
||||
--f7-page-bg-color: @background-tertiary;
|
||||
--f7-list-bg-color: @background-primary;
|
||||
--f7-list-item-border-color: @background-menu-divider;
|
||||
--f7-list-chevron-icon-color: @text-tertiary;
|
||||
--f7-toggle-inactive-color: @background-menu-divider;
|
||||
|
|
|
@ -57,7 +57,10 @@
|
|||
"textStartAt": "Start At",
|
||||
"textTable": "Table",
|
||||
"textTableSize": "Table Size",
|
||||
"txtNotUrl": "This field should be a URL in the format \"http://www.example.com\""
|
||||
"txtNotUrl": "This field should be a URL in the format \"http://www.example.com\"",
|
||||
"textTableContents": "Table of Contents",
|
||||
"textWithPageNumbers": "With Page Numbers",
|
||||
"textWithBlueLinks": "With Blue Links"
|
||||
},
|
||||
"Common": {
|
||||
"Collaboration": {
|
||||
|
@ -193,7 +196,9 @@
|
|||
"textDoNotShowAgain": "Don't show again",
|
||||
"textNumberingValue": "Numbering Value",
|
||||
"textOk": "OK",
|
||||
"textRows": "Rows"
|
||||
"textRows": "Rows",
|
||||
"textRefreshEntireTable": "Refresh entire table",
|
||||
"textRefreshPageNumbersOnly": "Refresh page numbers only"
|
||||
},
|
||||
"Edit": {
|
||||
"notcriticalErrorTitle": "Warning",
|
||||
|
@ -330,7 +335,29 @@
|
|||
"textTu": "Tu",
|
||||
"textType": "Type",
|
||||
"textWe": "We",
|
||||
"textWrap": "Wrap"
|
||||
"textWrap": "Wrap",
|
||||
"textTableOfCont": "TOC",
|
||||
"textPageNumbers": "Page Numbers",
|
||||
"textSimple": "Simple",
|
||||
"textRightAlign": "Right Align",
|
||||
"textLeader": "Leader",
|
||||
"textStructure": "Structure",
|
||||
"textRefresh": "Refresh",
|
||||
"textLevels": "Levels",
|
||||
"textRemoveTableContent": "Remove table of content",
|
||||
"textCurrent": "Current",
|
||||
"textOnline": "Online",
|
||||
"textClassic": "Classic",
|
||||
"textDistinctive": "Distinctive",
|
||||
"textCentered": "Centered",
|
||||
"textFormal": "Formal",
|
||||
"textStandard": "Standard",
|
||||
"textModern": "Modern",
|
||||
"textCancel": "Cancel",
|
||||
"textRefreshEntireTable": "Refresh entire table",
|
||||
"textRefreshPageNumbersOnly": "Refresh page numbers only",
|
||||
"textStyles": "Styles",
|
||||
"textAmountOfLevels": "Amount of Levels"
|
||||
},
|
||||
"Error": {
|
||||
"convertationTimeoutText": "Conversion timeout exceeded.",
|
||||
|
@ -616,7 +643,11 @@
|
|||
"txtScheme6": "Concourse",
|
||||
"txtScheme7": "Equity",
|
||||
"txtScheme8": "Flow",
|
||||
"txtScheme9": "Foundry"
|
||||
"txtScheme9": "Foundry",
|
||||
"textNavigation": "Navigation",
|
||||
"textEmptyScreens": "There are no headings in the document. Apply a headings style to the text so that it appeas in the table of cotents.",
|
||||
"textBeginningDocument": "Beginning of document",
|
||||
"textEmptyHeading": "Empty Heading"
|
||||
},
|
||||
"Toolbar": {
|
||||
"dlgLeaveMsgText": "You have unsaved changes. Click 'Stay on this Page' to wait for autosave. Click 'Leave this Page' to discard all the unsaved changes.",
|
||||
|
|
|
@ -114,9 +114,26 @@ class ContextMenu extends ContextMenuController {
|
|||
this.props.openOptions('coauth', 'cm-review-change');
|
||||
}, 400);
|
||||
break;
|
||||
case 'refreshEntireTable':
|
||||
this.onTableContentsUpdate('all');
|
||||
break;
|
||||
case 'refreshPageNumbers':
|
||||
this.onTableContentsUpdate('pages');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onTableContentsUpdate(type, currentTOC) {
|
||||
const api = Common.EditorApi.get();
|
||||
let props = api.asc_GetTableOfContentsPr(currentTOC);
|
||||
|
||||
if (props) {
|
||||
if (currentTOC && props)
|
||||
currentTOC = props.get_InternalClass();
|
||||
api.asc_UpdateTableOfContents(type == 'pages', currentTOC);
|
||||
}
|
||||
};
|
||||
|
||||
showCopyCutPasteModal() {
|
||||
const { t } = this.props;
|
||||
const _t = t("ContextMenu", { returnObjects: true });
|
||||
|
@ -223,6 +240,7 @@ class ContextMenu extends ContextMenuController {
|
|||
const { canViewComments, canCoAuthoring, canComments } = this.props;
|
||||
|
||||
const api = Common.EditorApi.get();
|
||||
const inToc = api.asc_GetTableOfContentsPr(true);
|
||||
const stack = api.getSelectedElements();
|
||||
const canCopy = api.can_CopyCut();
|
||||
|
||||
|
@ -294,6 +312,17 @@ class ContextMenu extends ContextMenuController {
|
|||
});
|
||||
}
|
||||
|
||||
if(inToc) {
|
||||
itemsText.push({
|
||||
caption: t('ContextMenu.textRefreshEntireTable'),
|
||||
event: 'refreshEntireTable'
|
||||
});
|
||||
itemsText.push({
|
||||
caption: t('ContextMenu.textRefreshPageNumbersOnly'),
|
||||
event: 'refreshPageNumbers'
|
||||
});
|
||||
}
|
||||
|
||||
return itemsIcon.concat(itemsText);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ import { f7 } from 'framework7-react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const ErrorController = inject('storeAppOptions')(({storeAppOptions, LoadingDocument}) => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t("Error", { returnObjects: true });
|
||||
|
||||
useEffect(() => {
|
||||
const on_engine_created = k => { k.asc_registerCallback('asc_onError', onError); };
|
||||
|
||||
|
@ -20,9 +23,6 @@ const ErrorController = inject('storeAppOptions')(({storeAppOptions, LoadingDocu
|
|||
});
|
||||
|
||||
const onError = (id, level, errData) => {
|
||||
const {t} = useTranslation();
|
||||
const _t = t("Error", { returnObjects: true });
|
||||
|
||||
if (id === Asc.c_oAscError.ID.LoadingScriptError) {
|
||||
f7.notification.create({
|
||||
title: _t.criticalErrorTitle,
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import React, {Component} from 'react';
|
||||
import { f7 } from 'framework7-react';
|
||||
import {Device} from '../../../../../common/mobile/utils/device';
|
||||
import {AddTableContents} from '../../view/add/AddTableContents';
|
||||
|
||||
class AddTableContentsController extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.onTableContents = this.onTableContents.bind(this);
|
||||
}
|
||||
|
||||
closeModal () {
|
||||
if ( Device.phone ) {
|
||||
f7.sheet.close('.add-popup', true);
|
||||
} else {
|
||||
f7.popover.close('#add-popover');
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const api = Common.EditorApi.get();
|
||||
const widthImage = !Device.phone ? 330 : window.innerWidth - 30;
|
||||
api.asc_getButtonsTOC('toc1', 'toc2', widthImage);
|
||||
}
|
||||
|
||||
onTableContents(type, currentTOC) {
|
||||
const api = Common.EditorApi.get();
|
||||
let props = api.asc_GetTableOfContentsPr(currentTOC);
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
if (!props) {
|
||||
props = new Asc.CTableOfContentsPr();
|
||||
props.put_OutlineRange(1, 9);
|
||||
}
|
||||
props.put_Hyperlink(true);
|
||||
props.put_ShowPageNumbers(true);
|
||||
props.put_RightAlignTab(true);
|
||||
props.put_TabLeader(Asc.c_oAscTabLeader.Dot);
|
||||
api.asc_AddTableOfContents(null, props);
|
||||
break;
|
||||
case 1:
|
||||
if (!props) {
|
||||
props = new Asc.CTableOfContentsPr();
|
||||
props.put_OutlineRange(1, 9);
|
||||
}
|
||||
props.put_Hyperlink(true);
|
||||
props.put_ShowPageNumbers(false);
|
||||
props.put_TabLeader(Asc.c_oAscTabLeader.None);
|
||||
props.put_StylesType(Asc.c_oAscTOCStylesType.Web);
|
||||
api.asc_AddTableOfContents(null, props);
|
||||
break;
|
||||
}
|
||||
|
||||
this.closeModal();
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<AddTableContents onTableContents={this.onTableContents} />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default AddTableContentsController;
|
|
@ -0,0 +1,277 @@
|
|||
import React, {Component} from 'react';
|
||||
import { f7 } from 'framework7-react';
|
||||
import {Device} from '../../../../../common/mobile/utils/device';
|
||||
import {observer, inject} from "mobx-react";
|
||||
import { EditTableContents } from '../../view/edit/EditTableContents';
|
||||
|
||||
class EditTableContentsController extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.startLevel = 1;
|
||||
this.endLevel = 3;
|
||||
this.fillTOCProps = this.fillTOCProps.bind(this);
|
||||
this.onRemoveTableContents = this.onRemoveTableContents.bind(this);
|
||||
this.onUpdateTableContents = this.onUpdateTableContents.bind(this);
|
||||
}
|
||||
|
||||
closeModal() {
|
||||
if (Device.phone) {
|
||||
f7.sheet.close('#edit-sheet', true);
|
||||
} else {
|
||||
f7.popover.close('#edit-popover');
|
||||
}
|
||||
}
|
||||
|
||||
getStylesImages() {
|
||||
const api = Common.EditorApi.get();
|
||||
const propsTableContents = api.asc_GetTableOfContentsPr();
|
||||
const arrValuesStyles = [Asc.c_oAscTOCStylesType.Simple, Asc.c_oAscTOCStylesType.Web, Asc.c_oAscTOCStylesType.Standard, Asc.c_oAscTOCStylesType.Modern, Asc.c_oAscTOCStylesType.Classic];
|
||||
|
||||
arrValuesStyles.forEach((value, index) => {
|
||||
let canvasElem = document.querySelector(`#image-toc-style${index}`);
|
||||
|
||||
propsTableContents.put_StylesType(value);
|
||||
api.SetDrawImagePlaceContents(canvasElem, propsTableContents);
|
||||
});
|
||||
}
|
||||
|
||||
onStyle(value) {
|
||||
const api = Common.EditorApi.get();
|
||||
const propsTableContents = api.asc_GetTableOfContentsPr();
|
||||
|
||||
propsTableContents.put_StylesType(value);
|
||||
api.asc_SetTableOfContentsPr(propsTableContents);
|
||||
}
|
||||
|
||||
onPageNumbers(value) {
|
||||
const api = Common.EditorApi.get();
|
||||
const propsTableContents = api.asc_GetTableOfContentsPr();
|
||||
|
||||
propsTableContents.put_ShowPageNumbers(value);
|
||||
api.asc_SetTableOfContentsPr(propsTableContents);
|
||||
}
|
||||
|
||||
onRightAlign(value) {
|
||||
const api = Common.EditorApi.get();
|
||||
const propsTableContents = api.asc_GetTableOfContentsPr();
|
||||
|
||||
propsTableContents.put_RightAlignTab(value);
|
||||
api.asc_SetTableOfContentsPr(propsTableContents);
|
||||
}
|
||||
|
||||
onLeader(value) {
|
||||
const api = Common.EditorApi.get();
|
||||
const propsTableContents = api.asc_GetTableOfContentsPr();
|
||||
|
||||
propsTableContents.put_TabLeader(value);
|
||||
api.asc_SetTableOfContentsPr(propsTableContents);
|
||||
}
|
||||
|
||||
onLevelsChange(value) {
|
||||
const api = Common.EditorApi.get();
|
||||
const propsTableContents = api.asc_GetTableOfContentsPr();
|
||||
|
||||
propsTableContents.clear_Styles();
|
||||
propsTableContents.put_OutlineRange(1, value);
|
||||
api.asc_SetTableOfContentsPr(propsTableContents);
|
||||
}
|
||||
|
||||
fillTOCProps(props) {
|
||||
const api = Common.EditorApi.get();
|
||||
const docStyles = api.asc_GetStylesArray();
|
||||
|
||||
let checkStyles = false,
|
||||
disableOutlines = false,
|
||||
styles = [];
|
||||
|
||||
docStyles.forEach(style => {
|
||||
let name = style.get_Name(),
|
||||
level = api.asc_GetHeadingLevel(name);
|
||||
|
||||
if (style.get_QFormat() || level >= 0) {
|
||||
styles.push({
|
||||
name: name,
|
||||
displayValue: style.get_TranslatedName(),
|
||||
allowSelected: false,
|
||||
checked: false,
|
||||
value: '',
|
||||
headerLevel: (level >= 0) ? level + 1 : -1
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
if(props) {
|
||||
let start = props.get_OutlineStart(),
|
||||
end = props.get_OutlineEnd(),
|
||||
count = props.get_StylesCount();
|
||||
|
||||
this.startLevel = start;
|
||||
this.endLevel = end;
|
||||
this.count = count;
|
||||
|
||||
if ((start < 0 || end < 0) && count < 1) {
|
||||
start = 1;
|
||||
end = 9;
|
||||
}
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
let styleName = props.get_StyleName(i),
|
||||
level = props.get_StyleLevel(i),
|
||||
rec = styles.find(style => style.name == styleName);
|
||||
|
||||
if (rec) {
|
||||
rec.checked = true;
|
||||
rec.value = level;
|
||||
if (rec.headerLevel !== level)
|
||||
disableOutlines = true;
|
||||
} else {
|
||||
styles.push({
|
||||
name: styleName,
|
||||
displayValue: styleName,
|
||||
allowSelected: false,
|
||||
checked: true,
|
||||
value: level,
|
||||
headerLevel: -1
|
||||
});
|
||||
|
||||
disableOutlines = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (start > 0 && end > 0) {
|
||||
for (let i = start; i <= end; i++) {
|
||||
let rec = styles.find(style => style.headerLevel === i);
|
||||
|
||||
if (rec) {
|
||||
rec.checked = true;
|
||||
rec.value = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let newStart = -1,
|
||||
newEnd = -1,
|
||||
emptyIndex = -1;
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
let rec = styles.find(style => style.headerLevel === i + 1);
|
||||
|
||||
if (rec) {
|
||||
let headerLevel = rec.headerLevel,
|
||||
level = rec.value;
|
||||
|
||||
if (headerLevel == level) {
|
||||
if (emptyIndex < 1) {
|
||||
if (newStart < 1)
|
||||
newStart = level;
|
||||
newEnd = level;
|
||||
} else {
|
||||
newStart = newEnd = -1;
|
||||
disableOutlines = true;
|
||||
break;
|
||||
}
|
||||
} else if (!rec.checked) {
|
||||
(newStart > 0) && (emptyIndex = i + 1);
|
||||
} else {
|
||||
newStart = newEnd = -1;
|
||||
disableOutlines = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkStyles = (disableOutlines || newStart > 1);
|
||||
} else {
|
||||
for (let i = this.startLevel; i <= this.endLevel; i++) {
|
||||
let rec = styles.find(style => style.headerLevel === i);
|
||||
|
||||
if (rec) {
|
||||
rec.checked = true;
|
||||
rec.value = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
styles.sort(function(a, b) {
|
||||
let aname = a.displayValue.toLocaleLowerCase(),
|
||||
bname = b.displayValue.toLocaleLowerCase();
|
||||
if (aname < bname) return -1;
|
||||
if (aname > bname) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return {
|
||||
styles,
|
||||
// start: this.startLevel,
|
||||
end: this.endLevel,
|
||||
count: this.count,
|
||||
// disableOutlines,
|
||||
// checkStyles
|
||||
}
|
||||
}
|
||||
|
||||
addStyles(styles, styleName, value) {
|
||||
const api = Common.EditorApi.get();
|
||||
const propsTableContents = api.asc_GetTableOfContentsPr();
|
||||
|
||||
propsTableContents.clear_Styles();
|
||||
|
||||
styles.forEach(style => {
|
||||
if(style.name === styleName) {
|
||||
propsTableContents.add_Style(styleName, value);
|
||||
} else {
|
||||
propsTableContents.add_Style(style.name, style.value);
|
||||
}
|
||||
});
|
||||
|
||||
if (propsTableContents.get_StylesCount() > 0) {
|
||||
propsTableContents.put_OutlineRange(-1, -1);
|
||||
} else {
|
||||
propsTableContents.put_OutlineRange(1, 9);
|
||||
}
|
||||
|
||||
api.asc_SetTableOfContentsPr(propsTableContents);
|
||||
}
|
||||
|
||||
onUpdateTableContents(type, currentTOC) {
|
||||
const api = Common.EditorApi.get();
|
||||
let props = api.asc_GetTableOfContentsPr(currentTOC);
|
||||
|
||||
if (props) {
|
||||
if (currentTOC && props)
|
||||
currentTOC = props.get_InternalClass();
|
||||
api.asc_UpdateTableOfContents(type == 'pages', currentTOC);
|
||||
}
|
||||
};
|
||||
|
||||
onRemoveTableContents(currentTOC) {
|
||||
const api = Common.EditorApi.get();
|
||||
currentTOC = !!currentTOC;
|
||||
let props = api.asc_GetTableOfContentsPr(currentTOC);
|
||||
|
||||
currentTOC = (currentTOC && props) ? props.get_InternalClass() : undefined;
|
||||
api.asc_RemoveTableOfContents(currentTOC);
|
||||
|
||||
this.closeModal();
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<EditTableContents
|
||||
onStyle={this.onStyle}
|
||||
onUpdateTableContents={this.onUpdateTableContents}
|
||||
onRemoveTableContents={this.onRemoveTableContents}
|
||||
onPageNumbers={this.onPageNumbers}
|
||||
onRightAlign={this.onRightAlign}
|
||||
onLeader={this.onLeader}
|
||||
onLevelsChange={this.onLevelsChange}
|
||||
fillTOCProps={this.fillTOCProps}
|
||||
addStyles={this.addStyles}
|
||||
getStylesImages={this.getStylesImages}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default EditTableContentsController;
|
|
@ -0,0 +1,83 @@
|
|||
import React, { Component } from "react";
|
||||
import { NavigationPopover, NavigationSheet } from "../../view/settings/Navigation";
|
||||
import { Device } from '../../../../../common/mobile/utils/device';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
class NavigationController extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.updateNavigation = this.updateNavigation.bind(this);
|
||||
}
|
||||
|
||||
updateNavigation() {
|
||||
const api = Common.EditorApi.get();
|
||||
const navigationObject = api.asc_ShowDocumentOutline();
|
||||
|
||||
if (!navigationObject) return;
|
||||
|
||||
const count = navigationObject.get_ElementsCount();
|
||||
const { t } = this.props;
|
||||
|
||||
let arrHeaders = [],
|
||||
prevLevel = -1,
|
||||
headerLevel = -1,
|
||||
firstHeader = !navigationObject.isFirstItemNotHeader();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
let level = navigationObject.get_Level(i),
|
||||
hasParent = true;
|
||||
if (level > prevLevel && i > 0)
|
||||
arrHeaders[i - 1]['hasSubItems'] = true;
|
||||
if (headerLevel < 0 || level <= headerLevel) {
|
||||
if (i > 0 || firstHeader)
|
||||
headerLevel = level;
|
||||
hasParent = false;
|
||||
}
|
||||
|
||||
arrHeaders.push({
|
||||
name: navigationObject.get_Text(i),
|
||||
level,
|
||||
index: i,
|
||||
hasParent,
|
||||
isEmptyItem: navigationObject.isEmptyItem(i)
|
||||
});
|
||||
|
||||
prevLevel = level;
|
||||
}
|
||||
|
||||
if (count > 0 && !firstHeader) {
|
||||
arrHeaders[0]['hasSubItems'] = false;
|
||||
arrHeaders[0]['isNotHeader'] = true;
|
||||
arrHeaders[0]['name'] = t('Settings.textBeginningDocument');
|
||||
}
|
||||
|
||||
return arrHeaders;
|
||||
}
|
||||
|
||||
onSelectItem(index) {
|
||||
const api = Common.EditorApi.get();
|
||||
const navigationObject = api.asc_ShowDocumentOutline();
|
||||
|
||||
if (navigationObject) {
|
||||
navigationObject.goto(index);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
!Device.phone ?
|
||||
<NavigationPopover
|
||||
onSelectItem={this.onSelectItem}
|
||||
updateNavigation={this.updateNavigation}
|
||||
/>
|
||||
:
|
||||
<NavigationSheet
|
||||
onSelectItem={this.onSelectItem}
|
||||
updateNavigation={this.updateNavigation}
|
||||
onclosed={this.props.onclosed}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation()(NavigationController);
|
|
@ -38,6 +38,26 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-sheet {
|
||||
border-radius: 10px 10px 0px 0px;
|
||||
&__title {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: @background-primary;
|
||||
padding-bottom: 10px;
|
||||
p {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
color: @text-normal;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Color Schemes
|
||||
|
|
|
@ -86,6 +86,25 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-sheet {
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
&__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: @background-primary;
|
||||
padding-bottom: 15px;
|
||||
padding-left: 16px;
|
||||
p {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 23px;
|
||||
color: @text-normal;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Color Schemes
|
||||
|
|
|
@ -157,4 +157,73 @@
|
|||
height: 50px;
|
||||
}
|
||||
|
||||
// Table of Contents
|
||||
|
||||
.item-contents {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.style-toc {
|
||||
&__image {
|
||||
margin: 0 15px;
|
||||
max-height: 150px;
|
||||
overflow: hidden;
|
||||
&_active {
|
||||
border: 1.5px solid @brandColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.block > .block-title:first-child, .list > .block-title:first-child {
|
||||
margin-top: var(--f7-block-margin-vertical);
|
||||
margin-left: calc(var(--f7-block-padding-horizontal) + var(--f7-safe-area-left));
|
||||
margin-right: calc(var(--f7-block-padding-horizontal) + var(--f7-safe-area-right));
|
||||
}
|
||||
|
||||
// Navigation
|
||||
|
||||
.empty-screens {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
&__icon {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
&__text {
|
||||
margin: 0 32px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.swipe-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
background-color: @background-primary;
|
||||
.icon-swipe {
|
||||
margin-top: 8px;
|
||||
width: 40px;
|
||||
height: 4px;
|
||||
background: @background-menu-divider;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-sheet {
|
||||
box-shadow: 0px -2px 20px rgba(0, 0, 0, 0.2);
|
||||
overflow: hidden;
|
||||
.sheet-modal-inner {
|
||||
background: @background-tertiary;
|
||||
}
|
||||
&.sheet-modal-bottom:before, &.sheet-modal:not(.sheet-modal-top):before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-list {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -81,6 +81,11 @@
|
|||
height: 22px;
|
||||
.encoded-svg-mask('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-1 7 22 22" fill="@{brandColor}"><g><path d="M21,18.5c0-0.3-0.1-0.6-0.7-0.9l-2.6-1.2l2.6-1.2c0.6-0.3,0.7-0.6,0.7-0.9c0-0.3-0.1-0.6-0.7-0.9l-8.9-4.1c-0.7-0.4-1.9-0.4-2.8,0l-8.9,4.1C-0.9,13.8-1,14.1-1,14.3s0.1,0.6,0.7,0.9l2.6,1.2l-2.6,1.2C-0.9,18-1,18.4-1,18.5c0,0.2,0.1,0.6,0.7,0.9l2.5,1.2l-2.5,1.2C-0.9,22.1-1,22.5-1,22.7c0,0.3,0.1,0.6,0.7,0.9l8.9,4.1c0.5,0.2,0.8,0.3,1.4,0.3s1-0.1,1.4-0.3l8.9-4.1c0.6-0.4,0.7-0.6,0.7-0.9c0-0.3-0.1-0.6-0.7-0.9l-2.5-1.2l2.5-1.2C20.9,19.2,21,18.8,21,18.5z M-0.2,14.3L-0.2,14.3c0,0,0.1-0.1,0.3-0.2L9,10c0.6-0.3,1.5-0.3,2,0l8.9,4.1c0.2,0.1,0.3,0.2,0.3,0.2l0,0c0,0-0.1,0.1-0.3,0.2L11,18.6c-0.6,0.3-1.5,0.3-2,0l-8.9-4.1C-0.1,14.4-0.2,14.3-0.2,14.3z M20.2,22.7L20.2,22.7c0,0-0.1,0.1-0.3,0.2L11,27.1c-0.6,0.3-1.5,0.3-2,0l-8.9-4.1c-0.2-0.1-0.3-0.2-0.3-0.2l0,0c0,0,0.1-0.1,0.3-0.2l3-1.5l5.5,2.6c0.7,0.4,1.9,0.4,2.8,0l5.5-2.6l3,1.5C20.1,22.7,20.2,22.7,20.2,22.7z M19.9,18.7L11,22.8c-0.6,0.3-1.5,0.3-2,0l-8.9-4.1c-0.2-0.1-0.3-0.2-0.3-0.2l0,0c0,0,0.1-0.1,0.3-0.2l3-1.5l5.5,2.6c0.7,0.4,1.9,0.4,2.8,0l5.5-2.6l3,1.5c0.2,0.1,0.3,0.2,0.3,0.2l0,0C20.2,18.5,20.1,18.6,19.9,18.7z"/></g></svg>');
|
||||
}
|
||||
&.icon-navigation {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
.encoded-svg-mask('<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M16 4H2V5H16V4Z" fill="@{brandColor}"/><path d="M19 9H5V10H19V9Z" fill="@{brandColor}"/><path d="M23 14V15H9V14H23Z" fill="@{brandColor}" /><path d="M16 20V19H2V20H16Z" fill="@{brandColor}"/></svg>');
|
||||
}
|
||||
&.icon-feedback {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
|
@ -403,6 +408,11 @@
|
|||
height: 24px;
|
||||
.encoded-svg-mask('<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.00049 18C7.00049 18.8284 6.32892 19.5 5.50049 19.5C4.67206 19.5 4.00049 18.8284 4.00049 18C4.00049 17.1716 4.67206 16.5 5.50049 16.5C6.32892 16.5 7.00049 17.1716 7.00049 18ZM13.5005 18C13.5005 18.8284 12.8289 19.5 12.0005 19.5C11.1721 19.5 10.5005 18.8284 10.5005 18C10.5005 17.1716 11.1721 16.5 12.0005 16.5C12.8289 16.5 13.5005 17.1716 13.5005 18ZM18.5005 19.5C19.3289 19.5 20.0005 18.8284 20.0005 18C20.0005 17.1716 19.3289 16.5 18.5005 16.5C17.6721 16.5 17.0005 17.1716 17.0005 18C17.0005 18.8284 17.6721 19.5 18.5005 19.5Z"/></svg>', @toolbar-segment);
|
||||
}
|
||||
&.icon-table-contents {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
.encoded-svg-mask('<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 20 13" fill="@{brandColor}"><path fill-rule="evenodd" clip-rule="evenodd" d="M17 0H0V1H17V0ZM11.9 4H0V5H11.9V4ZM0 8H17V9H0V8ZM15 12H0V13H15V12Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M20 0H19V1H20V0ZM20 4H19V5H20V4ZM19 8H20V9H19V8ZM20 12H19V13H20V12Z"/></svg>');
|
||||
}
|
||||
|
||||
&.icon-insert-comment {
|
||||
width: 24px;
|
||||
|
|
|
@ -136,6 +136,16 @@
|
|||
height: 22px;
|
||||
.encoded-svg-mask('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-1 7 22 22" fill="@{brandColor}"><g><path d="M21,18.5c0-0.3-0.1-0.6-0.7-0.9l-2.6-1.2l2.6-1.2c0.6-0.3,0.7-0.6,0.7-0.9c0-0.3-0.1-0.6-0.7-0.9l-8.9-4.1c-0.7-0.4-1.9-0.4-2.8,0l-8.9,4.1C-0.9,13.8-1,14.1-1,14.3s0.1,0.6,0.7,0.9l2.6,1.2l-2.6,1.2C-0.9,18-1,18.4-1,18.5c0,0.2,0.1,0.6,0.7,0.9l2.5,1.2l-2.5,1.2C-0.9,22.1-1,22.5-1,22.7c0,0.3,0.1,0.6,0.7,0.9l8.9,4.1c0.5,0.2,0.8,0.3,1.4,0.3s1-0.1,1.4-0.3l8.9-4.1c0.6-0.4,0.7-0.6,0.7-0.9c0-0.3-0.1-0.6-0.7-0.9l-2.5-1.2l2.5-1.2C20.9,19.2,21,18.8,21,18.5z M-0.2,14.3L-0.2,14.3c0,0,0.1-0.1,0.3-0.2L9,10c0.6-0.3,1.5-0.3,2,0l8.9,4.1c0.2,0.1,0.3,0.2,0.3,0.2l0,0c0,0-0.1,0.1-0.3,0.2L11,18.6c-0.6,0.3-1.5,0.3-2,0l-8.9-4.1C-0.1,14.4-0.2,14.3-0.2,14.3z M20.2,22.7L20.2,22.7c0,0-0.1,0.1-0.3,0.2L11,27.1c-0.6,0.3-1.5,0.3-2,0l-8.9-4.1c-0.2-0.1-0.3-0.2-0.3-0.2l0,0c0,0,0.1-0.1,0.3-0.2l3-1.5l5.5,2.6c0.7,0.4,1.9,0.4,2.8,0l5.5-2.6l3,1.5C20.1,22.7,20.2,22.7,20.2,22.7z M19.9,18.7L11,22.8c-0.6,0.3-1.5,0.3-2,0l-8.9-4.1c-0.2-0.1-0.3-0.2-0.3-0.2l0,0c0,0,0.1-0.1,0.3-0.2l3-1.5l5.5,2.6c0.7,0.4,1.9,0.4,2.8,0l5.5-2.6l3,1.5c0.2,0.1,0.3,0.2,0.3,0.2l0,0C20.2,18.5,20.1,18.6,19.9,18.7z"/></g></svg>');
|
||||
}
|
||||
&.icon-table-contents {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
.encoded-svg-mask('<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M2 12.9825V11.0175H16.9926V12.9825H2ZM2 18.9708V17.0058H16.9926V18.9708H2ZM2 5.02924H16.9926V6.99415H2V5.02924ZM19.0049 11.0175V9.9883H22V10.924L20.1749 12.9825H22V14.0117H19.0049V13.076L20.7833 11.0175H19.0049ZM19.9877 8.02339V5.02924H19.0049V4H20.9704V8.02339H19.9877ZM19.0049 17.0058V15.9766H22V20H19.0049V18.9708H20.9704V18.5029H19.9877V17.4737H20.9704V17.0058H19.0049Z" fill="@{brandColor}"/></svg>');
|
||||
}
|
||||
&.icon-navigation {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
.encoded-svg-mask('<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M16 4H2V5H16V4Z" fill="@{brandColor}"/><path d="M19 9H5V10H19V9Z" fill="@{brandColor}"/><path d="M23 14V15H9V14H23Z" fill="@{brandColor}" /><path d="M16 20V19H2V20H16Z" fill="@{brandColor}"/></svg>');
|
||||
}
|
||||
&.icon-feedback {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
|
|
|
@ -12,6 +12,7 @@ import { Device } from '../../../../common/mobile/utils/device'
|
|||
import { Search, SearchSettings } from '../controller/Search';
|
||||
import ContextMenu from '../controller/ContextMenu';
|
||||
import { Toolbar } from "../controller/Toolbar";
|
||||
import NavigationController from '../controller/settings/Navigation';
|
||||
|
||||
class MainPage extends Component {
|
||||
constructor(props) {
|
||||
|
@ -21,7 +22,8 @@ class MainPage extends Component {
|
|||
addOptionsVisible: false,
|
||||
addShowOptions: null,
|
||||
settingsVisible: false,
|
||||
collaborationVisible: false
|
||||
collaborationVisible: false,
|
||||
navigationVisible: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -44,6 +46,9 @@ class MainPage extends Component {
|
|||
} else if ( opts === 'coauth' ) {
|
||||
this.state.collaborationVisible && (opened = true);
|
||||
newState.collaborationVisible = true;
|
||||
} else if( opts === 'navigation') {
|
||||
this.state.navigationVisible && (opened = true);
|
||||
newState.navigationVisible = true;
|
||||
}
|
||||
|
||||
for (let key in this.state) {
|
||||
|
@ -75,6 +80,8 @@ class MainPage extends Component {
|
|||
return {settingsVisible: false};
|
||||
else if ( opts == 'coauth' )
|
||||
return {collaborationVisible: false};
|
||||
else if( opts == 'navigation')
|
||||
return {navigationVisible: false}
|
||||
});
|
||||
if ((opts === 'edit' || opts === 'coauth') && Device.phone) {
|
||||
f7.navbar.show('.main-navbar');
|
||||
|
@ -142,24 +149,28 @@ class MainPage extends Component {
|
|||
{/* {
|
||||
Device.phone ? null : <SearchSettings />
|
||||
} */}
|
||||
<SearchSettings useSuspense={false} />
|
||||
{
|
||||
!this.state.editOptionsVisible ? null :
|
||||
<EditOptions onclosed={this.handleOptionsViewClosed.bind(this, 'edit')} />
|
||||
}
|
||||
{
|
||||
!this.state.addOptionsVisible ? null :
|
||||
<AddOptions onclosed={this.handleOptionsViewClosed.bind(this, 'add')} showOptions={this.state.addShowOptions} />
|
||||
}
|
||||
{
|
||||
!this.state.settingsVisible ? null :
|
||||
<Settings openOptions={this.handleClickToOpenOptions.bind(this)} onclosed={this.handleOptionsViewClosed.bind(this, 'settings')} />
|
||||
}
|
||||
{
|
||||
!this.state.collaborationVisible ? null :
|
||||
<Collaboration onclosed={this.handleOptionsViewClosed.bind(this, 'coauth')} page={this.state.collaborationPage} />
|
||||
}
|
||||
{appOptions.isDocReady && <ContextMenu openOptions={this.handleClickToOpenOptions.bind(this)} /> }
|
||||
<SearchSettings useSuspense={false} />
|
||||
{
|
||||
!this.state.editOptionsVisible ? null :
|
||||
<EditOptions onclosed={this.handleOptionsViewClosed.bind(this, 'edit')} />
|
||||
}
|
||||
{
|
||||
!this.state.addOptionsVisible ? null :
|
||||
<AddOptions onclosed={this.handleOptionsViewClosed.bind(this, 'add')} showOptions={this.state.addShowOptions} />
|
||||
}
|
||||
{
|
||||
!this.state.settingsVisible ? null :
|
||||
<Settings openOptions={this.handleClickToOpenOptions.bind(this)} onclosed={this.handleOptionsViewClosed.bind(this, 'settings')} />
|
||||
}
|
||||
{
|
||||
!this.state.collaborationVisible ? null :
|
||||
<Collaboration onclosed={this.handleOptionsViewClosed.bind(this, 'coauth')} page={this.state.collaborationPage} />
|
||||
}
|
||||
{
|
||||
!this.state.navigationVisible ? null :
|
||||
<NavigationController onclosed={this.handleOptionsViewClosed.bind(this, 'navigation')} />
|
||||
}
|
||||
{appOptions.isDocReady && <ContextMenu openOptions={this.handleClickToOpenOptions.bind(this)} />}
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -34,6 +34,6 @@ export const stores = {
|
|||
storePalette: new storePalette(),
|
||||
storeReview: new storeReview(),
|
||||
storeComments: new storeComments(),
|
||||
storeToolbarSettings: new storeToolbarSettings()
|
||||
storeToolbarSettings: new storeToolbarSettings(),
|
||||
};
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import {AddOtherController} from "../../controller/add/AddOther";
|
|||
|
||||
import {PageImageLinkSettings} from "../add/AddImage";
|
||||
import {PageAddNumber, PageAddBreak, PageAddSectionBreak, PageAddFootnote} from "../add/AddOther";
|
||||
import AddTableContentsController from '../../controller/add/AddTableContents';
|
||||
|
||||
const routes = [
|
||||
// Image
|
||||
|
@ -41,6 +42,10 @@ const routes = [
|
|||
path: '/add-footnote/',
|
||||
component: PageAddFootnote,
|
||||
},
|
||||
{
|
||||
path: '/add-table-contents/',
|
||||
component: AddTableContentsController
|
||||
}
|
||||
];
|
||||
|
||||
const AddLayoutNavbar = ({ tabs, inPopover }) => {
|
||||
|
|
|
@ -209,6 +209,9 @@ const AddOther = props => {
|
|||
<Icon slot="media" icon="icon-sectionbreak"></Icon>
|
||||
</ListItem>
|
||||
}
|
||||
<ListItem title={_t.textTableContents} link="/add-table-contents/">
|
||||
<Icon slot="media" icon="icon-table-contents"></Icon>
|
||||
</ListItem>
|
||||
{(isShape || isChart) || (isText && disabledAddFootnote) ? null :
|
||||
<ListItem key='footnote' title={_t.textFootnote} link={'/add-footnote/'} routeProps={{
|
||||
getFootnoteProps: props.getFootnoteProps,
|
||||
|
|
21
apps/documenteditor/mobile/src/view/add/AddTableContents.jsx
Normal file
21
apps/documenteditor/mobile/src/view/add/AddTableContents.jsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import React, { PureComponent } 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 AddTableContents = props => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('Add', {returnObjects: true});
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<Navbar title={_t.textTableContents} backLink={_t.textBack}></Navbar>
|
||||
<BlockTitle>{_t.textWithPageNumbers}</BlockTitle>
|
||||
<div className="item-contents" id="toc1" onClick={() => props.onTableContents(0)}></div>
|
||||
<BlockTitle>{_t.textWithBlueLinks}</BlockTitle>
|
||||
<div className="item-contents" id="toc2" onClick={() => props.onTableContents(1)}></div>
|
||||
</Page>
|
||||
)
|
||||
};
|
||||
|
||||
export {AddTableContents}
|
|
@ -13,6 +13,7 @@ import EditTableController from "../../controller/edit/EditTable";
|
|||
import EditChartController from "../../controller/edit/EditChart";
|
||||
import EditHyperlinkController from "../../controller/edit/EditHyperlink";
|
||||
import EditHeaderController from "../../controller/edit/EditHeader";
|
||||
import EditTableContentsController from "../../controller/edit/EditTableContents";
|
||||
|
||||
import {PageTextFonts, PageTextAddFormatting, PageTextBulletsAndNumbers, PageTextLineSpacing, PageTextFontColor, PageTextCustomFontColor, PageTextHighlightColor} from "./EditText";
|
||||
import {ParagraphAdvSettings, PageParagraphBackColor, PageParagraphCustomColor} from "./EditParagraph";
|
||||
|
@ -20,6 +21,7 @@ import {PageShapeStyleNoFill, PageShapeStyle, PageShapeCustomFillColor, PageShap
|
|||
import {PageImageReorder, PageImageReplace, PageImageWrap, PageLinkSettings} from "./EditImage";
|
||||
import {PageTableOptions, PageTableWrap, PageTableStyle, PageTableStyleOptions, PageTableCustomFillColor, PageTableBorderColor, PageTableCustomBorderColor} from "./EditTable";
|
||||
import {PageChartDesign, PageChartDesignType, PageChartDesignStyle, PageChartDesignFill, PageChartDesignBorder, PageChartCustomFillColor, PageChartBorderColor, PageChartCustomBorderColor, PageChartWrap, PageChartReorder} from "./EditChart";
|
||||
import { PageEditLeaderTableContents, PageEditStylesTableContents, PageEditStructureTableContents } from './EditTableContents';
|
||||
|
||||
const routes = [
|
||||
//Edit text
|
||||
|
@ -183,6 +185,21 @@ const routes = [
|
|||
{
|
||||
path: '/edit-chart-custom-border-color/',
|
||||
component: PageChartCustomBorderColor,
|
||||
},
|
||||
|
||||
// Table Contents
|
||||
|
||||
{
|
||||
path: '/edit-style-table-contents/',
|
||||
component: PageEditStylesTableContents
|
||||
},
|
||||
{
|
||||
path: '/edit-leader-table-contents/',
|
||||
component: PageEditLeaderTableContents
|
||||
},
|
||||
{
|
||||
path: '/edit-structure-table-contents/',
|
||||
component: PageEditStructureTableContents
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -242,10 +259,13 @@ const EditLayoutContent = ({ editors }) => {
|
|||
const EditTabs = props => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('Edit', {returnObjects: true});
|
||||
const api = Common.EditorApi.get();
|
||||
const inToc = api.asc_GetTableOfContentsPr(true);
|
||||
|
||||
const settings = props.storeFocusObjects.settings;
|
||||
const headerType = props.storeFocusObjects.headerType;
|
||||
let editors = [];
|
||||
|
||||
if (settings.length < 1) {
|
||||
editors.push({
|
||||
caption: _t.textSettings,
|
||||
|
@ -301,6 +321,13 @@ const EditTabs = props => {
|
|||
component: <EditChartController />
|
||||
})
|
||||
}
|
||||
if(inToc) {
|
||||
editors.push({
|
||||
caption: _t.textTableOfCont,
|
||||
id: 'edit-table-contents',
|
||||
component: <EditTableContentsController />
|
||||
})
|
||||
}
|
||||
if (settings.indexOf('hyperlink') > -1) {
|
||||
editors.push({
|
||||
caption: _t.textHyperlink,
|
||||
|
|
302
apps/documenteditor/mobile/src/view/edit/EditTableContents.jsx
Normal file
302
apps/documenteditor/mobile/src/view/edit/EditTableContents.jsx
Normal file
|
@ -0,0 +1,302 @@
|
|||
import React, {Fragment, useEffect, useState } from 'react';
|
||||
import {observer, inject} from "mobx-react";
|
||||
import {f7, View, List, ListItem, Icon, Row, Button, Page, Navbar, NavRight, Segmented, BlockTitle, Link, ListButton, Toggle, Actions, ActionsButton, ActionsGroup} from 'framework7-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {Device} from '../../../../../common/mobile/utils/device';
|
||||
|
||||
const EditTableContents = props => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('Edit', {returnObjects: true});
|
||||
const api = Common.EditorApi.get();
|
||||
const propsTableContents = api.asc_GetTableOfContentsPr();
|
||||
const stylesCount = propsTableContents.get_StylesCount();
|
||||
const [type, setType] = useState(0);
|
||||
const [styleValue, setStyleValue] = useState(propsTableContents.get_StylesType());
|
||||
const [pageNumbers, setPageNumbers] = useState(propsTableContents.get_ShowPageNumbers());
|
||||
const [rightAlign, setRightAlign] = useState(propsTableContents.get_RightAlignTab());
|
||||
const [leaderValue, setLeaderValue] = useState(propsTableContents.get_TabLeader() ? propsTableContents.get_TabLeader() : Asc.c_oAscTabLeader.Dot);
|
||||
|
||||
const arrStyles = (type === 1) ? [
|
||||
{ displayValue: t('Edit.textCurrent'), value: Asc.c_oAscTOFStylesType.Current },
|
||||
{ displayValue: t('Edit.textSimple'), value: Asc.c_oAscTOFStylesType.Simple },
|
||||
{ displayValue: t('Edit.textOnline'), value: Asc.c_oAscTOFStylesType.Web },
|
||||
{ displayValue: t('Edit.textClassic'), value: Asc.c_oAscTOFStylesType.Classic },
|
||||
{ displayValue: t('Edit.textDistinctive'), value: Asc.c_oAscTOFStylesType.Distinctive },
|
||||
{ displayValue: t('Edit.textCentered'), value: Asc.c_oAscTOFStylesType.Centered },
|
||||
{ displayValue: t('Edit.textFormal'), value: Asc.c_oAscTOFStylesType.Formal }
|
||||
] : [
|
||||
{ displayValue: t('Edit.textSimple'), value: Asc.c_oAscTOCStylesType.Simple },
|
||||
{ displayValue: t('Edit.textOnline'), value: Asc.c_oAscTOCStylesType.Web },
|
||||
{ displayValue: t('Edit.textStandard'), value: Asc.c_oAscTOCStylesType.Standard },
|
||||
{ displayValue: t('Edit.textModern'), value: Asc.c_oAscTOCStylesType.Modern },
|
||||
{ displayValue: t('Edit.textClassic'), value: Asc.c_oAscTOCStylesType.Classic }
|
||||
];
|
||||
|
||||
const arrLeaders = [
|
||||
{ value: Asc.c_oAscTabLeader.None, displayValue: t('Edit.textNone') },
|
||||
{ value: Asc.c_oAscTabLeader.Dot, displayValue: '....................' },
|
||||
{ value: Asc.c_oAscTabLeader.Hyphen, displayValue: '-----------------' },
|
||||
{ value: Asc.c_oAscTabLeader.Underscore,displayValue: '__________' }
|
||||
];
|
||||
|
||||
const activeStyle = arrStyles.find(style => style.value === styleValue);
|
||||
const activeLeader = arrLeaders.find(leader => leader.value === leaderValue);
|
||||
|
||||
const openActionsButtonsRefresh = () => {
|
||||
f7.actions.create({
|
||||
buttons: [
|
||||
[
|
||||
{
|
||||
text: t('Edit.textRefreshEntireTable'),
|
||||
onClick: () => props.onUpdateTableContents('all')
|
||||
},
|
||||
{
|
||||
text: t('Edit.textRefreshPageNumbersOnly'),
|
||||
onClick: () => props.onUpdateTableContents('pages')
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
text: t('Edit.textCancel'),
|
||||
bold: true
|
||||
}
|
||||
]
|
||||
]
|
||||
}).open();
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<List>
|
||||
<ListItem title={t('Edit.textStyle')} link="/edit-style-table-contents/" after={activeStyle.displayValue} routeProps={{
|
||||
onStyle: props.onStyle,
|
||||
arrStyles,
|
||||
setStyleValue,
|
||||
styleValue,
|
||||
getStylesImages: props.getStylesImages
|
||||
}}></ListItem>
|
||||
</List>
|
||||
<List>
|
||||
<ListItem>
|
||||
<span>{t('Edit.textPageNumbers')}</span>
|
||||
<Toggle checked={pageNumbers} onToggleChange={() => {
|
||||
setPageNumbers(!pageNumbers);
|
||||
props.onPageNumbers(!pageNumbers);
|
||||
}}></Toggle>
|
||||
</ListItem>
|
||||
{pageNumbers &&
|
||||
<ListItem>
|
||||
<span>{t('Edit.textRightAlign')}</span>
|
||||
<Toggle checked={rightAlign} onToggleChange={() => {
|
||||
setRightAlign(!rightAlign);
|
||||
props.onRightAlign(!rightAlign);
|
||||
}}></Toggle>
|
||||
</ListItem>
|
||||
}
|
||||
{(pageNumbers && rightAlign) &&
|
||||
<ListItem title={t('Edit.textLeader')} link="/edit-leader-table-contents/"
|
||||
after={activeLeader ? activeLeader.displayValue : arrLeaders[0].displayValue} routeProps={{
|
||||
onLeader: props.onLeader,
|
||||
arrLeaders,
|
||||
leaderValue,
|
||||
setLeaderValue
|
||||
}}></ListItem>
|
||||
}
|
||||
<ListItem title={t('Edit.textStructure')} link="/edit-structure-table-contents/" after={stylesCount ? t('Edit.textStyles') : t('Edit.textLevels')} routeProps={{
|
||||
onLevelsChange: props.onLevelsChange,
|
||||
fillTOCProps: props.fillTOCProps,
|
||||
addStyles: props.addStyles
|
||||
}}></ListItem>
|
||||
</List>
|
||||
<List className="buttons-list">
|
||||
<ListButton className={'button-fill button-raised'} title={t('Edit.textRefresh')}
|
||||
onClick={() => openActionsButtonsRefresh()} />
|
||||
<ListButton className='button-red button-fill button-raised' title={t('Edit.textRemoveTableContent')} onClick={() => props.onRemoveTableContents()} />
|
||||
</List>
|
||||
</Fragment>
|
||||
)
|
||||
};
|
||||
|
||||
const PageEditStylesTableContents = props => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('Edit', {returnObjects: true});
|
||||
const [styleValue, setStyleValue] = useState(props.styleValue);
|
||||
const widthImage = !Device.phone ? '330px' : window.innerWidth - 30 + 'px';
|
||||
|
||||
useEffect(() => {
|
||||
props.getStylesImages();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<Navbar title={_t.textStyle} backLink={_t.textBack}>
|
||||
{Device.phone &&
|
||||
<NavRight>
|
||||
<Link sheetClose='#edit-sheet'>
|
||||
<Icon icon='icon-expand-down'/>
|
||||
</Link>
|
||||
</NavRight>
|
||||
}
|
||||
</Navbar>
|
||||
<List>
|
||||
{props.arrStyles.map((style, index) => {
|
||||
return (
|
||||
<div className='style-toc' key={index} onClick={() => {
|
||||
setStyleValue(style.value);
|
||||
props.setStyleValue(style.value);
|
||||
props.onStyle(style.value)
|
||||
}}>
|
||||
<BlockTitle>{style.displayValue}</BlockTitle>
|
||||
<div className={'style-toc__image ' + (style.value === styleValue ? 'style-toc__image_active' : '')}>
|
||||
<canvas style={{width: widthImage, height: '10px'}} id={`image-toc-style${index}`}></canvas>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
const PageEditLeaderTableContents = props => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('Edit', {returnObjects: true});
|
||||
const [leaderValue, setLeaderValue] = useState(props.leaderValue);
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<Navbar title={t('Edit.textLeader')} backLink={_t.textBack}>
|
||||
{Device.phone &&
|
||||
<NavRight>
|
||||
<Link sheetClose='#edit-sheet'>
|
||||
<Icon icon='icon-expand-down'/>
|
||||
</Link>
|
||||
</NavRight>
|
||||
}
|
||||
</Navbar>
|
||||
<List>
|
||||
{props.arrLeaders.map((leader, index) => {
|
||||
return (
|
||||
<ListItem key={index} radio title={leader.displayValue} checked={leaderValue === leader.value} onClick={() => {
|
||||
setLeaderValue(leader.value);
|
||||
props.setLeaderValue(leader.value);
|
||||
props.onLeader(leader.value);
|
||||
}}></ListItem>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
const PageEditStructureTableContents = props => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('Edit', {returnObjects: true});
|
||||
const isAndroid = Device.android;
|
||||
const api = Common.EditorApi.get();
|
||||
const propsTableContents = api.asc_GetTableOfContentsPr();
|
||||
const {styles, end, count} = props.fillTOCProps(propsTableContents);
|
||||
const chosenStyles = styles.filter(style => style.checked);
|
||||
|
||||
const [structure, setStructure] = useState(count ? 1 : 0);
|
||||
const [amountLevels, setAmountLevels] = useState(end);
|
||||
|
||||
const addNewStyle = (style) => {
|
||||
let indexStyle = chosenStyles.findIndex(currentStyle => currentStyle.name === style.name);
|
||||
|
||||
if(indexStyle === -1) {
|
||||
chosenStyles.push(style);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<Navbar title={t('Edit.textStructure')} backLink={_t.textBack}>
|
||||
{Device.phone &&
|
||||
<NavRight>
|
||||
<Link sheetClose='#edit-sheet'>
|
||||
<Icon icon='icon-expand-down'/>
|
||||
</Link>
|
||||
</NavRight>
|
||||
}
|
||||
</Navbar>
|
||||
<List>
|
||||
<ListItem radio checked={structure === 0} title={t('Edit.textLevels')} onClick={() => setStructure(0)}></ListItem>
|
||||
<ListItem radio checked={structure === 1} title={t('Edit.textStyles')} onClick={() => setStructure(1)}></ListItem>
|
||||
</List>
|
||||
{structure === 0 ?
|
||||
<List>
|
||||
<ListItem title={t('Edit.textAmountOfLevels')}>
|
||||
{!isAndroid && <div slot='after-start'>{amountLevels === -1 ? '-' : amountLevels}</div>}
|
||||
<div slot='after'>
|
||||
<Segmented>
|
||||
<Button outline className='decrement item-link' onClick={() => {
|
||||
if(amountLevels > 1) {
|
||||
setAmountLevels(amountLevels - 1);
|
||||
props.onLevelsChange(amountLevels - 1);
|
||||
}
|
||||
}}>
|
||||
{isAndroid ? <Icon icon="icon-expand-down"></Icon> : ' - '}
|
||||
</Button>
|
||||
{isAndroid && <label>{amountLevels === -1 ? '-' : amountLevels}</label>}
|
||||
<Button outline className='increment item-link' onClick={() => {
|
||||
if(amountLevels < 9) {
|
||||
if(amountLevels === -1) {
|
||||
setAmountLevels(9);
|
||||
props.onLevelsChange(9);
|
||||
} else {
|
||||
setAmountLevels(amountLevels + 1);
|
||||
props.onLevelsChange(amountLevels + 1);
|
||||
}
|
||||
}
|
||||
}}>
|
||||
{isAndroid ? <Icon icon="icon-expand-up"></Icon> : ' + '}
|
||||
</Button>
|
||||
</Segmented>
|
||||
</div>
|
||||
</ListItem>
|
||||
</List>
|
||||
:
|
||||
<List>
|
||||
{styles.map((style, index) => {
|
||||
return (
|
||||
<ListItem checkbox key={index} title={style.displayValue} checked={style.checked}>
|
||||
{!isAndroid && <div slot='after-start'>{style.value}</div>}
|
||||
<div slot='after'>
|
||||
<Segmented>
|
||||
<Button outline className='decrement item-link' onClick={() => {
|
||||
if(style.value > 1) {
|
||||
setAmountLevels(-1);
|
||||
addNewStyle(style);
|
||||
props.addStyles(chosenStyles, style.name, style.value - 1);
|
||||
}
|
||||
}}>
|
||||
{isAndroid ? <Icon icon="icon-expand-down"></Icon> : ' - '}
|
||||
</Button>
|
||||
{isAndroid && <label>{style.value}</label>}
|
||||
<Button outline className='increment item-link' onClick={() => {
|
||||
if(style.value < 9) {
|
||||
setAmountLevels(-1);
|
||||
addNewStyle(style);
|
||||
props.addStyles(chosenStyles, style.name, style.value + 1);
|
||||
}
|
||||
}}>
|
||||
{isAndroid ? <Icon icon="icon-expand-up"></Icon> : ' + '}
|
||||
</Button>
|
||||
</Segmented>
|
||||
</div>
|
||||
</ListItem>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
}
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
EditTableContents,
|
||||
PageEditStylesTableContents,
|
||||
PageEditLeaderTableContents,
|
||||
PageEditStructureTableContents
|
||||
};
|
122
apps/documenteditor/mobile/src/view/settings/Navigation.jsx
Normal file
122
apps/documenteditor/mobile/src/view/settings/Navigation.jsx
Normal file
|
@ -0,0 +1,122 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Device } from '../../../../../common/mobile/utils/device';
|
||||
import {f7, View, List, ListItem, Icon, Row, Button, Page, Navbar, NavRight, Segmented, BlockTitle, Link, ListButton, Toggle, Actions, ActionsButton, ActionsGroup, Sheet} from 'framework7-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const NavigationPopover = props => {
|
||||
const { t } = useTranslation();
|
||||
const _t = t('Settings', {returnObjects: true});
|
||||
const api = Common.EditorApi.get();
|
||||
const navigationObject = api.asc_ShowDocumentOutline();
|
||||
const [currentPosition, setCurrentPosition] = useState(navigationObject.get_CurrentPosition());
|
||||
const arrHeaders = props.updateNavigation();
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<Navbar title={t('Settings.textNavigation')} backLink={_t.textBack} />
|
||||
{!arrHeaders || !arrHeaders.length
|
||||
?
|
||||
<div className="empty-screens">
|
||||
<p className="empty-screens__text">{t('Settings.textEmptyScreens')}</p>
|
||||
</div>
|
||||
:
|
||||
<List className="navigation-list">
|
||||
{arrHeaders.map((header, index) => {
|
||||
return (
|
||||
<ListItem radio key={index} title={header.isEmptyItem ? t('Settings.textEmptyHeading') : header.name} checked={header.index === currentPosition} style={{paddingLeft: header.level * 16}} onClick={() => {
|
||||
setCurrentPosition(header.index);
|
||||
props.onSelectItem(header.index);
|
||||
}}></ListItem>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
}
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
const NavigationSheet = props => {
|
||||
const { t } = useTranslation();
|
||||
const api = Common.EditorApi.get();
|
||||
const navigationObject = api.asc_ShowDocumentOutline();
|
||||
const [currentPosition, setCurrentPosition] = useState(navigationObject.get_CurrentPosition());
|
||||
const arrHeaders = props.updateNavigation();
|
||||
|
||||
const [stateHeight, setHeight] = useState('45%');
|
||||
const [stateOpacity, setOpacity] = useState(1);
|
||||
|
||||
const [stateStartY, setStartY] = useState();
|
||||
const [isNeedClose, setNeedClose] = useState(false);
|
||||
|
||||
const handleTouchStart = (event) => {
|
||||
const touchObj = event.changedTouches[0];
|
||||
setStartY(parseInt(touchObj.clientY));
|
||||
};
|
||||
|
||||
const handleTouchMove = (event) => {
|
||||
const touchObj = event.changedTouches[0];
|
||||
const dist = parseInt(touchObj.clientY) - stateStartY;
|
||||
|
||||
if (dist < 0) {
|
||||
setHeight('90%');
|
||||
setOpacity(1);
|
||||
setNeedClose(false);
|
||||
} else if (dist < 80) {
|
||||
setHeight('45%');
|
||||
setOpacity(1);
|
||||
setNeedClose(false);
|
||||
} else {
|
||||
setNeedClose(true);
|
||||
setOpacity(0.6);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchEnd = (event) => {
|
||||
const touchObj = event.changedTouches[0];
|
||||
const swipeEnd = parseInt(touchObj.clientY);
|
||||
const dist = swipeEnd - stateStartY;
|
||||
|
||||
if (isNeedClose) {
|
||||
f7.sheet.close('#view-navigation-sheet');
|
||||
} else if (stateHeight === '90%' && dist > 20) {
|
||||
setHeight('45%');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
f7.sheet.open('#view-navigation-sheet', true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Sheet id="view-navigation-sheet" className="navigation-sheet" backdrop={false} closeByBackdropClick={false} closeByOutsideClick={true} onSheetClosed={() => props.onclosed()} style={{height: `${stateHeight}`, opacity: `${stateOpacity}`}}>
|
||||
<div id='swipe-handler' className='swipe-container' onTouchStart={handleTouchStart} onTouchMove={handleTouchMove} onTouchEnd={handleTouchEnd}>
|
||||
<Icon icon='icon icon-swipe'/>
|
||||
</div>
|
||||
<div className="navigation-sheet__title">
|
||||
<p>{t('Settings.textNavigation')}</p>
|
||||
</div>
|
||||
{!arrHeaders || !arrHeaders.length
|
||||
?
|
||||
<div className="empty-screens">
|
||||
<p className="empty-screens__text">{t('Settings.textEmptyScreens')}</p>
|
||||
</div>
|
||||
:
|
||||
<List className="navigation-list">
|
||||
{arrHeaders.map((header, index) => {
|
||||
return (
|
||||
<ListItem radio key={index} title={header.isEmptyItem ? t('Settings.textEmptyHeading') : header.name} checked={header.index === currentPosition} style={{paddingLeft: header.level * 16}} onClick={() => {
|
||||
setCurrentPosition(header.index);
|
||||
props.onSelectItem(header.index);
|
||||
}}></ListItem>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
}
|
||||
</Sheet>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
NavigationPopover,
|
||||
NavigationSheet
|
||||
};
|
|
@ -12,6 +12,7 @@ import ApplicationSettingsController from "../../controller/settings/Application
|
|||
import { DocumentFormats, DocumentMargins, DocumentColorSchemes } from "./DocumentSettings";
|
||||
import { MacrosSettings } from "./ApplicationSettings";
|
||||
import About from '../../../../../common/mobile/lib/view/About';
|
||||
import NavigationController from '../../controller/settings/Navigation';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
|
@ -53,6 +54,13 @@ const routes = [
|
|||
{
|
||||
path: '/about/',
|
||||
component: About
|
||||
},
|
||||
|
||||
// Navigation
|
||||
|
||||
{
|
||||
path: '/navigation/',
|
||||
component: NavigationController
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -79,9 +87,14 @@ const SettingsList = inject("storeAppOptions", "storeReview")(observer(props =>
|
|||
}
|
||||
};
|
||||
|
||||
const onOpenCollaboration = async () => {
|
||||
await closeModal();
|
||||
await props.openOptions('coauth');
|
||||
const onOpenCollaboration = () => {
|
||||
closeModal();
|
||||
props.openOptions('coauth');
|
||||
}
|
||||
|
||||
const onOpenNavigation = () => {
|
||||
closeModal();
|
||||
props.openOptions('navigation');
|
||||
}
|
||||
|
||||
// set mode
|
||||
|
@ -121,6 +134,14 @@ const SettingsList = inject("storeAppOptions", "storeReview")(observer(props =>
|
|||
<Icon slot="media" icon="icon-search"></Icon>
|
||||
</ListItem>
|
||||
}
|
||||
<ListItem title={t('Settings.textNavigation')} link='#' onClick={() => {
|
||||
if(Device.phone) {
|
||||
onOpenNavigation();
|
||||
} else {
|
||||
onoptionclick.bind(this, "/navigation/")();
|
||||
}}}>
|
||||
<Icon slot="media" icon="icon-navigation"></Icon>
|
||||
</ListItem>
|
||||
{window.matchMedia("(max-width: 359px)").matches ?
|
||||
<ListItem title={_t.textCollaboration} link="#" onClick={onOpenCollaboration} className='no-indicator'>
|
||||
<Icon slot="media" icon="icon-collaboration"></Icon>
|
||||
|
|
Loading…
Reference in a new issue