diff --git a/apps/common/main/resources/less/combo-dataview.less b/apps/common/main/resources/less/combo-dataview.less index 30ebd1c33e..caef2d6a61 100644 --- a/apps/common/main/resources/less/combo-dataview.less +++ b/apps/common/main/resources/less/combo-dataview.less @@ -442,6 +442,38 @@ } +.combo-chart-template { + .combo-template(62px); + + top: -4px; + position: absolute; + .padding-right-12(); + + .more-container & { + position: static; + } + + .view .dataview, .dropdown-menu { + padding: 1px; + } + + .dropdown-menu { + padding: 5px 1px 5px 1px; + + .dataview { + .group-description { + padding: 3px 0 3px 10px; + .font-weight-bold(); + + .rtl & { + padding: 3px 10px 3px 0; + } + } + } + } + +} + .combo-slicer-style { .combo-textart(60px, 4px); } diff --git a/apps/spreadsheeteditor/main/app.js b/apps/spreadsheeteditor/main/app.js index 83459f78f0..675a0a47b5 100644 --- a/apps/spreadsheeteditor/main/app.js +++ b/apps/spreadsheeteditor/main/app.js @@ -146,6 +146,7 @@ require([ 'DataTab', 'ViewTab', 'TableDesignTab', + 'ChartTab', 'Search', 'WBProtection', 'Common.Controllers.Fonts', @@ -185,6 +186,7 @@ require([ 'spreadsheeteditor/main/app/controller/DataTab', 'spreadsheeteditor/main/app/controller/ViewTab', 'spreadsheeteditor/main/app/controller/TableDesignTab', + 'spreadsheeteditor/main/app/controller/ChartTab', 'spreadsheeteditor/main/app/controller/Search', 'spreadsheeteditor/main/app/controller/WBProtection', // 'spreadsheeteditor/main/app/view/ParagraphSettings', diff --git a/apps/spreadsheeteditor/main/app/controller/ChartTab.js b/apps/spreadsheeteditor/main/app/controller/ChartTab.js new file mode 100644 index 0000000000..925d472478 --- /dev/null +++ b/apps/spreadsheeteditor/main/app/controller/ChartTab.js @@ -0,0 +1,577 @@ +/* + * (c) Copyright Ascensio System SIA 2010-2024 + * + * This program is a free software product. You can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License (AGPL) + * version 3 as published by the Free Software Foundation. In accordance with + * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect + * that Ascensio System SIA expressly excludes the warranty of non-infringement + * of any third-party rights. + * + * This program is distributed WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For + * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html + * + * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish + * street, Riga, Latvia, EU, LV-1050. + * + * The interactive user interfaces in modified source and object code versions + * of the Program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU AGPL version 3. + * + * Pursuant to Section 7(b) of the License you must retain the original Product + * logo when distributing the program. Pursuant to Section 7(e) we decline to + * grant you any rights under trademark law for use of our trademarks. + * + * All the Product's GUI elements, including illustrations and icon sets, as + * well as technical writing content are licensed under the terms of the + * Creative Commons Attribution-ShareAlike 4.0 International. See the License + * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + * + */ + +/** + * ChartTab.js + * + * Created on 06.10.2025 + * + */ + +define([ + 'core', + 'spreadsheeteditor/main/app/view/ChartTab' +], function () { + 'use strict'; + + SSE.Controllers.ChartTab = Backbone.Controller.extend(_.extend({ + models : [], + collections : [ + ], + views : [ + 'ChartTab' + ], + sdkViewName : '#id_main', + + initialize: function () { + this._state = { + Width: 0, + Height: 0, + ChartStyle: 1, + ChartType: -1, + SeveralCharts: false, + DisabledControls: false, + keepRatio: false, + }; + this._nRatio = 1; + this.spinners = []; + this.chPoints = []; + this.lockedControls = []; + this._locked = false; + this.defColor = {color: '4f81bd', effectId: 24}; + this.isChart = true; + + this._noApply = false; + this._originalProps = null; + + this.addListeners({ + 'ChartTab': { + 'charttab:advanced': _.bind(this.openAdvancedSettings, this), + 'charttab:type': _.bind(this.onChangeType, this), + 'charttab:selectdata': _.bind(this.onSelectData, this), + 'charttab:rowscols': _.bind(this.onSwitchRowsCols, this), + 'charttab:widthchange': _.bind(this.onWidthChange, this), + 'charttab:heightchange': _.bind(this.onHeightChange, this), + 'charttab:ratio': _.bind(this.onToggleRatio, this), + }, + }); + }, + + onToggleRatio: function (value) { + if (value && this.view.spnHeight.getNumberValue()>0) { + this._nRatio = this.view.spnWidth.getNumberValue()/this.view.spnHeight.getNumberValue(); + } + if (this.api) { + var props = new Asc.asc_CImgProperty(); + props.asc_putLockAspect(value); + this.api.asc_setGraphicObjectProps(props); + } + }, + + ChangeSettings: function(props) { + if (this._initSettings) { + this.updateMetricUnit(); + this.spinners.push(this.view.spnWidth); + this.spinners.push(this.view.spnHeight); + } + + if (this._isEditType) { + this._props = props; + return; + } + + var isChart = !!(props && props.asc_getChartProperties && props.asc_getChartProperties()), + chartSettings = isChart ? this.api.asc_getChartSettings(true) : null, // don't lock chart object + props3d = chartSettings ? chartSettings.getView3d() : null; + + if ( this.isChart!==isChart || this._state.is3D!==!!props3d ) { + this.ShowHideElem(isChart, !!props3d); + } + this._state.is3D=!!props3d; + this.disableControls(this._locked); + if (this.api && props){ + if (isChart) { // chart + this._originalProps = new Asc.asc_CImgProperty(props); + this.isChart = true; + + this._noApply = true; + this.chartProps = props.asc_getChartProperties(); + + var value = props.asc_getSeveralCharts() || this._locked; + if (this._state.SeveralCharts!==value) { + this.view.btnAdvancedSettings.setDisabled(value) + this._state.SeveralCharts=value; + } + + value = props.asc_getSeveralChartTypes(); + var type = (this._state.SeveralCharts && value) ? null : this.chartProps.getType(); + if (this._state.ChartType !== type) { + this.ShowCombinedProps(type); + !(type===null || type==Asc.c_oAscChartTypeSettings.comboBarLine || type==Asc.c_oAscChartTypeSettings.comboBarLineSecondary || + type==Asc.c_oAscChartTypeSettings.comboAreaBar || type==Asc.c_oAscChartTypeSettings.comboCustom) && this.updateChartStyles(this.api.asc_getChartPreviews(type, undefined, true)); + this._state.ChartType = type; + } + + if (!(type==Asc.c_oAscChartTypeSettings.comboBarLine || type==Asc.c_oAscChartTypeSettings.comboBarLineSecondary || + type==Asc.c_oAscChartTypeSettings.comboAreaBar || type==Asc.c_oAscChartTypeSettings.comboCustom)) { + value = props.asc_getSeveralChartStyles(); + if (this._state.SeveralCharts && value) { + this.view.chartStyles.fieldPicker.deselectAll(); + this.view.chartStyles.menuPicker.deselectAll(); + this._state.ChartStyle = null; + } else { + value = this.chartProps.getStyle(); + if (this._state.ChartStyle!==value || this._isChartStylesChanged) { + this._state.ChartStyle=value; + var arr = this.selectCurrentChartStyle(); + this._isChartStylesChanged && this.api.asc_generateChartPreviews(this._state.ChartType, arr); + } + } + this._isChartStylesChanged = false; + } + + this._noApply = false; + + value = props.asc_getWidth(); + if ( Math.abs(this._state.Width-value)>0.001 || + (this._state.Width===null || value===null)&&(this._state.Width!==value)) { + this.view.spnWidth.setValue((value!==null) ? Common.Utils.Metric.fnRecalcFromMM(value) : '', true); + this._state.Width = value; + } + + value = props.asc_getHeight(); + if ( Math.abs(this._state.Height-value)>0.001 || + (this._state.Height===null || value===null)&&(this._state.Height!==value)) { + this.view.spnHeight.setValue((value!==null) ? Common.Utils.Metric.fnRecalcFromMM(value) : '', true); + this._state.Height = value; + } + + if (props.asc_getHeight()>0) + this._nRatio = props.asc_getWidth()/props.asc_getHeight(); + + value = props.asc_getLockAspect(); + if (this._state.keepRatio!==value) { + console.log(this.view.chRatio) + this.view.chRatio.setValue(value, true); + this._state.keepRatio=value; + } + + var series = chartSettings ? chartSettings.getSeries() : null; + this.view.btnSwitchRowsCols.setDisabled(this._locked || !series || series.length<1 || !chartSettings || !chartSettings.getRange()); + + if (props3d) { + value = props3d.asc_getRotX(); + if ((this._state.X===undefined || value===undefined)&&(this._state.X!==value) || + Math.abs(this._state.X-value)>0.001) { + // this.spnX.setValue((value!==null && value !== undefined) ? value : '', true); + this._state.X = value; + } + + value = props3d.asc_getRotY(); + if ( (this._state.Y===undefined || value===undefined)&&(this._state.Y!==value) || + Math.abs(this._state.Y-value)>0.001) { + // this.spnY.setValue((value!==null && value !== undefined) ? value : '', true); + this._state.Y = value; + } + + value = props3d.asc_getRightAngleAxes(); + if ( this._state.RightAngle!==value ) { + // this.chRightAngle.setValue((value !== null && value !== undefined) ? value : 'indeterminate', true); + this._state.RightAngle=value; + } + + value = props3d.asc_getPerspective(); + if ( (this._state.Perspective===undefined || value===undefined)&&(this._state.Perspective!==value) || + Math.abs(this._state.Perspective-value)>0.001) { + // this.spnPerspective.setMinValue((value!==null && value !== undefined) ? 0.1 : 0); + // this.spnPerspective.setValue((value!==null && value !== undefined) ? value : 0, true); + this._state.Perspective = value; + } + // this.spnPerspective.setDisabled(this._locked || !!this._state.RightAngle); + // this.btnNarrow.setDisabled(this._locked || !!this._state.RightAngle); + // this.btnWiden.setDisabled(this._locked || !!this._state.RightAngle); + + value = props3d.asc_getDepth(); + if ( Math.abs(this._state.Depth-value)>0.001 || + (this._state.Depth===undefined || value===undefined)&&(this._state.Depth!==value)) { + // this.spn3DDepth.setValue((value!==null && value !== undefined) ? value : '', true); + this._state.Depth = value; + } + + value = props3d.asc_getHeight(); + if ( Math.abs(this._state.Height3d-value)>0.001 || + (this._state.Height3d===undefined || this._state.Height3d===null || value===null)&&(this._state.Height3d!==value)) { + (value!==null) && this.spn3DHeight.setValue(value, true); + // this.chAutoscale.setValue(value===null, true); + this._state.Height3d = value; + } + // this.spn3DHeight.setDisabled(this._locked || value===null); + } + } + } + }, + + ShowHideElem: function(isChart, is3D) { + // this.ChartSizeContainer.toggleClass('settings-hidden', !isChart); + // this.ChartTypesContainer.toggleClass('settings-hidden', !isChart); + // this.SparkTypesContainer.toggleClass('settings-hidden', isChart); + // this.SparkPointsContainer.toggleClass('settings-hidden', isChart); + // this.Chart3DContainer.toggleClass('settings-hidden', !isChart || !is3D); + this.fireEvent('updatescroller', this); + }, + + updateMetricUnit: function() { + if (this.spinners) { + for (var i=0; ithis.view.spnHeight.options.maxValue) { + h = this.view.spnHeight.options.maxValue; + w = h * this._nRatio; + this.view.spnWidth.setValue(w, true); + } + this.view.spnHeight.setValue(h, true); + } + if (this.api) { + var props = new Asc.asc_CImgProperty(); + props.asc_putWidth(Common.Utils.Metric.fnRecalcToMM(w)); + props.asc_putHeight(Common.Utils.Metric.fnRecalcToMM(h)); + this.api.asc_setGraphicObjectProps(props); + } + }, + + onHeightChange: function(field, newValue, oldValue, eOpts){ + var h = field.getNumberValue(), w = this.spnWidth.getNumberValue(); + if (this.view.chRatio.getValue()) { + w = h * this._nRatio; + if (w>this.view.spnWidth.options.maxValue) { + w = this.view.spnWidth.options.maxValue; + h = w/this._nRatio; + this.view.spnHeight.setValue(h, true); + } + this.view.spnWidth.setValue(w, true); + } + if (this.api) { + var props = new Asc.asc_CImgProperty(); + props.asc_putWidth(Common.Utils.Metric.fnRecalcToMM(w)); + props.asc_putHeight(Common.Utils.Metric.fnRecalcToMM(h)); + this.api.asc_setGraphicObjectProps(props); + } + }, + + disableControls: function(disable) { + if (this._initSettings) return; + + if (this._state.DisabledControls!==disable) { + this._state.DisabledControls = disable; + _.each(this.lockedControls, function(item) { + item.setDisabled(disable); + }); + this.view.btnAdvancedSettings.setDisabled(disable); + } + }, + + ShowCombinedProps: function(type) { + this.view.chartStyles.setVisible(!(type===null || type==Asc.c_oAscChartTypeSettings.comboBarLine || type==Asc.c_oAscChartTypeSettings.comboBarLineSecondary || + type==Asc.c_oAscChartTypeSettings.comboAreaBar || type==Asc.c_oAscChartTypeSettings.comboCustom)); + }, + + updateChartStyles: function(styles) { + var me = this; + this._isChartStylesChanged = true; + + this.view.chartStyles.render($('#chart-combo-style')); + this.view.chartStyles.on('click', _.bind(this.onSelectStyle, this)); + this.view.chartStyles.openButton.menu.on('show:after', function () { + me.view.chartStyles.menuPicker.scroller.update({alwaysVisibleY: true}); + }); + this.lockedControls.push(this.view.chartStyles); + + if (styles && styles.length>0){ + var stylesStore = this.view.chartStyles.menuPicker.store; + if (stylesStore) { + var stylearray = []; + _.each(styles, function(item, index){ + stylearray.push({ + imageUrl: item.asc_getImage(), + data : item.asc_getName(), + tip : me.textStyle + ' ' + item.asc_getName() + }); + }); + stylesStore.reset(stylearray, {silent: false}); + } + } else { + this.view.chartStyles.menuPicker.store.reset(); + this.view.chartStyles.clearComboView(); + } + this.view.chartStyles.setDisabled(!styles || styles.length<1 || this._locked); + }, + + onSelectStyle: function(combo, record) { + if (this._noApply) return; + + if (this.api && !this._noApply && this.chartProps) { + var props = new Asc.asc_CImgProperty(); + this.chartProps.putStyle(record.get('data')); + props.asc_putChartProperties(this.chartProps); + this.api.asc_setGraphicObjectProps(props); + } + Common.NotificationCenter.trigger('edit:complete', this); + }, + + selectCurrentChartStyle: function() { + if (!this.view.chartStyles) return; + + this.view.chartStyles.suspendEvents(); + var rec = this.view.chartStyles.menuPicker.store.findWhere({data: this._state.ChartStyle}); + this.view.chartStyles.menuPicker.selectRecord(rec); + this.view.chartStyles.resumeEvents(); + + if (this._isChartStylesChanged) { + var currentRecords; + if (rec) + currentRecords = this.view.chartStyles.fillComboView(this.view.chartStyles.menuPicker.getSelectedRec(), true); + else + currentRecords = this.view.chartStyles.fillComboView(this.view.chartStyles.menuPicker.store.at(0), true); + if (currentRecords && currentRecords.length>0) { + var arr = []; + _.each(currentRecords, function(style, index){ + arr.push(style.get('data')); + }); + return arr; + } + } + }, + + setApi: function(api) { + if (api) { + this.api = api; + this.api.asc_registerCallback('asc_onCoAuthoringDisconnect',_.bind(this.SetDisabled, this)); + Common.NotificationCenter.on('api:disconnect', _.bind(this.SetDisabled, this)); + this.api.asc_registerCallback('asc_onSelectionChanged', _.bind(this.onSelectionChanged, this)); + this.api.asc_registerCallback('asc_onAddChartStylesPreview', _.bind(this.onAddChartStylesPreview, this)); + this.api.asc_registerCallback('asc_onUpdateChartStyles', _.bind(this._onUpdateChartStyles, this)); + Common.NotificationCenter.on('cells:range', _.bind(this.onCellsRange, this)); + } + return this; + }, + + onAddChartStylesPreview: function(styles){ + if (this._isEditType || !this.view.chartStyles) return; + + if (styles && styles.length>0){ + var stylesStore = this.view.chartStyles.menuPicker.store; + if (stylesStore) { + _.each(styles, function(item, index){ + var rec = stylesStore.findWhere({ + data: item.asc_getName() + }); + rec && rec.set('imageUrl', item.asc_getImage()); + }); + } + } + }, + + _onUpdateChartStyles: function() { + if (this.api && this._state.ChartType!==null && this._state.ChartType>-1 && + !(this._state.ChartType==Asc.c_oAscChartTypeSettings.comboBarLine || this._state.ChartType==Asc.c_oAscChartTypeSettings.comboBarLineSecondary || + this._state.ChartType==Asc.c_oAscChartTypeSettings.comboAreaBar || this._state.ChartType==Asc.c_oAscChartTypeSettings.comboCustom)) { + this.updateChartStyles(this.api.asc_getChartPreviews(this._state.ChartType, undefined, true)); + this.api.asc_generateChartPreviews(this._state.ChartType, this.selectCurrentChartStyle()); + } + }, + + setMode: function(mode) { + this.appConfig = mode; + return this; + }, + + setConfig: function(config) { + this.view = this.createView('ChartTab', { + toolbar: config.toolbar.toolbar + }); + }, + + SetDisabled: function(state) { + this.view && this.view.SetDisabled(state); + }, + + createToolbarPanel: function() { + return this.view.getPanel(); + }, + + getView: function(name) { + return !name && this.view ? + this.view : Backbone.Controller.prototype.getView.call(this, name); + }, + + onCoAuthoringDisconnect: function() { + this.SetDisabled(true); + }, + + onSelectionChanged: function(info) { + if (this.rangeSelectionMode || !this.appConfig.isEdit || !this.view) return; + var selectType = info.asc_getSelectionType(); + var selectedObjects = this.api.asc_getGraphicObjectProps(); + if ((selectType == Asc.c_oAscSelectionType.RangeChart || selectType == Asc.c_oAscSelectionType.RangeChartText) && selectedObjects) + for (var i = 0; i < selectedObjects.length; i++) { + if (selectedObjects[i].asc_getObjectType() == Asc.c_oAscTypeSelectElement.Image) { + var elValue = selectedObjects[i].asc_getObjectValue(); + if ( elValue.asc_getChartProperties() ) { + this.ChangeSettings(this.api.asc_getGraphicObjectProps()[i].asc_getObjectValue()); + break; + } + } + } + }, + + onCellsRange: function(status) { + this.rangeSelectionMode = (status != Asc.c_oAscSelectionDialogType.None); + }, + + }, SSE.Controllers.ChartTab || {})); +}); \ No newline at end of file diff --git a/apps/spreadsheeteditor/main/app/controller/DocumentHolderExt.js b/apps/spreadsheeteditor/main/app/controller/DocumentHolderExt.js index b418c53da3..5802fb0d80 100644 --- a/apps/spreadsheeteditor/main/app/controller/DocumentHolderExt.js +++ b/apps/spreadsheeteditor/main/app/controller/DocumentHolderExt.js @@ -50,6 +50,17 @@ define([], function () { 'DocumentHolder': { 'createdelayedelements': this.onCreateDelayedElements, 'equation:callback': this.equationCallback + }, + 'ChartTab': { + 'charttab:updatemenu': function (menu) { + me.chartProps = me.getCurrentChartProps(); + if (me.chartProps) { + this.updateChartElementMenu(menu, me.chartProps) + } + }, + 'charttab:elementselected': function (menu, item) { + me.onChartElement(menu, item) + } } }); diff --git a/apps/spreadsheeteditor/main/app/controller/Main.js b/apps/spreadsheeteditor/main/app/controller/Main.js index e671064878..d25696035b 100644 --- a/apps/spreadsheeteditor/main/app/controller/Main.js +++ b/apps/spreadsheeteditor/main/app/controller/Main.js @@ -1716,6 +1716,9 @@ define([ if (!me.appOptions.isEditMailMerge && !me.appOptions.isEditDiagram && !me.appOptions.isEditOle && me.appOptions.canFeatureTable) application.getController('TableDesignTab').setMode(me.appOptions); + if (!me.appOptions.isEditMailMerge && !me.appOptions.isEditDiagram && !me.appOptions.isEditOle && me.appOptions.canFeatureTable) + application.getController('ChartTab').setMode(me.appOptions); + var viewport = this.getApplication().getController('Viewport').getView('Viewport'); viewport.applyEditorMode(); rightmenuController.getView('RightMenu').setMode(me.appOptions).setApi(me.api); diff --git a/apps/spreadsheeteditor/main/app/controller/Toolbar.js b/apps/spreadsheeteditor/main/app/controller/Toolbar.js index 54a10d26be..762ff4a0a1 100644 --- a/apps/spreadsheeteditor/main/app/controller/Toolbar.js +++ b/apps/spreadsheeteditor/main/app/controller/Toolbar.js @@ -206,7 +206,8 @@ define([ is_lockShape: false, isUserProtected: false, showPivotTab: false, - showTableDesignTab: false + showTableDesignTab: false, + showChartTab: false }; this.binding = {}; @@ -3075,6 +3076,14 @@ define([ toolbar.btnInsertChart.updateHint(in_chart ? toolbar.tipChangeChart : toolbar.tipInsertChart); this._state.in_chart = in_chart; } + if (this._state.inchart !== in_chart) { + if ( !in_chart && this.toolbar.isTabActive('charttab') ) + this.toolbar.setTab('home'); + this.toolbar.setVisible('charttab', !!in_chart); + if (in_chart && this._state.showChartTab) + this.toolbar.setTab('charttab'); + this._state.inchart = in_chart; + } if (in_chart) return; if (!toolbar.mode.isEditDiagram) @@ -4592,6 +4601,18 @@ define([ me.toolbar.btnsTableDesign = tabledesignbuttons; } + tab = {caption: 'Chart', action: 'charttab', extcls: config.isEdit ? 'canedit' : '', layoutname: 'toolbar-charttab', dataHintTitle: 'V', aux: true}; + var charttab = me.getApplication().getController('ChartTab'); + charttab.setApi(me.api).setConfig({toolbar: me}); + var view = charttab.getView('ChartTab'); + var chartbuttons = view.getButtons(); + var $panel = charttab.createToolbarPanel(); + if ($panel) { + me.toolbar.addTab(tab, $panel); + me._state.inchart && me.toolbar.setVisible('charttab', true); + Array.prototype.push.apply(me.toolbar.lockControls, chartbuttons); + } + if (!config.compactHeader) { // hide 'print' and 'save' buttons group and next separator me.toolbar.btnPrint.$el.parents('.group').hide().next().hide(); diff --git a/apps/spreadsheeteditor/main/app/view/ChartTab.js b/apps/spreadsheeteditor/main/app/view/ChartTab.js new file mode 100644 index 0000000000..613aca6b77 --- /dev/null +++ b/apps/spreadsheeteditor/main/app/view/ChartTab.js @@ -0,0 +1,787 @@ +/* + * (c) Copyright Ascensio System SIA 2010-2024 + * + * This program is a free software product. You can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License (AGPL) + * version 3 as published by the Free Software Foundation. In accordance with + * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect + * that Ascensio System SIA expressly excludes the warranty of non-infringement + * of any third-party rights. + * + * This program is distributed WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For + * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html + * + * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish + * street, Riga, Latvia, EU, LV-1050. + * + * The interactive user interfaces in modified source and object code versions + * of the Program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU AGPL version 3. + * + * Pursuant to Section 7(b) of the License you must retain the original Product + * logo when distributing the program. Pursuant to Section 7(e) we decline to + * grant you any rights under trademark law for use of our trademarks. + * + * All the Product's GUI elements, including illustrations and icon sets, as + * well as technical writing content are licensed under the terms of the + * Creative Commons Attribution-ShareAlike 4.0 International. See the License + * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + * + */ +/** + * ChartTab.js + * + * Created on 06.10.2025 + * + */ + +define([ + 'common/main/lib/util/utils', + 'common/main/lib/component/Button', + 'common/main/lib/component/BaseView', + 'common/main/lib/component/Layout' +], function () { + 'use strict'; + + SSE.Views.ChartTab = Common.UI.BaseView.extend(_.extend((function(){ + var template = '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '
' + + '
' + + '' + + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '' + + '
' + + '
'; + + function setEvents() { + var me = this; + + me.btnChartElements.on('click', function (btn, e) { + me.fireEvent('charttab:updatemenu', [me.menuChartElement.menu]); + }); + me.menuChartElement.menu.items.forEach(item => { + if (item.menu) { + item.menu.items.forEach(item => { + item.on('click', function() { + me.fireEvent('charttab:elementselected', [item.menu, item]); + }); + }); + } + }); + me.btnChartType.on('click', function (btn, e) { + me.fireEvent('charttab:type'); + }); + me.btnSelectData.on('click', function (btn, e) { + me.fireEvent('charttab:selectdata'); + }); + me.btnSwitchRowsCols.on('click', function (btn, e) { + me.fireEvent('charttab:rowscols'); + }); + me.btnAdvancedSettings.on('click', function (btn, e) { + me.fireEvent('charttab:advanced'); + }); + me.spnWidth.on('change', function () { + me.fireEvent('charttab:widthchange'); + }); + me.spnHeight.on('change',function () { + me.fireEvent('charttab:heightchange'); + }); + me.chRatio.on('change',function (field, value) { + me.fireEvent('charttab:ratio', [value === 'checked']); + }); + } + + return { + initialize: function (options) { + var controller = SSE.getController('ChartTab'); + this._state = controller._state; + Common.UI.BaseView.prototype.initialize.call(this); + + this.lockedControls = []; + + var me = this, + _set = Common.enumLock; + + this.menuChartElement = new Common.UI.MenuItem({ + menu: new Common.UI.Menu({ + items: [ + { + caption: 'Axes', + value: 'axes', + disabled: false, + menu: new Common.UI.Menu({ + cls: 'shifted-right', + menuAlign: 'tl-tr', + items: [ + { + caption: 'Horizontal Axis', + value: 'bShowHorAxis', + stopPropagation: true, + checkable: true + }, + { + caption: 'Vertical Axis', + value: 'bShowVertAxis', + stopPropagation: true, + checkable: true + }, + { + caption: 'Horizontal Axis Sec', + value: 'bShowHorAxSec', + stopPropagation: true, + checkable: true + }, + { + caption: 'Vertical Axis Sec', + value: 'bShowVertAxSec', + stopPropagation: true, + checkable: true + }, + { + caption: 'Depth Axes', + value: 'bShowDepthAxes', + stopPropagation: true, + checkable: true + } + ] + }) + }, + { + caption: 'Axis Titles', + value: 'axisTitles', + disabled: false, + menu: new Common.UI.Menu({ + cls: 'shifted-right', + menuAlign: 'tl-tr', + items: [ + { + caption:'Horizontal Axis', + value: 'bShowHorAxTitle', + stopPropagation: true, + checkable: true + }, + { + caption: 'Vertical Axis', + value: 'bShowVertAxTitle', + stopPropagation: true, + checkable: true + }, + { + caption: 'Horizontal Axis Sec', + value: 'bShowHorAxTitleSec', + stopPropagation: true, + checkable: true + }, + { + caption: 'Vertical Axis Sec', + value: 'bShowVertAxisTitleSec', + stopPropagation: true, + checkable: true + }, + { + caption: 'Depth Axis Title', + value: 'bShowDepthAxisTitle', + stopPropagation: true, + checkable: true + } + ] + }) + }, + { + caption: 'Chart Title', + value: 'chartTitle', + disabled: false, + menu: new Common.UI.Menu({ + cls: 'shifted-right', + menuAlign: 'tl-tr', + items: [ + { + caption: 'None', + value: 'bShowChartTitleNone', + stopPropagation: true, + toggleGroup: 'chartTitle', + checkable: true + }, + { + caption: 'No overlay', + value: 'bShowChartTitle', + stopPropagation: true, + toggleGroup: 'chartTitle', + checkable: true + }, + { + caption: 'Overlay', + value: 'bOverlayTitle', + stopPropagation: true, + toggleGroup: 'chartTitle', + checkable: true + } + ] + }) + }, + { + caption: 'Data Labels', + value: 'dataLabels', + disabled: false, + menu: new Common.UI.Menu({ + cls: 'shifted-right', + menuAlign: 'tl-tr', + items: [ + { + caption: 'None', + value: 'bShowDataLabels', + stopPropagation: true, + toggleGroup: 'dataLabels', + checkable: true + }, + { + caption: 'Center', + value: 'CenterData', + stopPropagation: true, + toggleGroup: 'dataLabels', + checkable: true + }, + { + caption: 'Inner bottom', + value: 'InnerBottomData', + stopPropagation: true, + toggleGroup: 'dataLabels', + checkable: true + }, + { + caption: 'Inner top', + value: 'InnerTopData', + stopPropagation: true, + toggleGroup: 'dataLabels', + checkable: true + }, + { + caption: 'Outer top', + value: 'OuterTopData', + stopPropagation: true, + toggleGroup: 'dataLabels', + checkable: true + }, + { + caption: 'Top', + value: 'TopData', + stopPropagation: true, + toggleGroup: 'dataLabels', + checkable: true + }, + { + caption: 'Left', + value: 'LeftData', + stopPropagation: true, + toggleGroup: 'dataLabels', + checkable: true + }, + { + caption: 'Right', + value: 'RightData', + stopPropagation: true, + checkable: true + }, + { + caption: 'Bottom', + value: 'BottomData', + stopPropagation: true, + toggleGroup: 'dataLabels', + checkable: true + }, + { + caption: 'Fit', + value: 'FitWidthData', + stopPropagation: true, + toggleGroup: 'dataLabels', + checkable: true + } + ] + }) + }, + // { + // caption: me.textDataTable, + // value: 'dataTable', + // disabled: false, + // menu: new Common.UI.Menu({ + // cls: 'shifted-right', + // menuAlign: 'tl-tr', + // items: [ + // { + // caption: me.textNone, + // value: 'bShowDataNone' + // }, + // { + // caption: me.textShowDataTable, + // value: 'bShowDataTable' + // }, + // { + // caption: me.textShowLegendKeys, + // value: 'bShowLegendKeys' + // } + // ] + // }) + // }, + { + caption: 'Error bars', + value: 'errorBars', + disabled: false, + menu: new Common.UI.Menu({ + cls: 'shifted-right', + menuAlign: 'tl-tr', + items: [ + { + caption: 'Standart Error', + value: 'standardError', + stopPropagation: true, + disabled: false + }, + { + caption: 'Percentage', + value: 'percentage', + stopPropagation: true, + disabled: false + }, + { + caption: 'Standart Deviation', + value: 'standardDeviation', + stopPropagation: true, + disabled: false + } + ] + }) + }, + { + caption: 'Gridlines', + value: 'gridLines', + disabled: false, + menu: new Common.UI.Menu({ + cls: 'shifted-right', + menuAlign: 'tl-tr', + items: [ + { + caption: 'Horizontal Major', + value: 'bShowHorMajor', + stopPropagation: true, + checkable: true + }, + { + caption: 'Vertical Major', + value: 'bShowVerMajor', + stopPropagation: true, + checkable: true + }, + { + caption: 'Horizontal Minor', + value: 'bShowHorMinor', + stopPropagation: true, + checkable: true + }, + { + caption: 'Vertical Minor', + value: 'bShowVerMinor', + stopPropagation: true, + checkable: true + } + ] + }) + }, + { + caption: 'Legend', + value: 'legend', + disabled: false, + menu: new Common.UI.Menu({ + cls: 'shifted-right', + menuAlign: 'tl-tr', + items: [ + { + caption: 'Top', + value: 'TopLegend', + stopPropagation: true, + toggleGroup: 'legend', + checkable: true + }, + { + caption: 'Left', + value: 'LeftLegend', + stopPropagation: true, + toggleGroup: 'legend', + checkable: true + }, + { + caption: 'Right', + value: 'RightLegend', + stopPropagation: true, + toggleGroup: 'legend', + checkable: true + }, + { + caption: 'Bottom', + value: 'BottomLegend', + stopPropagation: true, + toggleGroup: 'legend', + checkable: true + }, + { + caption: 'Left overlay', + value: 'LeftOverlay', + stopPropagation: true, + toggleGroup: 'legend', + checkable: true + }, + { + caption: 'Right overlay', + value: 'RightOverlay', + stopPropagation: true, + toggleGroup: 'legend', + checkable: true + } + ] + }) + }, + { + caption: 'Trendline', + value: 'trendLines', + disabled: false, + menu: new Common.UI.Menu({ + cls: 'shifted-right', + menuAlign: 'tl-tr', + items: [ + { + caption: 'None', + stopPropagation: true, + value: 'trendLineNone' + }, + { + caption: 'Linear', + stopPropagation: true, + value: 'trendLineLinear' + }, + { + caption: 'Exponential', + stopPropagation: true, + value: 'trendLineExponential' + }, + { + caption: 'Linear Forecast', + stopPropagation: true, + value: 'trendLineForecast' + }, + { + caption: 'Moving average', + stopPropagation: true, + value: 'trendLineMovingAverage' + } + ] + }) + }, + // { + // caption: me.textUpDownBars, + // value: 'upDownBars', + // disabled: false, + // menu: new Common.UI.Menu({ + // cls: 'shifted-right', + // menuAlign: 'tl-tr', + // items: [ + // { + // caption: me.textNone, + // stopPropagation: true, + // value: 'bShowUpDownNone' + // }, + // { + // caption: me.textShowUpDown, + // stopPropagation: true, + // value: 'bShowUpDownBars' + // } + // ] + // }) + // } + ] + }) + }); + + this.btnChartElements = new Common.UI.Button({ + cls: 'btn-toolbar x-huge icon-top', + iconCls: 'toolbar__icon btn-freeze-panes', + caption: 'Chart Elements', + lock: [_set.sheetLock, _set.lostConnect, _set.coAuth, _set.editCell], + dataHint: '1', + dataHintDirection: 'bottom', + dataHintOffset: 'small', + menu: true, + menu: this.menuChartElement.menu + }); + this.lockedControls.push(this.btnChartElements); + + this.btnChartType = new Common.UI.Button({ + cls: 'btn-toolbar x-huge icon-top', + iconCls: 'toolbar__icon btn-big-sheet-view', + caption: 'Chart Type', + lock : [_set.lostConnect, _set.coAuth, _set.editCell], + action: 'sheet-view', + dataHint : '1', + dataHintDirection: 'bottom', + dataHintOffset: 'small' + }); + this.lockedControls.push(this.btnChartType); + + this.btnSelectData = new Common.UI.Button({ + cls: 'btn-toolbar x-huge icon-top', + iconCls: 'toolbar__icon btn-big-pivot-sum', + caption: 'Select Data', + lock : [_set.lostConnect, _set.coAuth, _set.editCell], + action: 'sheet-view', + dataHint : '1', + dataHintDirection: 'bottom', + dataHintOffset: 'small' + }); + this.lockedControls.push(this.btnSelectData); + + this.btnSwitchRowsCols = new Common.UI.Button({ + cls: 'btn-toolbar x-huge icon-top', + iconCls: 'toolbar__icon btn-big-slicer', + caption: 'Switch Row / Column', + lock : [_set.lostConnect, _set.coAuth, _set.editCell], + action: 'sheet-view', + dataHint : '1', + dataHintDirection: 'bottom', + dataHintOffset: 'small' + }); + this.lockedControls.push(this.btnSwitchRowsCols); + + this.chartStyles = new Common.UI.ComboDataView({ + cls : 'combo-chart-template', + style : 'min-width: 103px; max-width: 517px;', + enableKeyEvents : true, + itemWidth : 50, + itemHeight : 50, + menuMaxHeight : 300, + groups : new Common.UI.DataViewGroupStore(), + autoWidth : true, + lock: [_set.editCell, _set.selRangeEdit, _set.lostConnect, _set.coAuth, _set.wsLock, _set.cantModifyFilter], + beforeOpenHandler: function(e) { + var cmp = this, + menu = cmp.openButton.menu, + columnCount = 7; + + if (menu.cmpEl) { + var itemEl = $(cmp.cmpEl.find('.dataview.inner .style').get(0)).parent(); + var itemMargin = 8; + var itemWidth = itemEl.is(':visible') ? parseFloat(itemEl.css('width')) : + (cmp.itemWidth + parseFloat(itemEl.css('padding-left')) + parseFloat(itemEl.css('padding-right')) + + parseFloat(itemEl.css('border-left-width')) + parseFloat(itemEl.css('border-right-width'))); + + menu.menuAlignEl = cmp.cmpEl; + menu.menuAlign = 'tl-tl'; + var menuWidth = columnCount * (itemMargin + itemWidth) + 17, // for scroller + buttonOffsetLeft = Common.Utils.getOffset(cmp.openButton.$el).left; + if (menuWidth>Common.Utils.innerWidth()) + menuWidth = Math.max(Math.floor((Common.Utils.innerWidth()-17)/(itemMargin + itemWidth)), 2) * (itemMargin + itemWidth) + 17; + var offset = cmp.cmpEl.width() - cmp.openButton.$el.width() - Math.min(menuWidth, buttonOffsetLeft) - 1; + if (Common.UI.isRTL()) { + offset = cmp.openButton.$el.width() + parseFloat($(cmp.$el.find('.combo-dataview').get(0)).css('padding-left')); + } + menu.setOffset(Common.UI.isRTL() ? offset : Math.min(offset, 0)); + + menu.cmpEl.css({ + 'width': menuWidth, + 'min-height': cmp.cmpEl.height() + }); + } + }, + dataHint: '1', + dataHintDirection: 'bottom', + dataHintOffset: '-16, 0' + }); + this.lockedControls.push(this.chartStyles); + + this.btnAdvancedSettings = new Common.UI.Button({ + cls: 'btn-toolbar x-huge icon-top', + iconCls: 'toolbar__icon btn-day', + caption: 'Advanced Settings', + lock : [_set.lostConnect, _set.coAuth, _set.editCell], + action: 'sheet-view', + dataHint : '1', + dataHintDirection: 'bottom', + dataHintOffset: 'small' + }); + this.lockedControls.push(this.btnAdvancedSettings); + + this.spnWidth = new Common.UI.MetricSpinner({ + el: $('#chart-spin-width'), + step: .1, + width: 78, + defaultUnit : "cm", + value: '3 cm', + maxValue: 55.88, + minValue: 0, + dataHint: '1', + dataHintDirection: 'bottom', + dataHintOffset: 'big', + ariaLabel: this.textWidth + }); + this.lockedControls.push(this.spnWidth); + + this.spnHeight = new Common.UI.MetricSpinner({ + el: $('#chart-spin-height'), + step: .1, + width: 78, + defaultUnit : "cm", + value: '3 cm', + maxValue: 55.88, + minValue: 0, + dataHint: '1', + dataHintDirection: 'bottom', + dataHintOffset: 'big', + ariaLabel: this.textHeight + }); + this.lockedControls.push(this.spnHeight); + + this.chRatio = new Common.UI.CheckBox({ + labelText: 'Constant Proportions', + value: true, + lock : [_set.lostConnect, _set.editCell], + dataHint : '1', + dataHintDirection: 'left', + dataHintOffset: 'small' + }); + this.lockedControls.push(this.chRatio); + + this.spnWidth.on('inputleave', function(){ Common.NotificationCenter.trigger('edit:complete', me);}); + this.spnHeight.on('inputleave', function(){ Common.NotificationCenter.trigger('edit:complete', me);}); + + Common.UI.LayoutManager.addControls(this.lockedControls); + Common.NotificationCenter.on('app:ready', this.onAppReady.bind(this)); + }, + + onCheckStyleChange: function(type, stateName, field, newValue, oldValue, eOpts) { + var me = this; + me.fireEvent('tabledesigntab:stylechange', [type, stateName, newValue]); + }, + + render: function (el) { + if ( el ) el.html( this.getPanel() ); + + return this; + }, + + getPanel: function () { + this.$el = $(_.template(template)( {} )); + var $host = this.$el; + + this.btnChartElements && this.btnChartElements.render($host.find('#slot-btn-chart-elements')); + this.btnChartType && this.btnChartType.render($host.find('#slot-btn-chart-type')); + this.btnSelectData && this.btnSelectData.render($host.find('#slot-btn-select-data')); + this.btnSwitchRowsCols && this.btnSwitchRowsCols.render($host.find('#slot-btn-switch-rowscols')); + this.btnAdvancedSettings && this.btnAdvancedSettings.render($host.find('#slot-btn-chart-advanced-settings')); + this.chRatio && this.chRatio.render($host.find('#slot-chk-ratio')); + $host.find('#slot-lbl-width').text('Width'); + $host.find('#slot-lbl-height').text('Height'); + // this.btnRemDuplicates && this.btnRemDuplicates.render($host.find('#slot-btn-remove-duplicates')); + this.chartStyles.render(this.$el.find('#slot-field-chart-styles')); + return this.$el; + }, + + onAppReady: function (config) { + var me = this; + (new Promise(function (accept, reject) { + accept(); + })).then(function(){ + me.btnAdvancedSettings.updateHint('Advanced settings') + setEvents.call(me); + }); + }, + + show: function () { + Common.UI.BaseView.prototype.show.call(this); + this.fireEvent('show', this); + }, + + getButtons: function(type) { + return this.lockedControls + }, + + SetDisabled: function (state) { + this.lockedControls && this.lockedControls.forEach(function(button) { + if ( button ) { + button.setDisabled(state); + } + }, this); + }, + + txtRowsCols: 'Rows & Columns', + tipRowsCols: 'Rows & Columns', + txtGroupTable_Custom: 'Custom', + txtGroupTable_Light: 'Light', + txtGroupTable_Medium: 'Medium', + txtGroupTable_Dark: 'Dark', + tipRemDuplicates: 'Removing duplicate lines from a sheet.', + tipConvertRange: 'Convert this table to a regular range of cells.', + tipInsertSlicer: 'Insert slicer', + tipInsertPivot: 'Insert Pivot Table', + tipHeaderRow: 'Show or hide the header row in a table.', + tipAltText: 'Set alternative title and description for a table.', + selectRowText: 'Select row', + selectColumnText: 'Select entire column', + selectColumnData: 'Select column data', + selectTableText: 'Select table', + insertRowAboveText: 'Insert row above', + insertRowBelowText: 'Insert row below', + insertColumnLeftText: 'Insert column left', + insertColumnRightText: 'Insert column right', + deleteRowText: 'Delete row', + deleteColumnText: 'Delete column', + deleteTableText: 'Delete table', + txtRemDuplicates: 'Remove duplicates', + txtConvertToRange: 'Convert to range', + txtSlicer: 'Slicer', + txtPivot: 'Pivot', + txtHeaderRow: 'Header row', + txtTotalRow: 'Total row', + txtFirstColumn: 'First column', + txtLastColumn: 'Last column', + txtBandedRows: 'Banded rows', + txtBandedColumns: 'Banded columns', + txtFilterButton: 'Filter button', + txtAltText: 'Alt text' + } + }()), SSE.Views.ChartTab || {})); +}); diff --git a/apps/spreadsheeteditor/main/app_dev.js b/apps/spreadsheeteditor/main/app_dev.js index 07e63842fb..2328aea67e 100644 --- a/apps/spreadsheeteditor/main/app_dev.js +++ b/apps/spreadsheeteditor/main/app_dev.js @@ -136,6 +136,7 @@ require([ 'DataTab', 'ViewTab', 'TableDesignTab', + 'ChartTab', 'Search', 'WBProtection', 'Common.Controllers.Fonts', @@ -175,6 +176,7 @@ require([ 'spreadsheeteditor/main/app/controller/DataTab', 'spreadsheeteditor/main/app/controller/ViewTab', 'spreadsheeteditor/main/app/controller/TableDesignTab', + 'spreadsheeteditor/main/app/controller/ChartTab', 'spreadsheeteditor/main/app/controller/Search', 'spreadsheeteditor/main/app/controller/WBProtection', // 'spreadsheeteditor/main/app/view/ParagraphSettings',