From 020126898a8d19e7020e889476732134b89447d1 Mon Sep 17 00:00:00 2001
From: SergeyEzhin <ezhin@mail.ru>
Date: Thu, 28 Jan 2021 18:07:00 +0300
Subject: [PATCH] [PE mobile] Added Chart Settings and small edits

---
 apps/presentationeditor/mobile/locale/en.json |   3 +-
 .../mobile/src/controller/Main.jsx            |  12 +-
 .../mobile/src/controller/edit/EditChart.jsx  | 171 +++++++++
 .../mobile/src/controller/edit/EditImage.jsx  |   1 -
 .../mobile/src/store/chartSettings.js         | 152 ++++++++
 .../mobile/src/store/focusObjects.js          |  26 ++
 .../mobile/src/store/mainStore.js             |   7 +-
 .../mobile/src/view/edit/Edit.jsx             |  35 +-
 .../mobile/src/view/edit/EditChart.jsx        | 341 ++++++++++++++++++
 .../mobile/src/view/edit/EditTable.jsx        |   6 +-
 10 files changed, 742 insertions(+), 12 deletions(-)
 create mode 100644 apps/presentationeditor/mobile/src/controller/edit/EditChart.jsx
 create mode 100644 apps/presentationeditor/mobile/src/store/chartSettings.js
 create mode 100644 apps/presentationeditor/mobile/src/view/edit/EditChart.jsx

diff --git a/apps/presentationeditor/mobile/locale/en.json b/apps/presentationeditor/mobile/locale/en.json
index d7cdebf9a..ea3358380 100644
--- a/apps/presentationeditor/mobile/locale/en.json
+++ b/apps/presentationeditor/mobile/locale/en.json
@@ -197,7 +197,8 @@
       "textBandedColumn": "Banded Column",
       "textStyleOptions": "Style Options",
       "textRemoveTable": "Remove Table",
