From 3998229504afe5a05699594c9863c622aeeab84d Mon Sep 17 00:00:00 2001 From: Marius Date: Fri, 21 Apr 2017 11:08:21 +0200 Subject: [PATCH 1/2] Added support for auto filters --- README.md | 36 +++++++++ lib/doc/worksheet.js | 5 +- lib/stream/xlsx/workbook-writer.js | 3 +- lib/stream/xlsx/worksheet-writer.js | 11 ++- lib/xlsx/xform/sheet/auto-filter-xform.js | 75 +++++++++++++++++++ lib/xlsx/xform/sheet/worksheet-xform.js | 3 + .../xform/sheet/auto-filter-xform.spec.js | 27 +++++++ 7 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 lib/xlsx/xform/sheet/auto-filter-xform.js create mode 100644 spec/unit/xlsx/xform/sheet/auto-filter-xform.spec.js diff --git a/README.md b/README.md index c4396f312..b87e7b017 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ I have just one request; If you submit a pull request for a bugfix, please add a
  • Split Views
  • +
  • Auto Filters
  • Columns
  • Rows
  • Handling Individual Cells
  • @@ -371,6 +372,41 @@ worksheet.views = [ ]; ``` +## Auto filters + +It is possible to apply an auto filter to your worksheet. + +```javascript +// Set an auto filter from A1 to C1 +worksheet.autoFilter = { + from: 'A1', + to: 'C1', +} + +// Set an auto filter from the cell in row 3 and column 1 +// to the cell in row 5 and column 12 +worksheet.autoFilter = { + from: { + row: 3, + column: 1 + }, + to: { + row: 5, + column: 12 + } +} + +// Set an auto filter from D3 to the +// cell in row 7 and column 5 +worksheet.autoFilter = { + from: 'D3', + to: { + row: 7, + column: 5 + } +} +``` + ## Columns ```javascript diff --git a/lib/doc/worksheet.js b/lib/doc/worksheet.js index 08b8d560b..bd2709cd5 100644 --- a/lib/doc/worksheet.js +++ b/lib/doc/worksheet.js @@ -100,6 +100,8 @@ var Worksheet = module.exports = function(options) { // for freezepanes, split, zoom, gridlines, etc this.views = options.views || []; + + this.autoFilter = options.autoFilter || null; }; Worksheet.prototype = { @@ -481,7 +483,8 @@ Worksheet.prototype = { dataValidations: this.dataValidations.model, properties: this.properties, pageSetup: this.pageSetup, - views: this.views + views: this.views, + autoFilter: this.autoFilter }; // ================================================= diff --git a/lib/stream/xlsx/workbook-writer.js b/lib/stream/xlsx/workbook-writer.js index e39a9411c..01c8f9b9e 100644 --- a/lib/stream/xlsx/workbook-writer.js +++ b/lib/stream/xlsx/workbook-writer.js @@ -180,7 +180,8 @@ WorkbookWriter.prototype = { useSharedStrings: useSharedStrings, properties: options.properties, pageSetup: options.pageSetup, - views: options.views + views: options.views, + autoFilter: options.autoFilter }); this._worksheets[id] = worksheet; diff --git a/lib/stream/xlsx/worksheet-writer.js b/lib/stream/xlsx/worksheet-writer.js index e29144576..3eb04df6d 100644 --- a/lib/stream/xlsx/worksheet-writer.js +++ b/lib/stream/xlsx/worksheet-writer.js @@ -50,6 +50,7 @@ var HyperlinkXform = require('../../xlsx/xform/sheet/hyperlink-xform'); var SheetViewXform = require('../../xlsx/xform/sheet/sheet-view-xform'); var PageMarginsXform = require('../../xlsx/xform/sheet/page-margins-xform'); var PageSetupXform = require('../../xlsx/xform/sheet/page-setup-xform'); +var AutoFilterXform = require('../../xlsx/xform/sheet/auto-filter-xform'); // since prepare and render is functional, we can use singletons var xform = { @@ -61,7 +62,8 @@ var xform = { hyperlinks: new ListXform({tag: 'hyperlinks', length: false, childXform: new HyperlinkXform()}), sheetViews: new ListXform({tag: 'sheetViews', length: false, childXform: new SheetViewXform()}), pageMargins: new PageMarginsXform(), - pageSeteup: new PageSetupXform() + pageSeteup: new PageSetupXform(), + autoFilter: new AutoFilterXform() }; @@ -143,6 +145,9 @@ var WorksheetWriter = module.exports = function(options) { // views this._views = options.views || []; + // auto filter + this.autoFilter = options.autoFilter || null; + // start writing to stream now this._writeOpenWorksheet(); @@ -190,6 +195,7 @@ WorksheetWriter.prototype = { this._writeOpenSheetData(); } this._writeCloseSheetData(); + this._writeAutoFilter(); this._writeMergeCells(); // for some reason, Excel can't handle dimensions at the bottom of the file @@ -492,6 +498,9 @@ WorksheetWriter.prototype = { _writePageSetup: function() { this.stream.write(xform.pageSeteup.toXml(this.pageSetup)); }, + _writeAutoFilter: function() { + this.stream.write(xform.autoFilter.toXml(this.autoFilter)); + }, _writeDimensions: function() { this._write(''); }, diff --git a/lib/xlsx/xform/sheet/auto-filter-xform.js b/lib/xlsx/xform/sheet/auto-filter-xform.js new file mode 100644 index 000000000..78b7b4566 --- /dev/null +++ b/lib/xlsx/xform/sheet/auto-filter-xform.js @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2015 Guyon Roche + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +'use strict'; + +var utils = require('../../../utils/utils'); +var colCache = require('../../../utils/col-cache'); +var BaseXform = require('../base-xform'); + +var AutoFilterXform = module.exports = function() { +}; + +utils.inherits(AutoFilterXform, BaseXform, { + + get tag() { return 'autoFilter'; }, + + render: function(xmlStream, model) { + function getAddress (autoFilterObject) { + if (autoFilterObject) { + if (typeof autoFilterObject === 'string') { + return colCache.getAddress(autoFilterObject); + } else if (autoFilterObject.row && autoFilterObject.column) { + return colCache.getAddress(autoFilterObject.row, autoFilterObject.column); + } + } + } + if (model) { + var firstAddress = getAddress(model.from); + var secondAddress = getAddress(model.to); + if (firstAddress && secondAddress) { + xmlStream.leafNode('autoFilter', {ref: firstAddress.address + ':' + secondAddress.address}); + } + } + }, + + parseOpen: function(node) { + if (node.name === 'autoFilter') { + var cells = node.attributes.ref.split(':'); + if (cells.length === 2) { + var firstAddress = colCache.getAddress(cells[0]); + var secondAddress = colCache.getAddress(cells[1]); + this.model = { + from: { + row: firstAddress.row, + column: firstAddress.col + }, + to: { + row: secondAddress.row, + column: secondAddress.col + } + } + } + } + } + +}); \ No newline at end of file diff --git a/lib/xlsx/xform/sheet/worksheet-xform.js b/lib/xlsx/xform/sheet/worksheet-xform.js index e6c9d6c8e..f9d8f116b 100644 --- a/lib/xlsx/xform/sheet/worksheet-xform.js +++ b/lib/xlsx/xform/sheet/worksheet-xform.js @@ -45,6 +45,7 @@ var SheetViewXform = require('./sheet-view-xform'); var PageMarginsXform = require('./page-margins-xform'); var PageSetupXform = require('./page-setup-xform'); var PrintOptionsXform = require('./print-options-xform'); +var AutoFilterXform = require('./auto-filter-xform'); var WorkSheetXform = module.exports = function() { this.map = { @@ -54,6 +55,7 @@ var WorkSheetXform = module.exports = function() { sheetFormatPr: new SheetFormatPropertiesXform(), cols: new ListXform({tag: 'cols', count: false, childXform: new ColXform()}), sheetData: new ListXform({tag: 'sheetData', count: false, empty: true, childXform: new RowXform()}), + autoFilter: new AutoFilterXform(), mergeCells: new ListXform({tag: 'mergeCells', count: true, childXform: new MergeCellXform()}), hyperlinks: new ListXform({tag: 'hyperlinks', count: false, childXform: new HyperlinkXform()}), pageMargins: new PageMarginsXform(), @@ -114,6 +116,7 @@ utils.inherits(WorkSheetXform, BaseXform, { this.map.sheetFormatPr.render(xmlStream, sheetFormatPropertiesModel); this.map.cols.render(xmlStream, model.cols); this.map.sheetData.render(xmlStream, model.rows); + this.map.autoFilter.render(xmlStream, model.autoFilter); this.map.mergeCells.render(xmlStream, model.mergeCells); this.map.dataValidations.render(xmlStream, model.dataValidations); this.map.hyperlinks.render(xmlStream, model.hyperlinks);//For some reason hyperlinks have to be after the data validations diff --git a/spec/unit/xlsx/xform/sheet/auto-filter-xform.spec.js b/spec/unit/xlsx/xform/sheet/auto-filter-xform.spec.js new file mode 100644 index 000000000..d488af005 --- /dev/null +++ b/spec/unit/xlsx/xform/sheet/auto-filter-xform.spec.js @@ -0,0 +1,27 @@ +'use strict'; + +var AutoFilterXform = require('../../../../../lib/xlsx/xform/sheet/auto-filter-xform'); +var testXformHelper = require('./../test-xform-helper'); + +var expectations = [ + { + title: 'Row and Column Adress', + create: function() { return new AutoFilterXform(); }, + preparedModel: {from: {row: 1, column: 1}, to: {row: 1, column: 3}}, + xml: '', + parsedModel: {from: {row: 1, column: 1}, to: {row: 1, column: 3}}, + tests: ['render', 'renderIn', 'parse'] + }, + { + title: 'String address', + create: function() { return new AutoFilterXform(); }, + preparedModel: {from: 'A1', to: 'C1'}, + xml: '', + parsedModel: {from: {row: 1, column: 1}, to: {row: 1, column: 3}}, + tests: ['render', 'renderIn', 'parse'] + } +]; + +describe('AutoFilterXform', function() { + testXformHelper(expectations); +}); \ No newline at end of file From 310be1aa49dac74c836b4929bde21556986464d0 Mon Sep 17 00:00:00 2001 From: Marius Date: Tue, 25 Apr 2017 23:20:14 +0200 Subject: [PATCH 2/2] Resolve conflicts in worksheet-writer.js Because of a commit while this pull request has not be merged, some spaces had to be removed in order to resolve the conflicts. --- lib/stream/xlsx/worksheet-writer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/stream/xlsx/worksheet-writer.js b/lib/stream/xlsx/worksheet-writer.js index 3eb04df6d..23d852cf9 100644 --- a/lib/stream/xlsx/worksheet-writer.js +++ b/lib/stream/xlsx/worksheet-writer.js @@ -47,9 +47,9 @@ var SheetFormatPropertiesXform = require('../../xlsx/xform/sheet/sheet-format-pr var ColXform = require('../../xlsx/xform/sheet/col-xform'); var RowXform = require('../../xlsx/xform/sheet/row-xform'); var HyperlinkXform = require('../../xlsx/xform/sheet/hyperlink-xform'); -var SheetViewXform = require('../../xlsx/xform/sheet/sheet-view-xform'); -var PageMarginsXform = require('../../xlsx/xform/sheet/page-margins-xform'); -var PageSetupXform = require('../../xlsx/xform/sheet/page-setup-xform'); +var SheetViewXform = require('../../xlsx/xform/sheet/sheet-view-xform'); +var PageMarginsXform = require('../../xlsx/xform/sheet/page-margins-xform'); +var PageSetupXform = require('../../xlsx/xform/sheet/page-setup-xform'); var AutoFilterXform = require('../../xlsx/xform/sheet/auto-filter-xform'); // since prepare and render is functional, we can use singletons @@ -507,4 +507,4 @@ WorksheetWriter.prototype = { _writeCloseWorksheet: function() { this._write(''); } -}; \ No newline at end of file +};