diff --git a/apps/spreadsheeteditor/main/app/view/ChartDataDialog.js b/apps/spreadsheeteditor/main/app/view/ChartDataDialog.js new file mode 100644 index 000000000..b1af4167a --- /dev/null +++ b/apps/spreadsheeteditor/main/app/view/ChartDataDialog.js @@ -0,0 +1,585 @@ +/* + * + * (c) Copyright Ascensio System SIA 2010-2020 + * + * 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-12 Ernesta Birznieka-Upisha + * 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 + * + */ + +/** + * ChartDataDialog.js + * + * Created by Julia Radzhabova on 06.07.2020 + * Copyright (c) 2020 Ascensio System SIA. All rights reserved. + * + */ + +define([ + 'common/main/lib/util/utils', + 'common/main/lib/component/MetricSpinner', + 'common/main/lib/component/ComboBox', + 'common/main/lib/component/ListView', + 'common/main/lib/view/AdvancedSettingsWindow' +], function () { 'use strict'; + + var _CustomItem = Common.UI.DataViewItem.extend({ + initialize : function(options) { + Common.UI.BaseView.prototype.initialize.call(this, options); + + var me = this; + + me.template = me.options.template || me.template; + + me.listenTo(me.model, 'change:sort', function() { + me.render(); + me.trigger('change', me, me.model); + }); + me.listenTo(me.model, 'change:selected', function() { + var el = me.$el || $(me.el); + el.toggleClass('selected', me.model.get('selected') && me.model.get('allowSelected')); + me.onSelectChange(me.model, me.model.get('selected') && me.model.get('allowSelected')); + }); + me.listenTo(me.model, 'remove', me.remove); + } + }); + + SSE.Views.ChartDataDialog = Common.Views.AdvancedSettingsWindow.extend(_.extend({ + options: { + contentWidth: 370, + height: 490, + buttons: null + }, + + initialize : function(options) { + var me = this; + + _.extend(this.options, { + title: this.textTitle, + template: [ + '
', + '
', + '
', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '
', + '', + '
', + '
', + '
', + '', + '
', + '
', + '', + '', + '', + '
', + '
', + '
', + '
', + '
', + '', + '
', + '', + '
', + '
', + '', + '
', + '
', + '
', + '
', + '
', + '' + ].join('') + }, options); + + this.handler = options.handler; + + Common.Views.AdvancedSettingsWindow.prototype.initialize.call(this, this.options); + + this._noApply = true; + this._changedProps = null; + + this.api = this.options.api; + this.chartSettings = this.options.chartSettings; + this.dataRangeValid = ''; + this.dataDirect = 0; + this.currentChartType = Asc.c_oAscChartTypeSettings.barNormal; + }, + + render: function() { + Common.Views.AdvancedSettingsWindow.prototype.render.call(this); + var me = this; + + this.txtDataRange = new Common.UI.InputFieldBtn({ + el : $('#chart-dlg-txt-range'), + name : 'range', + style : 'width: 100%;', + btnHint : this.textSelectData, + allowBlank : true, + validateOnChange: true + }); + this.txtDataRange.on('button:click', _.bind(this.onSelectData_simple, this)); + this.txtDataRange.on('changed:after', function(input, newValue, oldValue, e) { + if (newValue !==me.dataRangeValid) { + if (me.isRangeValid()) { + me.dataRangeValid = newValue; + me.txtDataRange.checkValidate(); + me.chartSettings.putRange(me.dataRangeValid); + me.api.asc_editChartDrawingObject(me.chartSettings); + } else { + me.txtDataRange.setValue(me.dataRangeValid); + me.txtDataRange.checkValidate(); + } + } + }); + + // Chart data + this.seriesList = new Common.UI.ListView({ + el: $('#chart-dlg-series-list', this.$window), + store: new Common.UI.DataViewStore(), + emptyText: '', + scrollAlwaysVisible: true, + template: _.template(['
'].join('')), + itemTemplate: _.template([ + '
', + '', + '
', + '
<%= Common.Utils.String.htmlEncode(value) %>
', + '
', + '
' + ].join('')) + }); + this.seriesList.createNewItem = function(record) { + return new _CustomItem({ + template: this.itemTemplate, + model: record + }); + }; + this.seriesList.on({ + 'item:change': this.onItemChanged.bind(this), + 'item:add': this.onItemChanged.bind(this), + 'item:select': this.onCellCheck.bind(this) + }); + this.seriesList.onKeyDown = _.bind(this.onListKeyDown, this, 'series'); + this.seriesList.createNewItem = function(record) { + return new _CustomItem({ + template: this.itemTemplate, + model: record + }); + }; + this.seriesList.on('item:select', _.bind(this.onSelectSeries, this)); + + this.btnAdd = new Common.UI.Button({ + el: $('#chart-dlg-btn-add') + }); + this.btnAdd.on('click', _.bind(this.onAddSeries, this, false)); + + this.btnDelete = new Common.UI.Button({ + el: $('#chart-dlg-btn-delete') + }); + // this.btnDelete.on('click', _.bind(this.onDeleteSeries, this)); + + this.btnEdit = new Common.UI.Button({ + el: $('#chart-dlg-btn-edit') + }); + this.btnEdit.on('click', _.bind(this.onEditSeries, this, false)); + + this.btnUp = new Common.UI.Button({ + parentEl: $('#chart-dlg-btn-up'), + cls: 'btn-toolbar', + iconCls: 'caret-up', + hint: this.textUp + }); + // this.btnUp.on('click', _.bind(this.onMoveClick, this, true)); + + this.btnDown = new Common.UI.Button({ + parentEl: $('#chart-dlg-btn-down'), + cls: 'btn-toolbar', + iconCls: 'caret-down', + hint: this.textDown + }); + // this.btnDown.on('click', _.bind(this.onMoveClick, this, false)); + + this.btnSwitch = new Common.UI.Button({ + el: $('#chart-dlg-btn-switch') + }); + // this.btnSwitch.on('click', _.bind(this.onSwitch, this)); + + this.categoryList = new Common.UI.ListView({ + el: $('#chart-dlg-category-list', this.$window), + store: new Common.UI.DataViewStore(), + emptyText: '', + scrollAlwaysVisible: true, + template: _.template(['
'].join('')), + itemTemplate: _.template([ + '
', + '', + '
', + '
<%= Common.Utils.String.htmlEncode(value) %>
', + '
', + '
' + ].join('')) + }); + this.categoryList.on({ + 'item:change': this.onItemChanged.bind(this), + 'item:add': this.onItemChanged.bind(this), + 'item:select': this.onCellCheck.bind(this) + }); + this.categoryList.onKeyDown = _.bind(this.onListKeyDown, this, 'category'); + + this.btnEditCategory = new Common.UI.Button({ + el: $('#chart-dlg-btn-category-edit') + }); + this.btnEditCategory.on('click', _.bind(this.onEditCategory, this, false)); + + this.afterRender(); + }, + + afterRender: function() { + this._setDefaults(this.chartSettings); + }, + + show: function() { + Common.Views.AdvancedSettingsWindow.prototype.show.apply(this, arguments); + + var me = this; + _.delay(function(){ + me.txtDataRange.cmpEl.find('input').focus(); + },50); + }, + + close: function () { + this.api.asc_onCloseChartFrame(); + Common.Views.AdvancedSettingsWindow.prototype.close.apply(this, arguments); + }, + + _setDefaults: function (props) { + var me = this; + if (props ){ + this.chartSettings = props; + + this.currentChartType = props.getType(); + + var value = props.getRange(); + this.txtDataRange.setValue((value) ? value : ''); + this.dataRangeValid = value; + + this.txtDataRange.validation = function(value) { + if (_.isEmpty(value)) { + return true; + } + + var isvalid = me.api.asc_checkDataRange(Asc.c_oAscSelectionDialogType.Chart, value, false); + return (isvalid==Asc.c_oAscError.ID.DataRangeError) ? me.textInvalidRange : true; + }; + + this.dataDirect = props.getInColumns() ? 1 : 0; + } + }, + + getSettings: function () { + this.chartSettings.putRange(this.txtDataRange.getValue()); + return { chartSettings: this.chartSettings}; + }, + + onDlgBtnClick: function(event) { + var me = this; + var state = (typeof(event) == 'object') ? event.currentTarget.attributes['result'].value : event; + if (state == 'ok') { + this.handler && this.handler.call(this, state, (state == 'ok') ? this.getSettings() : undefined); + } + + this.close(); + }, + + onPrimary: function() { + this.onDlgBtnClick('ok'); + return false; + }, + + isRangeValid: function() { + var isvalid; + if (!_.isEmpty(this.txtDataRange.getValue())) { + isvalid = this.api.asc_checkDataRange(Asc.c_oAscSelectionDialogType.Chart, this.txtDataRange.getValue(), true, this.dataDirect===0, this.currentChartType); + if (isvalid == Asc.c_oAscError.ID.No) + return true; + } else + return true; + + if (isvalid == Asc.c_oAscError.ID.StockChartError) { + Common.UI.warning({msg: this.errorStockChart}); + } else if (isvalid == Asc.c_oAscError.ID.MaxDataSeriesError) { + Common.UI.warning({msg: this.errorMaxRows}); + } else if (isvalid == Asc.c_oAscError.ID.MaxDataPointsError) + Common.UI.warning({msg: this.errorMaxPoints}); + else + this.txtDataRange.cmpEl.find('input').focus(); + return false; + }, + + onSelectData_simple: function() { + var me = this; + if (me.api) { + var props = me.chartSettings; + var handlerDlg = function(dlg, result) { + if (result == 'ok' && me.isRangeValid()) { + me.dataRangeValid = dlg.getSettings(); + me.txtDataRange.setValue(me.dataRangeValid); + me.txtDataRange.checkValidate(); + props.putRange(me.dataRangeValid); + me.api.asc_setSelectionDialogMode(Asc.c_oAscSelectionDialogType.None); + me.api.asc_editChartDrawingObject(props); + } + }; + + var win = new SSE.Views.CellRangeDialog({ + handler: handlerDlg + }).on('close', function() { + me.show(); + }); + + var xy = me.$window.offset(); + me.hide(); + win.show(xy.left + 160, xy.top + 125); + win.setSettings({ + api : me.api, + range : (!_.isEmpty(me.txtDataRange.getValue()) && (me.txtDataRange.checkValidate()==true)) ? me.txtDataRange.getValue() : me.dataRangeValid, + type : Asc.c_oAscSelectionDialogType.Chart + }); + } + }, + + onSelectData: function() { + var me = this; + if (me.api) { + var props = me.chartSettings, + handlerDlg = function(dlg, result) { + if (result == 'ok' && me.isRangeValid()) { + props.putRange(dlg.getSettings()); + me.api.asc_setSelectionDialogMode(Asc.c_oAscSelectionDialogType.None); + me.api.asc_editChartDrawingObject(props); + } + }, + validation = function(value) { + var isvalid; + if (!_.isEmpty(value)) { + isvalid = me.api.asc_checkDataRange(Asc.c_oAscSelectionDialogType.Chart, value, true, !props.getInColumns(), me.currentChartType); + if (isvalid == Asc.c_oAscError.ID.No) + return true; + } else return ''; + + if (isvalid == Asc.c_oAscError.ID.StockChartError) { + return this.errorStockChart; + } else if (isvalid == Asc.c_oAscError.ID.MaxDataSeriesError) { + return this.errorMaxRows; + } + return this.txtInvalidRange; + }; + + var win = new SSE.Views.CellRangeDialog({ + handler: handlerDlg + }).on('close', function() { + me.show(); + // me.api.asc_onCloseChartFrame(); + }); + + var xy = me.$window.offset(); + me.hide(); + win.show(xy.left + 160, xy.top + 125); + win.setSettings({ + api : me.api, + range : props.getRange(), + validation: validation, + type : Asc.c_oAscSelectionDialogType.Chart + }); + } + }, + + onItemChanged: function (view, record) { + var state = record.model.get('check'); + if ( state == 'indeterminate' ) + $('input[type=checkbox]', record.$el).prop('indeterminate', true); + else $('input[type=checkbox]', record.$el).prop({checked: state, indeterminate: false}); + }, + + onCellCheck: function (listView, itemView, record) { + if (this.checkCellTrigerBlock) + return; + + var target = '', isLabel = false, bound = null; + + var event = window.event ? window.event : window._event; + if (event) { + target = $(event.currentTarget).find('.list-item'); + + if (target.length) { + bound = target.get(0).getBoundingClientRect(); + var _clientX = event.clientX*Common.Utils.zoom(), + _clientY = event.clientY*Common.Utils.zoom(); + if (bound.left < _clientX && _clientX < bound.right && + bound.top < _clientY && _clientY < bound.bottom) { + isLabel = true; + } + } + + if (isLabel || event.target.className.match('checkbox')) { + this.updateCellCheck(listView, record); + + _.delay(function () { + listView.$el.find('.listview').focus(); + }, 100, this); + } + } + }, + + onListKeyDown: function (type, e, data) { + var record = null, listView = (type=='series') ? this.seriesList : this.categoryList; + + if (listView.disabled) return; + if (_.isUndefined(undefined)) data = e; + + if (data.keyCode == Common.UI.Keys.SPACE) { + data.preventDefault(); + data.stopPropagation(); + + this.updateCellCheck(listView, listView.getSelectedRec()); + } else if (type=='series' && data.keyCode==Common.UI.Keys.DELETE && !this.btnDelete.isDisabled()) { + // this.onDeleteSeries(); + } else { + Common.UI.DataView.prototype.onKeyDown.call(listView, e, data); + } + }, + + updateCellCheck: function (listView, record) { + if (record && listView) { + record.set('check', !record.get('check')); + // listView.scroller.update({minScrollbarLength : 40, alwaysVisibleY: true, suppressScrollX: true}); + } + }, + + onSelectSeries: function(lisvView, itemView, record) { + this.updateMoveButtons(); + }, + + updateMoveButtons: function() { + var rec = this.seriesList.getSelectedRec(), + index = rec ? this.seriesList.store.indexOf(rec) : -1; + this.btnUp.setDisabled(index<1); + this.btnDown.setDisabled(index<0 || index==this.seriesList.store.length-1); + }, + + onAddSeries: function() { + var handlerDlg = function(dlg, result) { + if (result == 'ok') { + var changedValue = dlg.getSettings(); + } + }; + this.changeDataRange(1, true, handlerDlg); + }, + + onEditSeries: function() { + var handlerDlg = function(dlg, result) { + if (result == 'ok') { + var changedValue = dlg.getSettings(); + } + }; + this.changeDataRange(1, false, handlerDlg); + }, + + onEditCategory: function() { + var handlerDlg = function(dlg, result) { + if (result == 'ok') { + var changedValue = dlg.getSettings(); + } + }; + this.changeDataRange(0, false, handlerDlg); + }, + + changeDataRange: function(type, add, handlerDlg) { + var me = this; + var win = new SSE.Views.ChartDataRangeDialog({ + type: type, //series + handler: handlerDlg + }).on('close', function() { + me.show(); + }); + + var xy = me.$window.offset(); + me.hide(); + win.show(xy.left + 160, xy.top + 125); + win.setSettings({ + api : me.api + }); + }, + + textTitle: 'Chart Data', + txtEmpty: 'This field is required', + textInvalidRange: 'ERROR! Invalid cells range', + textSelectData: 'Select data', + errorMaxRows: 'ERROR! The maximum number of data series per chart is 255.', + errorStockChart: 'Incorrect row order. To build a stock chart place the data on the sheet in the following order:
opening price, max price, min price, closing price.', + errorMaxPoints: 'ERROR! The maximum number of points in series per chart is 4096.', + textSeries: 'Legend Entries (Series)', + textAdd: 'Add', + textEdit: 'Edit', + textDelete: 'Remove', + textSwitch: 'Switch Row/Column', + textCategory: 'Horizontal (Category) Axis Labels', + textUp: 'Up', + textDown: 'Down', + textData: 'Data' + }, SSE.Views.ChartDataDialog || {})) +}); diff --git a/apps/spreadsheeteditor/main/app/view/ChartSettings.js b/apps/spreadsheeteditor/main/app/view/ChartSettings.js index 831622d30..0afbfdec4 100644 --- a/apps/spreadsheeteditor/main/app/view/ChartSettings.js +++ b/apps/spreadsheeteditor/main/app/view/ChartSettings.js @@ -46,7 +46,8 @@ define([ 'common/main/lib/component/Button', 'common/main/lib/component/MetricSpinner', 'common/main/lib/component/ComboDataView', - 'spreadsheeteditor/main/app/view/ChartSettingsDlg' + 'spreadsheeteditor/main/app/view/ChartSettingsDlg', + 'spreadsheeteditor/main/app/view/ChartDataDialog' ], function (menuTemplate, $, _, Backbone) { 'use strict'; @@ -851,7 +852,7 @@ define([ } }, - onSelectData: function() { + onSelectData_simple: function() { var me = this; if (me.api) { var props = me.api.asc_getChartObject(), @@ -895,7 +896,29 @@ define([ }); } }, - + + onSelectData: function() { + var me = this; + var win, props; + if (me.api){ + props = me.api.asc_getChartObject(); + if (props) { + (new SSE.Views.ChartDataDialog( + { + chartSettings: props, + api: me.api, + handler: function(result, value) { + if (result == 'ok') { + if (me.api) { + } + } + Common.NotificationCenter.trigger('edit:complete', me); + } + })).show(); + } + } + }, + onSelectType: function(btn, picker, itemView, record) { if (this._noApply) return;