-      "textCellMargins": "Cell Margins"
+      "textCellMargins": "Cell Margins",
+      "textRemoveChart": "Remove Chart"
     }
   },
   "Common": {
diff --git a/apps/presentationeditor/mobile/src/controller/Main.jsx b/apps/presentationeditor/mobile/src/controller/Main.jsx
index 24086eccc..902dca3d7 100644
--- a/apps/presentationeditor/mobile/src/controller/Main.jsx
+++ b/apps/presentationeditor/mobile/src/controller/Main.jsx
@@ -5,7 +5,7 @@ import { f7 } from "framework7-react";
 import { withTranslation } from 'react-i18next';
 import CollaborationController from '../../../../common/mobile/lib/controller/Collaboration.jsx'
 
-@inject("storeFocusObjects", "storeAppOptions", "storePresentationInfo", "storePresentationSettings", "storeSlideSettings", "storeTextSettings", "storeTableSettings")
+@inject("storeFocusObjects", "storeAppOptions", "storePresentationInfo", "storePresentationSettings", "storeSlideSettings", "storeTextSettings", "storeTableSettings", "storeChartSettings")
 class MainController extends Component {
     constructor(props) {
         super(props)
@@ -306,6 +306,16 @@ class MainController extends Component {
         this.api.asc_registerCallback('asc_onInitTableTemplates', (templates) => {
             storeTableSettings.initTableTemplates(templates);
         });
+
+        // Chart settings
+
+        const storeChartSettings = this.props.storeChartSettings;
+
+        this.api.asc_registerCallback('asc_onUpdateChartStyles', () => {
+            if (storeFocusObjects.chartObject) {
+                storeChartSettings.updateChartStyles(this.api.asc_getChartPreviews(storeFocusObjects.chartObject.getType()));
+            }
+        });
     }
 
     _onDocumentContentReady() {
diff --git a/apps/presentationeditor/mobile/src/controller/edit/EditChart.jsx b/apps/presentationeditor/mobile/src/controller/edit/EditChart.jsx
new file mode 100644
index 000000000..10274945a
--- /dev/null
+++ b/apps/presentationeditor/mobile/src/controller/edit/EditChart.jsx
@@ -0,0 +1,171 @@
+import React, {Component} from 'react';
+import { f7 } from 'framework7-react';
+import {Device} from '../../../../../common/mobile/utils/device';
+import {observer, inject} from "mobx-react";
+
+import { EditChart } from '../../view/edit/EditChart'
+
+class EditChartController extends Component {
+    constructor (props) {
+        super(props);
+        this.onType = this.onType.bind(this);
+        this.onBorderColor = this.onBorderColor.bind(this);
+        this.onBorderSize = this.onBorderSize.bind(this);
+
+        const api = Common.EditorApi.get();
+        props.storeChartSettings.updateChartStyles(api.asc_getChartPreviews(props.storeFocusObjects.chartObject.getType()));
+    }
+
+    onRemoveChart () {
+        const api = Common.EditorApi.get();
+        api.asc_Remove();
+
+        if ( Device.phone ) {
+            f7.sheet.close('#edit-sheet', true);
+        } else {
+            f7.popover.close('#edit-popover');
+        } 
+    }
+
+    onReorder(type) {
+        const api = Common.EditorApi.get();
+
+        switch(type) {
+            case 'all-up':
+                api.shapes_bringToFront();
+                break;
+            case 'all-down':
+                api.shapes_bringToBack();
+                break;
+            case 'move-up':
+                api.shapes_bringForward();
+                break;
+            case 'move-down':
+                api.shapes_bringBackward();
+                break;
+        }
+    }
+
+    onAlign(type) {
+        const api = Common.EditorApi.get();
+
+        switch(type) {
+            case 'align-left':
+                api.put_ShapesAlign(Asc.c_oAscAlignShapeType.ALIGN_LEFT);
+                break;
+            case 'align-center':
+                api.put_ShapesAlign(Asc.c_oAscAlignShapeType.ALIGN_CENTER);
+                break;
+            case 'align-right':
+                api.put_ShapesAlign(Asc.c_oAscAlignShapeType.ALIGN_RIGHT);
+                break;
+            case 'align-top':
+                api.put_ShapesAlign(Asc.c_oAscAlignShapeType.ALIGN_TOP);
+                break;
+            case 'align-middle':
+                api.put_ShapesAlign(Asc.c_oAscAlignShapeType.ALIGN_MIDDLE);
+                break;
+            case 'align-bottom':
+                api.put_ShapesAlign(Asc.c_oAscAlignShapeType.ALIGN_BOTTOM);
+                break;
+            case 'distrib-hor':
+                api.DistributeHorizontally();
+                break;
+            case 'distrib-vert':
+                api.DistributeVertically();
+                break;
+        }
+    }
+
+    onStyle (style) {
+        const api = Common.EditorApi.get();
+        let chart = new Asc.CAscChartProp();
+        chart.putStyle(style);
+        api.ChartApply(chart);
+    }
+
+    onType (type) {
+        const api = Common.EditorApi.get();
+        let chart = new Asc.CAscChartProp();
+        chart.changeType(type);
+        api.ChartApply(chart);
+        // Force update styles
+        this.props.storeChartSettings.updateChartStyles(api.asc_getChartPreviews(chart.getType()));
+    }
+
+    onFillColor (color) {
+        const api = Common.EditorApi.get();
+        const shape = new Asc.asc_CShapeProperty();
+        const fill = new Asc.asc_CShapeFill();
+
+        if (color == 'transparent') {
+            fill.put_type(Asc.c_oAscFill.FILL_TYPE_NOFILL);
+            fill.put_fill(null);
+        } else {
+            fill.put_type(Asc.c_oAscFill.FILL_TYPE_SOLID);
+            fill.put_fill(new Asc.asc_CFillSolid());
+            fill.get_fill().put_color(Common.Utils.ThemeColor.getRgbColor(color));
+        }
+
+        shape.put_fill(fill);
+        api.ShapeApply(shape);
+    }
+
+    onBorderColor (color) {
+        const api = Common.EditorApi.get();
+        const currentShape = this.props.storeFocusObjects.shapeObject;
+        const shape = new Asc.asc_CShapeProperty();
+        const stroke = new Asc.asc_CStroke();
+
+        if (currentShape.get_stroke().get_width() < 0.01) {
+            stroke.put_type(Asc.c_oAscStrokeType.STROKE_NONE);
+        } else {
+            stroke.put_type(Asc.c_oAscStrokeType.STROKE_COLOR);
+            stroke.put_color(Common.Utils.ThemeColor.getRgbColor(color));
+            stroke.put_width(currentShape.get_stroke().get_width());
+            stroke.asc_putPrstDash(currentShape.get_stroke().asc_getPrstDash());
+        }
+
+        shape.put_stroke(stroke);
+        api.ShapeApply(shape);
+    }
+
+    onBorderSize (value) {
+        const api = Common.EditorApi.get();
+        const shape = new Asc.asc_CShapeProperty();
+        const stroke = new Asc.asc_CStroke();
+
+        const _borderColor = this.props.storeChartSettings.borderColor;
+
+        if (value < 0.01) {
+            stroke.put_type(Asc.c_oAscStrokeType.STROKE_NONE);
+        } else {
+            stroke.put_type(Asc.c_oAscStrokeType.STROKE_COLOR);
+            if (_borderColor == 'transparent')
+                stroke.put_color(Common.Utils.ThemeColor.getRgbColor({color: '000000', effectId: 29}));
+            else
+                stroke.put_color(Common.Utils.ThemeColor.getRgbColor(Common.Utils.ThemeColor.colorValue2EffectId(_borderColor)));
+            stroke.put_width(value * 25.4 / 72.0);
+        }
+
+        shape.put_stroke(stroke);
+        api.ShapeApply(shape);
+        this.props.storeChartSettings.initBorderColor(this.props.storeFocusObjects.shapeObject.get_stroke()); // when select STROKE_NONE or change from STROKE_NONE to STROKE_COLOR
+    }
+
+    render () {
+        return (
+            <EditChart onRemoveChart={this.onRemoveChart}
+                       onAlign={this.onAlign}
+                       onReorder={this.onReorder}
+                       onStyle={this.onStyle}
+                       onType={this.onType}
+                       onFillColor={this.onFillColor}
+                       onBorderColor={this.onBorderColor}
+                       onBorderSize={this.onBorderSize}
+            />
+        )
+    }
+}
+
+export default inject("storeChartSettings", "storeFocusObjects")(observer(EditChartController));
\ No newline at end of file
diff --git a/apps/presentationeditor/mobile/src/controller/edit/EditImage.jsx b/apps/presentationeditor/mobile/src/controller/edit/EditImage.jsx
index 4ec1991f3..eb607e1f6 100644
--- a/apps/presentationeditor/mobile/src/controller/edit/EditImage.jsx
+++ b/apps/presentationeditor/mobile/src/controller/edit/EditImage.jsx
@@ -29,7 +29,6 @@ class EditImageController extends Component {
             case 'move-down':
                 api.shapes_bringBackward();
                 break;
-            
         }
     }
 
diff --git a/apps/presentationeditor/mobile/src/store/chartSettings.js b/apps/presentationeditor/mobile/src/store/chartSettings.js
new file mode 100644
index 000000000..622cd0c43
--- /dev/null
+++ b/apps/presentationeditor/mobile/src/store/chartSettings.js
@@ -0,0 +1,152 @@
+import {action, observable, computed} from 'mobx';
+
+export class storeChartSettings {
+    
+    // Style
+    
+    @observable chartStyles;
+
+    @action updateChartStyles (styles) {
+        this.chartStyles = styles;
+    }
+
+    @computed get styles () {
+        const widthContainer = document.querySelector(".page-content").clientWidth;
+        const columns = parseInt(widthContainer / 70); // magic
+        let row = -1;
+        const styles = [];
+        
+        this.chartStyles.forEach((style, index) => {
+            if (0 == index % columns) {
+                styles.push([]);
+                row++
+            }
+            styles[row].push(style);
+        });
+
+        return styles;
+    }
+
+    @computed get types () {
+        const types = [
+            { type: Asc.c_oAscChartTypeSettings.barNormal,               thumb: 'chart-03.png'},
+            { type: Asc.c_oAscChartTypeSettings.barStacked,              thumb: 'chart-02.png'},
+            { type: Asc.c_oAscChartTypeSettings.barStackedPer,           thumb: 'chart-01.png'},
+            { type: Asc.c_oAscChartTypeSettings.lineNormal,              thumb: 'chart-06.png'},
+            { type: Asc.c_oAscChartTypeSettings.lineStacked,             thumb: 'chart-05.png'},
+            { type: Asc.c_oAscChartTypeSettings.lineStackedPer,          thumb: 'chart-04.png'},
+            { type: Asc.c_oAscChartTypeSettings.hBarNormal,              thumb: 'chart-09.png'},
+            { type: Asc.c_oAscChartTypeSettings.hBarStacked,             thumb: 'chart-08.png'},
+            { type: Asc.c_oAscChartTypeSettings.hBarStackedPer,          thumb: 'chart-07.png'},
+            { type: Asc.c_oAscChartTypeSettings.areaNormal,              thumb: 'chart-12.png'},
+            { type: Asc.c_oAscChartTypeSettings.areaStacked,             thumb: 'chart-11.png'},
+            { type: Asc.c_oAscChartTypeSettings.areaStackedPer,          thumb: 'chart-10.png'},
+            { type: Asc.c_oAscChartTypeSettings.pie,                     thumb: 'chart-13.png'},
+            { type: Asc.c_oAscChartTypeSettings.doughnut,                thumb: 'chart-14.png'},
+            { type: Asc.c_oAscChartTypeSettings.pie3d,                   thumb: 'chart-22.png'},
+            { type: Asc.c_oAscChartTypeSettings.scatter,                 thumb: 'chart-15.png'},
+            { type: Asc.c_oAscChartTypeSettings.stock,                   thumb: 'chart-16.png'},
+            { type: Asc.c_oAscChartTypeSettings.line3d,                  thumb: 'chart-21.png'},
+            { type: Asc.c_oAscChartTypeSettings.barNormal3d,             thumb: 'chart-17.png'},
+            { type: Asc.c_oAscChartTypeSettings.barStacked3d,            thumb: 'chart-18.png'},
+            { type: Asc.c_oAscChartTypeSettings.barStackedPer3d,         thumb: 'chart-19.png'},
+            { type: Asc.c_oAscChartTypeSettings.hBarNormal3d,            thumb: 'chart-25.png'},
+            { type: Asc.c_oAscChartTypeSettings.hBarStacked3d,           thumb: 'chart-24.png'},
+            { type: Asc.c_oAscChartTypeSettings.hBarStackedPer3d,        thumb: 'chart-23.png'},
+            { type: Asc.c_oAscChartTypeSettings.barNormal3dPerspective,  thumb: 'chart-20.png'}
+        ];
+        const columns = 3;
+        const arr = [];
+        let row = -1;
+        types.forEach((type, index) => {
+            if (0 == index % columns) {
+                arr.push([]);
+                row++
+            }
+            arr[row].push(type);
+        });
+        return arr;
+    }
+
+    // Fill Color
+
+    @observable fillColor = undefined;
+
+    setFillColor (color) {
+        this.fillColor = color;
+    }
+
+    getFillColor (shapeProperties) {
+        let fill = shapeProperties.get_fill();
+        const fillType = fill.get_type();
+        let color = 'transparent';
+
+        if (fillType == Asc.c_oAscFill.FILL_TYPE_SOLID) {
+            fill = fill.get_fill();
+            const sdkColor = fill.get_color();
+            if (sdkColor) {
+                if (sdkColor.get_type() == Asc.c_oAscColor.COLOR_TYPE_SCHEME) {
+                    color = {color: Common.Utils.ThemeColor.getHexColor(sdkColor.get_r(), sdkColor.get_g(), sdkColor.get_b()), effectValue: sdkColor.get_value()};
+                } else {
+                    color = Common.Utils.ThemeColor.getHexColor(sdkColor.get_r(), sdkColor.get_g(), sdkColor.get_b());
+                }
+            }
+        }
+
+        this.fillColor = color;
+        return color;
+    }
+
+    // Border size and border color
+
+    @observable borderColor;
+
+    setBorderColor (color) {
+        this.borderColor = color;
+    }
+
+    initBorderColor (stroke) {
+        let color = 'transparent';
+
+        if (stroke && stroke.get_type() == Asc.c_oAscStrokeType.STROKE_COLOR) {
+            const sdkColor = stroke.get_color();
+            if (sdkColor) {
+                if (sdkColor.get_type() == Asc.c_oAscColor.COLOR_TYPE_SCHEME) {
+                    color = {color: Common.Utils.ThemeColor.getHexColor(sdkColor.get_r(), sdkColor.get_g(), sdkColor.get_b()), effectValue: sdkColor.get_value()};
+                }
+                else {
+                    color = Common.Utils.ThemeColor.getHexColor(sdkColor.get_r(), sdkColor.get_g(), sdkColor.get_b());
+                }
+            }
+        }
+
+        this.borderColor = color;
+        return color;
+    }
+
+    borderSizeTransform () {
+        const _sizes = [0, 0.5, 1, 1.5, 2.25, 3, 4.5, 6];
+
+        return {
+            sizeByIndex: function (index) {
+                if (index < 1) return _sizes[0];
+                if (index > _sizes.length - 1) return _sizes[_sizes.length - 1];
+                return _sizes[index];
+            },
+
+            indexSizeByValue: function (value) {
+                let index = 0;
+                _sizes.forEach((size, idx) => {
+                    if (Math.abs(size - value) < 0.25) {
+                        index = idx;
+                    }
+                });
+                return index;
+            },
+
+            sizeByValue: function (value) {
+                return _sizes[this.indexSizeByValue(value)];
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/apps/presentationeditor/mobile/src/store/focusObjects.js b/apps/presentationeditor/mobile/src/store/focusObjects.js
index fdf9bdd6b..64e69df71 100644
--- a/apps/presentationeditor/mobile/src/store/focusObjects.js
+++ b/apps/presentationeditor/mobile/src/store/focusObjects.js
@@ -136,4 +136,30 @@ export class storeFocusObjects {
         }
         return false;
     }
+
+    @computed get chartObject() {
+        const charts = [];
+        const shapes = []
+        
+        for (let object of this._focusObjects) {
+            if (object.get_ObjectValue() == Asc.c_oAscTypeSelectElement.Chart) {
+                charts.push(object);
+            }
+            else if(object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Shape && object.get_ObjectValue() && object.get_ObjectValue().get_FromChart()) {
+                shapes.push(object)
+            }
+        }
+
+        const getTopObject = array => {
+            if (array.length > 0) {
+                var object = array[array.length - 1]; // get top
+                return object.get_ObjectValue();
+            } else {
+                return undefined;
+            }
+        };
+
+        getTopObject(charts);
+        getTopObject(shapes);
+    }
 }
\ No newline at end of file
diff --git a/apps/presentationeditor/mobile/src/store/mainStore.js b/apps/presentationeditor/mobile/src/store/mainStore.js
index ba4b9be18..a8d4020ea 100644
--- a/apps/presentationeditor/mobile/src/store/mainStore.js
+++ b/apps/presentationeditor/mobile/src/store/mainStore.js
@@ -10,12 +10,12 @@ import { storePalette } from './palette';
 import { storeSlideSettings } from './slideSettings';
 import { storeTextSettings } from './textSettings';
 import { storeShapeSettings } from './shapeSettings';
-import {storeTableSettings} from "./tableSettings";
+import { storeTableSettings } from "./tableSettings";
+import { storeChartSettings } from "./chartSettings";
 // import {storeTextSettings} from "./textSettings";
 // import {storeParagraphSettings} from "./paragraphSettings";
 // import {storeShapeSettings} from "./shapeSettings";
 // import {storeImageSettings} from "./imageSettings";
-// import {storeChartSettings} from "./chartSettings";
 
 export const stores = {
     storeAppOptions: new storeAppOptions(),
@@ -29,7 +29,8 @@ export const stores = {
     storePalette: new storePalette(),
     storeTextSettings: new storeTextSettings(),
     storeShapeSettings: new storeShapeSettings(),
-    storeTableSettings: new storeTableSettings()
+    storeTableSettings: new storeTableSettings(),
+    storeChartSettings: new storeChartSettings()
     // storeTextSettings: new storeTextSettings(),
     // storeParagraphSettings: new storeParagraphSettings(),
     // storeShapeSettings: new storeShapeSettings(),
diff --git a/apps/presentationeditor/mobile/src/view/edit/Edit.jsx b/apps/presentationeditor/mobile/src/view/edit/Edit.jsx
index d0cfca654..155016cd5 100644
--- a/apps/presentationeditor/mobile/src/view/edit/Edit.jsx
+++ b/apps/presentationeditor/mobile/src/view/edit/Edit.jsx
@@ -9,13 +9,15 @@ import EditSlideController from "../../controller/edit/EditSlide";
 import EditTextController from "../../controller/edit/EditText";
 import EditShapeController from "../../controller/edit/EditShape";
 import EditImageController from "../../controller/edit/EditImage";
+import EditTableController from "../../controller/edit/EditTable";
+import EditChartController from "../../controller/edit/EditChart";
+
 import { Theme, Layout, Transition, Type, Effect, StyleFillColor, CustomFillColor } from './EditSlide';
 import { PageTextFonts, PageTextFontColor, PageTextCustomFontColor, PageTextAddFormatting, PageTextBullets, PageTextNumbers, PageTextLineSpacing } from './EditText';
 import { PageShapeStyle, PageShapeStyleNoFill, PageReplaceContainer, PageReorderContainer, PageAlignContainer, PageShapeBorderColor, PageShapeCustomBorderColor, PageShapeCustomFillColor } from './EditShape';
 import { PageImageReplace, PageImageReorder, PageImageAlign, PageLinkSettings } from './EditImage';
 import { PageTableStyle, PageTableStyleOptions, PageTableCustomFillColor, PageTableBorderColor, PageTableCustomBorderColor, PageTableReorder, PageTableAlign } from './EditTable';
-import EditTableController from "../../controller/edit/EditTable";
-//import EditChartController from "../../controller/edit/EditChart";
+import { PageChartStyle, PageChartCustomFillColor, PageChartBorderColor, PageChartCustomBorderColor, PageChartReorder, PageChartAlign } from './EditChart'
 //import EditLinkController from "../../controller/edit/EditLink";
 
 const routes = [
@@ -165,6 +167,33 @@ const routes = [
     {
         path: '/edit-table-custom-fill-color/',
         component: PageTableCustomFillColor
+    }, 
+
+    // Chart
+
+    {
+        path: '/edit-chart-style/',
+        component: PageChartStyle
+    },
+    {
+        path: '/edit-chart-reorder/',
+        component: PageChartReorder
+    },
+    {
+        path: '/edit-chart-align/',
+        component: PageChartAlign
+    },
+    {
+        path: '/edit-chart-border-color/',
+        component: PageChartBorderColor
+    },
+    {
+        path: '/edit-chart-custom-border-color/',
+        component: PageChartCustomBorderColor
+    },
+    {
+        path: '/edit-chart-custom-fill-color/',
+        component: PageChartCustomFillColor
     }
 ];
 
@@ -267,7 +296,6 @@ const EditTabs = props => {
                 component: <EditTableController />
             })
         }
-        /*
         if (settings.indexOf('chart') > -1) {
             editors.push({
                 caption: _t.textChart,
@@ -275,6 +303,7 @@ const EditTabs = props => {
                 component: <EditChartController />
             })
         }
+        /*
         if (settings.indexOf('hyperlink') > -1) {
             editors.push({
                 caption: _t.textHyperlink,
diff --git a/apps/presentationeditor/mobile/src/view/edit/EditChart.jsx b/apps/presentationeditor/mobile/src/view/edit/EditChart.jsx
new file mode 100644
index 000000000..376930be0
--- /dev/null
+++ b/apps/presentationeditor/mobile/src/view/edit/EditChart.jsx
@@ -0,0 +1,341 @@
+import React, {Fragment, useState} from 'react';
+import {observer, inject} from "mobx-react";
+import {List, ListItem, ListButton, Icon, Row, Page, Navbar, BlockTitle, Toggle, Range, Link, Tabs, Tab} from 'framework7-react';
+import { useTranslation } from 'react-i18next';
+import {Device} from '../../../../../common/mobile/utils/device';
+import {CustomColorPicker, ThemeColorPalette} from "../../../../../common/mobile/lib/component/ThemeColorPalette.jsx";
+
+const PageCustomFillColor = props => {
+    const { t } = useTranslation();
+    const _t = t('View.Edit', {returnObjects: true});
+    let fillColor = props.storeChartSettings.fillColor;
+
+    if (typeof fillColor === 'object') {
+        fillColor = fillColor.color;
+    }
+
+    const onAddNewColor = (colors, color) => {
+        props.storePalette.changeCustomColors(colors);
+        props.onFillColor(color);
+        props.storeChartSettings.setFillColor(color);
+        props.f7router.back();
+    };
+
+    return (
+        <Page>
+            <Navbar title={_t.textCustomColor} backLink={_t.textBack} />
+            <CustomColorPicker currentColor={fillColor} onAddNewColor={onAddNewColor}/>
+        </Page>
+    )
+};
+
+const PaletteFill = inject("storeFocusObjects", "storeChartSettings", "storePalette")(observer(props => {
+    const { t } = useTranslation();
+    const _t = t('View.Edit', {returnObjects: true});
+    const storeChartSettings = props.storeChartSettings;
+    const shapeProperties = props.storeFocusObjects.shapeObject;
+    const curFillColor = storeChartSettings.fillColor ? storeChartSettings.fillColor : storeChartSettings.getFillColor(shapeProperties);
+    const customColors = props.storePalette.customColors;
+
+    const changeColor = (color, effectId, effectValue) => {
+        if (color !== 'empty') {
+            if (effectId !==undefined ) {
+                const newColor = {color: color, effectId: effectId, effectValue: effectValue};
+                props.onFillColor(newColor);
+                storeChartSettings.setFillColor(newColor);
+            } else {
+                props.onFillColor(color);
+                storeChartSettings.setFillColor(color);
+            }
+        } else {
+            // open custom color menu
+            props.f7router.navigate('/edit-chart-custom-fill-color/');
+        }
+    };
+
+    return (
+        <Fragment>
+            <ThemeColorPalette changeColor={changeColor} curColor={curFillColor} customColors={customColors} transparent={true}/>
+            <List>
+                <ListItem title={_t.textAddCustomColor} link={'/edit-chart-custom-fill-color/'} routeProps={{
+                    onFillColor: props.onFillColor
+                }}></ListItem>
+            </List>
+        </Fragment>
+    )
+}));
+
+const PageCustomBorderColor = props => {
+    const { t } = useTranslation();
+    const _t = t('View.Edit', {returnObjects: true});
+    let borderColor = props.storeChartSettings.borderColor;
+
+    if (typeof borderColor === 'object') {
+        borderColor = borderColor.color;
+    }
+
+    const onAddNewColor = (colors, color) => {
+        props.storePalette.changeCustomColors(colors);
+        props.onBorderColor(color);
+        props.storeChartSettings.setBorderColor(color);
+        props.f7router.back();
+    };
+
+    return (
+        <Page>
+            <Navbar title={_t.textCustomColor} backLink={_t.textBack} />
+            <CustomColorPicker currentColor={borderColor} onAddNewColor={onAddNewColor}/>
+        </Page>
+    )
+};
+
+const PageBorderColor = props => {
+    const { t } = useTranslation();
+    const _t = t('View.Edit', {returnObjects: true});
+    const borderColor = props.storeChartSettings.borderColor;
+    const customColors = props.storePalette.customColors;
+
+    const changeColor = (color, effectId, effectValue) => {
+        if (color !== 'empty') {
+            if (effectId !==undefined ) {
+                const newColor = {color: color, effectId: effectId, effectValue: effectValue};
+                props.onBorderColor(newColor);
+                props.storeChartSettings.setBorderColor(newColor);
+            } else {
+                props.onBorderColor(color);
+                props.storeChartSettings.setBorderColor(color);
+            }
+        } else {
+            // open custom color menu
+            props.f7router.navigate('/edit-chart-custom-border-color/');
+        }
+    };
+
+    return (
+        <Page>
+            <Navbar title={_t.textColor} backLink={_t.textBack} />
+            <ThemeColorPalette changeColor={changeColor} curColor={borderColor} customColors={customColors}/>
+            <List>
+                <ListItem title={_t.textAddCustomColor} link={'/edit-chart-custom-border-color/'} routeProps={{
+                    onBorderColor: props.onBorderColor
+                }}></ListItem>
+            </List>
+        </Page>
+    )
+};
+
+const PageStyle = props => {
+    const { t } = useTranslation();
+    const _t = t('View.Edit', {returnObjects: true});
+    const storeChartSettings = props.storeChartSettings;
+    const chartProperties = props.storeFocusObjects.chartObject;
+
+    const types = storeChartSettings.types;
+    const curType = chartProperties.getType();
+
+    const styles = storeChartSettings.styles;
+
+    const shapeObject = props.storeFocusObjects.shapeObject;
+    const shapeStroke = shapeObject.get_stroke();
+
+    // Init border size
+
+    const borderSizeTransform = storeChartSettings.borderSizeTransform();
+    const borderSize = shapeStroke.get_width() * 72.0 / 25.4;
+    const borderType = shapeStroke.get_type();
+    const displayBorderSize = (borderType == Asc.c_oAscStrokeType.STROKE_NONE) ? 0 : borderSizeTransform.indexSizeByValue(borderSize);
+    const displayTextBorderSize = (borderType == Asc.c_oAscStrokeType.STROKE_NONE) ? 0 : borderSizeTransform.sizeByValue(borderSize);
+    const [stateBorderSize, setBorderSize] = useState(displayBorderSize);
+    const [stateTextBorderSize, setTextBorderSize] = useState(displayTextBorderSize);
+
+    // Init border color
+
+    const borderColor = !storeChartSettings.borderColor ? storeChartSettings.initBorderColor(shapeStroke) : storeChartSettings.borderColor;
+    const displayBorderColor = borderColor !== 'transparent' ? `#${(typeof borderColor === "object" ? borderColor.color : borderColor)}` : borderColor;
+
+    return (
+        <Page>
+            <Navbar backLink={_t.textBack}>
+                <div className="tab-buttons tabbar">
+                    <Link key={"pe-link-chart-type"}  tabLink={"#edit-chart-type"} tabLinkActive={true}>{_t.textType}</Link>
+                    <Link key={"pe-link-chart-style"}  tabLink={"#edit-chart-style"}>{_t.textStyle}</Link>
+                    <Link key={"pe-link-chart-fill"}  tabLink={"#edit-chart-fill"}>{_t.textFill}</Link>
+                    <Link key={"pe-link-chart-border"}  tabLink={"#edit-chart-border"}>{_t.textBorder}</Link>
+                </div>
+            </Navbar>
+            <Tabs animated>
+                <Tab key={"pe-tab-chart-type"} id={"edit-chart-type"} className="page-content no-padding-top dataview" tabActive={true}>
+                    <div className="chart-types">
+                        {types.map((row, rowIndex) => {
+                            return (
+                                <ul className="row" key={`row-${rowIndex}`}>
+                                    {row.map((type, index)=>{
+                                        return(
+                                            <li key={`${rowIndex}-${index}`}
+                                                className={curType === type.type ? ' active' : ''}
+                                                onClick={()=>{props.onType(type.type)}}>
+                                                <div className={'thumb'}
+                                                     style={{backgroundImage: `url('resources/img/charts/${type.thumb}')`}}>
+                                                </div>
+                                            </li>
+                                        )
+                                    })}
+                                </ul>
+                            )
+                        })}
+                    </div>
+                </Tab>
+                <Tab key={"pe-tab-chart-style"} id={"edit-chart-style"} className="page-content no-padding-top dataview">
+                    <div className={'chart-styles'}>
+                        {styles.map((row, rowIndex) => {
+                            return (
+                                <ul className="row" key={`row-${rowIndex}`}>
+                                    {row.map((style, index)=>{
+                                        return(
+                                            <li key={`${rowIndex}-${index}`}
+                                                onClick={()=>{props.onStyle(style.asc_getName())}}>
+                                                <img src={`${style.asc_getImage()}`}/>
+                                            </li>
+                                        )
+                                    })}
+                                </ul>
+                            )
+                        })}
+                    </div>
+                </Tab>
+                <Tab key={"pe-tab-chart-fill"} id={"edit-chart-fill"} className="page-content no-padding-top">
+                    <PaletteFill onFillColor={props.onFillColor}/>
+                </Tab>
+                <Tab key={"pe-tab-chart-border"} id={"edit-chart-border"} className="page-content no-padding-top">
+                    <List>
+                        <ListItem>
+                            <div slot="root-start" className='inner-range-title'>{_t.textSize}</div>
+                            <div slot='inner' style={{width: '100%'}}>
+                                <Range min="0" max="7" step="1" value={stateBorderSize}
+                                       onRangeChange={(value) => {setBorderSize(value); setTextBorderSize(borderSizeTransform.sizeByIndex(value));}}
+                                       onRangeChanged={(value) => {props.onBorderSize(borderSizeTransform.sizeByIndex(value))}}
+                                ></Range>
+                            </div>
+                            <div slot='inner-end' style={{minWidth: '60px', textAlign: 'right'}}>
+                                {stateTextBorderSize + ' ' + Common.Utils.Metric.getMetricName(Common.Utils.Metric.c_MetricUnits.pt)}
+                            </div>
+                        </ListItem>
+                        <ListItem title={_t.textColor} link='/edit-chart-border-color/' routeProps={{
+                            onBorderColor: props.onBorderColor
+                        }}>
+                            <span className="color-preview"
+                                  slot="after"
+                                  style={{ background: displayBorderColor}}
+                            ></span>
+                        </ListItem>
+                    </List>
+                </Tab>
+            </Tabs>
+        </Page>
+    )
+};
+
+const PageReorder = props => {
+    const { t } = useTranslation();
+    const _t = t('View.Edit', {returnObjects: true});
+    return (
+        <Page>
+            <Navbar title={_t.textReorder} backLink={_t.textBack} />
+            <List>
+                <ListItem title={_t.textBringToForeground} onClick={() => {props.onReorder('all-up')}} link='#' className='no-indicator'>
+                    <Icon slot="media" icon="icon-move-foreground"></Icon>
+                </ListItem>
+                <ListItem title={_t.textSendToBackground} onClick={() => {props.onReorder('all-down')}} link='#' className='no-indicator'>
+                    <Icon slot="media" icon="icon-move-background"></Icon>
+                </ListItem>
+                <ListItem title={_t.textMoveForward} onClick={() => {props.onReorder('move-up')}} link='#' className='no-indicator'>
+                    <Icon slot="media" icon="icon-move-forward"></Icon>
+                </ListItem>
+                <ListItem title={_t.textMoveBackward} onClick={() => {props.onReorder('move-down')}} link='#' className='no-indicator'>
+                    <Icon slot="media" icon="icon-move-backward"></Icon>
+                </ListItem>
+            </List>
+        </Page>
+    )
+};
+
+const PageAlign = props => {
+    const { t } = useTranslation();
+    const _t = t('View.Edit', {returnObjects: true});
+
+    return (
+        <Page>
+            <Navbar title={_t.textAlign} backLink={_t.textBack} />
+            <List>
+                <ListItem title={_t.textAlignLeft} link='#' onClick={() => {props.onAlign('align-left')}} className='no-indicator'>
+                    <Icon slot="media" icon="icon-align-left"></Icon>
+                </ListItem>
+                <ListItem title={_t.textAlignCenter} link='#' onClick={() => {props.onAlign('align-center')}} className='no-indicator'>
+                    <Icon slot="media" icon="icon-align-center"></Icon>
+                </ListItem>
+                <ListItem title={_t.textAlignRight} link='#' onClick={() => {props.onAlign('align-right')}} className='no-indicator'>
+                    <Icon slot="media" icon="icon-align-right"></Icon>
+                </ListItem>
+                <ListItem title={_t.textAlignTop} link='#' onClick={() => {props.onAlign('align-top')}} className='no-indicator'>
+                    <Icon slot="media" icon="icon-align-top"></Icon>
+                </ListItem>
+                <ListItem title={_t.textAlignMiddle} link='#' onClick={() => {props.onAlign('align-middle')}} className='no-indicator'>
+                    <Icon slot="media" icon="icon-align-middle"></Icon>
+                </ListItem>
+                <ListItem title={_t.textAlignBottom} link='#' onClick={() => {props.onAlign('align-bottom')}} className='no-indicator'>
+                    <Icon slot="media" icon="icon-align-bottom"></Icon>
+                </ListItem>
+            </List>
+            <List>
+                <ListItem title={_t.textDistributeHorizontally} link='#' onClick={() => {props.onAlign('distrib-hor')}} className='no-indicator'>
+                    <Icon slot="media" icon="icon-align-horizontal"></Icon>
+                </ListItem>
+                <ListItem title={_t.textDistributeVertically} link='#' onClick={() => {props.onAlign('distrib-vert')}} className='no-indicator'>
+                    <Icon slot="media" icon="icon-align-vertical"></Icon>
+                </ListItem>
+            </List>
+        </Page>
+    )
+};
+
+const EditChart = props => {
+    const { t } = useTranslation();
+    const _t = t('View.Edit', {returnObjects: true});
+    return (
+        <Fragment>
+            <List>
+                <ListItem title={_t.textStyle} link='/edit-chart-style/' routeProps={{
+                    onType: props.onType,
+                    onStyle: props.onStyle,
+                    onFillColor: props.onFillColor,
+                    onBorderColor: props.onBorderColor,
+                    onBorderSize: props.onBorderSize
+                }}></ListItem>
+                <ListItem title={_t.textReorder} link='/edit-chart-reorder/' routeProps={{
+                    onReorder: props.onReorder
+                }}></ListItem>
+                 <ListItem title={_t.textAlign} link="/edit-chart-align/" routeProps={{
+                    onAlign: props.onAlign
+                }}></ListItem>
+            </List>
+            <List>
+                <ListButton title={_t.textRemoveChart} onClick={() => {props.onRemoveChart()}} className='button-red button-fill button-raised'/>
+            </List>
+        </Fragment>
+    )
+};
+
+const PageChartStyle = inject("storeChartSettings", "storeFocusObjects")(observer(PageStyle));
+const PageChartCustomFillColor = inject("storeChartSettings", "storePalette")(observer(PageCustomFillColor));
+const PageChartBorderColor = inject("storeChartSettings", "storePalette")(observer(PageBorderColor));
+const PageChartCustomBorderColor = inject("storeChartSettings", "storePalette")(observer(PageCustomBorderColor));
+
+export {
+    EditChart,
+    PageChartStyle,
+    PageChartCustomFillColor,
+    PageChartBorderColor,
+    PageChartCustomBorderColor,
+    PageReorder as PageChartReorder,
+    PageAlign as PageChartAlign
+}
\ No newline at end of file
diff --git a/apps/presentationeditor/mobile/src/view/edit/EditTable.jsx b/apps/presentationeditor/mobile/src/view/edit/EditTable.jsx
index 38f94b54c..8786cc95e 100644
--- a/apps/presentationeditor/mobile/src/view/edit/EditTable.jsx
+++ b/apps/presentationeditor/mobile/src/view/edit/EditTable.jsx
@@ -296,7 +296,7 @@ const PageStyle = props => {
                 </div>
             </Navbar>
             <Tabs animated>
-                <Tab key={"de-tab-table-style"} id={"edit-table-style"} className="page-content no-padding-top" tabActive={true}>
+                <Tab key={"pe-tab-table-style"} id={"edit-table-style"} className="page-content no-padding-top" tabActive={true}>
                     <List>
                         <ListItem>
                             <StyleTemplates templates={templates} onStyleClick={props.onStyleClick}/>
@@ -308,10 +308,10 @@ const PageStyle = props => {
                         }}/>
                     </List>
                 </Tab>
-                <Tab key={"de-tab-table-fill"} id={"edit-table-fill"} className="page-content no-padding-top">
+                <Tab key={"pe-tab-table-fill"} id={"edit-table-fill"} className="page-content no-padding-top">
                     <TabFillColor onFillColor={props.onFillColor}/>
                 </Tab>
-                <Tab key={"de-tab-table-border"} id={"edit-table-border"} className="page-content no-padding-top">
+                <Tab key={"pe-tab-table-border"} id={"edit-table-border"} className="page-content no-padding-top">
                     <TabBorder onBorderTypeClick={props.onBorderTypeClick}/>
                 </Tab>
             </Tabs>