diff --git a/types/exceljs/exceljs-tests.ts b/types/exceljs/exceljs-tests.ts index 95147c7726..12b3337cb8 100644 --- a/types/exceljs/exceljs-tests.ts +++ b/types/exceljs/exceljs-tests.ts @@ -1,4 +1,7 @@ import * as Excel from 'exceljs'; +import * as stream from 'stream'; +import * as fs from 'fs'; +import * as Bluebird from 'bluebird'; // most examples taken and adapted from README.md @@ -18,65 +21,446 @@ workbook.views = [ } ]; -const worksheet: Excel.Worksheet = workbook.addWorksheet('My Sheet'); +const worksheet = workbook.addWorksheet('My Sheet'); + +workbook.eachSheet((worksheet, sheetId) => null); + const worksheet2 = workbook.getWorksheet('My Sheet'); const worksheet3 = workbook.getWorksheet(1); -const sheet = workbook.addWorksheet('My Sheet', {views: [{showGridLines: false}]}); +const sheet1 = workbook.addWorksheet('My Sheet', { + pageSetup: { fitToPage: true, fitToHeight: 5, fitToWidth: 7 }, + properties: { tabColor: { argb: 'FFC0000' } }, +}); +const sheet2 = workbook.addWorksheet('My Sheet', { + pageSetup: { paperSize: 9, orientation: 'landscape' }, + views: [ + { state: 'frozen', xSplit: 1, ySplit: 1 }, + { state: 'normal', showGridLines: false }, + { state: 'split', activePane: 'bottomLeft', } + ] +}); + +worksheet.properties.outlineLevelCol = 2; +worksheet.properties.defaultRowHeight = 15; + +worksheet.pageSetup.margins = { + left: 0.7, right: 0.7, + top: 0.75, bottom: 0.75, + header: 0.3, footer: 0.3 +}; + +worksheet.pageSetup.printArea = 'A1:G20'; +worksheet.pageSetup.printTitlesRow = '1:3'; + +worksheet.views = [ + { state: 'frozen', xSplit: 2, ySplit: 3, topLeftCell: 'G10', activeCell: 'A1' }, + { state: 'split', xSplit: 2000, ySplit: 3000, topLeftCell: 'G10', activeCell: 'A1' }, +]; + +worksheet.autoFilter = { from: 'A1', to: 'C1' }; +worksheet.autoFilter = { from: { row: 3, column: 1 }, to: { row: 5, column: 12 } }; +worksheet.autoFilter = { from: 'D3', to: { row: 7, column: 5 } }; + +worksheet.columns = [ + { header: 'Id', key: 'id', width: 10 }, + { header: 'Name', key: 'name', width: 32 }, + { header: 'D.O.B.', key: 'DOB', width: 10, outlineLevel: 1 } +]; + +const idCol = worksheet.getColumn('id'); +const nameCol = worksheet.getColumn('B'); +const dobCol = worksheet.getColumn(3); + +dobCol.header = 'Date of Birth'; +dobCol.header = ['Date of Birth', 'A.K.A. D.O.B.']; +dobCol.key = 'dob'; +dobCol.width = 15; +dobCol.hidden = true; +worksheet.getColumn(4).outlineLevel = 0; +const cl1 = worksheet.getColumn(4).collapsed; + +dobCol.eachCell((cell, rowNumber) => null); + +dobCol.eachCell({ includeEmpty: true }, (cell, rowNumber) => null); + +worksheet.spliceColumns(3, 2); + +const newCol3Values = [1, 2, 3, 4, 5]; +const newCol4Values = ['one', 'two', 'three', 'four', 'five']; +worksheet.spliceColumns(3, 1, newCol3Values, newCol4Values); + +worksheet.addRow({ id: 1, name: 'John Doe', dob: new Date(1970, 1, 1) }); +worksheet.addRow({ id: 2, name: 'Jane Doe', dob: new Date(1965, 1, 7) }); +worksheet.addRow([3, 'Sam', new Date()]); +const rowValues = []; +rowValues[1] = 4; +rowValues[5] = 'Kyle'; +rowValues[9] = new Date(); +worksheet.addRow(rowValues); +const rows = [ + [5, 'Bob', new Date()], + { id: 6, name: 'Barbara', dob: new Date() }, +]; +worksheet.addRows(rows); + +const row = worksheet.getRow(5); +const row1 = worksheet.lastRow; + +row.height = 42.5; +row.hidden = true; +worksheet.getRow(4).outlineLevel = 0; +worksheet.getRow(5).outlineLevel = 1; +worksheet.getRow(4).collapsed; -const row: Excel.Row = worksheet.addRow([3, 'Sam', new Date()]); row.getCell(1).value = 5; +row.getCell('name').value = 'Zeb'; +row.getCell('C').value = new Date(); -worksheet.eachRow((row: Excel.Row, rowNumber: number): void => { - // nothing -}); +const row2 = worksheet.getRow(4).values; -row.eachCell((cell: Excel.Cell, colNumber: number): void => { - // nothing -}); +row.values = [1, 2, 3]; + +const values = []; +values[5] = 7; +values[10] = 'Hello, World!'; +row.values = values; + +row.values = { id: 13, name: 'Thing 1', dob: new Date() }; + +worksheet.eachRow((row, rowNumber) => null); +worksheet.eachRow({ includeEmpty: true }, (row, rowNumber) => null); + +row.eachCell((cell, colNumber) => null); +row.eachCell({ includeEmpty: true }, (cell, colNumber) => null); + +worksheet.spliceRows(4, 3); + +const newRow3Values = [1, 2, 3, 4, 5]; +const newRow4Values = ['one', 'two', 'three', 'four', 'five']; +worksheet.spliceRows(3, 1, newRow3Values, newRow4Values); + +row.splice(3, 2); + +row.splice(4, 1, 'new value 1', 'new value 2'); + +row.commit(); + +const rowSize = row.cellCount; +const numValues = row.actualCellCount; + +worksheet.getCell('C3').value = new Date(1968, 5, 1); +const isDate = worksheet.getCell('C3').type === Excel.ValueType.Date; worksheet.mergeCells('A4:B5'); -worksheet.getCell('A1').numFmt = '0.00%'; -worksheet.getCell('A1').font = { - name: 'Comic Sans MS', - family: 4, - size: 16, - underline: true, - bold: true +worksheet.getCell('B5').value = 'Hello, World!'; +const master = worksheet.getCell('B5').master === worksheet.getCell('A4'); + +worksheet.unMergeCells('A4'); + +worksheet.mergeCells('G10', 'H11'); +worksheet.mergeCells(10, 11, 12, 13); + +worksheet.getCell('A1').name = 'PI'; +worksheet.getCell('A1').names = ['thing1', 'thing2']; +worksheet.getCell('A1').removeName('thing1'); + +worksheet.getCell('A1').dataValidation = { + type: 'list', + allowBlank: true, + formulae: ['"One,Two,Three,Four"'] }; +worksheet.getCell('A1').dataValidation = { + type: 'list', + allowBlank: true, + formulae: ['$D$5:$F$5'] +}; + +worksheet.getCell('A1').dataValidation = { + type: 'whole', + operator: 'notEqual', + showErrorMessage: true, + formulae: [5], + errorStyle: 'error', + errorTitle: 'Five', + error: 'The value must not be Five' +}; + +worksheet.getCell('A1').dataValidation = { + type: 'decimal', + operator: 'between', + allowBlank: true, + showInputMessage: true, + formulae: [1.5, 7], + promptTitle: 'Decimal', + prompt: 'The value must between 1.5 and 7' +}; + +worksheet.getCell('A1').dataValidation = { + type: 'textLength', + operator: 'lessThan', + showErrorMessage: true, + allowBlank: true, + formulae: [15] +}; + +worksheet.getCell('A1').dataValidation = { + type: 'date', + operator: 'lessThan', + showErrorMessage: true, + allowBlank: true, + formulae: [new Date(2016, 0, 1)] +}; + +worksheet.getCell('A1').value = null; +worksheet.getCell('A1').value = 5; +worksheet.getCell('A2').value = 3.14159; +worksheet.getCell('A1').value = 'Hello, World!'; +worksheet.getCell('A1').value = new Date(2017, 2, 15); +worksheet.getCell('A1').value = { text: 'www.mylink.com', hyperlink: 'http://www.mylink.com' }; +worksheet.getCell('A1').value = { text: 'Sheet2', hyperlink: '#\\"Sheet2\\"!A1' }; +worksheet.getCell('A3').value = { formula: 'A1+A2', result: 7 }; +worksheet.getCell('B3').value = { sharedFormula: 'A3', result: 10 }; +worksheet.getCell('A1').value = true; +worksheet.getCell('A2').value = false; +worksheet.getCell('A1').value = { error: '#N/A' }; +worksheet.getCell('A2').value = { error: '#VALUE!' }; +worksheet.getCell('A1').value = { + richText: [ + { text: 'This is ' }, + { font: { italic: true }, text: 'italic' } + ], +}; +worksheet.getCell('A3').formula === 'A1+A2'; +worksheet.getCell('A3').result === 7; + +const isMaster = worksheet.getCell('A3').formulaType === Excel.FormulaType.Master; +const isShared = worksheet.getCell('B3').formulaType === Excel.FormulaType.Shared; + +worksheet.getCell('A1').numFmt = '0.00%'; +worksheet.columns = [ + { header: 'Id', key: 'id', width: 10 }, + { header: 'Name', key: 'name', width: 32, style: { font: { name: 'Arial Black' } } }, + { header: 'D.O.B.', key: 'DOB', width: 10, style: { numFmt: 'dd/mm/yyyy' } } +]; + +worksheet.getColumn(3).numFmt = '"£"#,##0.00;[Red]\-"£"#,##0.00'; + +worksheet.getRow(2).font = { + name: 'Comic Sans MS', + family: 4, + size: 16, + underline: 'double', + bold: true, +}; + +worksheet.getCell('A1').numFmt = '# ?/?'; +worksheet.getCell('B1').numFmt = '0.00%'; + +worksheet.getCell('A1').font = { + name: 'Comic Sans MS', + family: 4, + size: 16, + underline: true, + bold: true +}; + +worksheet.getCell('A2').font = { + name: 'Arial Black', + color: { argb: 'FF00FF00' }, + family: 2, + size: 14, + italic: true +}; + +const font = { name: 'Arial', size: 12 }; +worksheet.getCell('A3').font = font; +font.size = 20; + worksheet.getCell('A1').alignment = { vertical: 'top', horizontal: 'left' }; worksheet.getCell('B1').alignment = { vertical: 'middle', horizontal: 'center' }; worksheet.getCell('C1').alignment = { vertical: 'bottom', horizontal: 'right' }; worksheet.getCell('D1').alignment = { wrapText: true }; worksheet.getCell('E1').alignment = { indent: 1 }; +worksheet.getCell('F1').alignment = { textRotation: 30 }; +worksheet.getCell('G1').alignment = { textRotation: -45 }; +worksheet.getCell('H1').alignment = { textRotation: 'vertical' }; worksheet.getCell('A1').border = { - top: {style: 'thin'}, - left: {style: 'thin'}, - bottom: {style: 'thin'}, - right: {style: 'thin'} + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } }; worksheet.getCell('A3').border = { - top: {style: 'double', color: {argb: 'FF00FF00'}}, - left: {style: 'double', color: {argb: 'FF00FF00'}}, - bottom: {style: 'double', color: {argb: 'FF00FF00'}}, - right: {style: 'double', color: {argb: 'FF00FF00'}} + top: { style: 'double', color: { argb: 'FF00FF00' } }, + left: { style: 'double', color: { argb: 'FF00FF00' } }, + bottom: { style: 'double', color: { argb: 'FF00FF00' } }, + right: { style: 'double', color: { argb: 'FF00FF00' } } +}; + +worksheet.getCell('A5').border = { + diagonal: { up: true, down: true, style: 'thick', color: { argb: 'FFFF0000' } } }; worksheet.getCell('A1').fill = { - type: 'pattern', - pattern: 'solid', - fgColor: {argb: 'FFFF0000'} + type: 'pattern', + pattern: 'darkVertical', + fgColor: { argb: 'FFFF0000' } }; -workbook.xlsx.readFile("/home/john/bla.xlsx") - .then((): void => { - // use workbook - }); +worksheet.getCell('A2').fill = { + type: 'pattern', + pattern: 'darkTrellis', + fgColor: { argb: 'FFFFFF00' }, + bgColor: { argb: 'FF0000FF' } +}; -workbook.xlsx.writeFile("/home/john/bla.xlsx") - .then((): void => { - // done - }); +worksheet.getCell('A3').fill = { + type: 'gradient', + gradient: 'angle', + degree: 0, + stops: [ + { position: 0, color: { argb: 'FF0000FF' } }, + { position: 0.5, color: { argb: 'FFFFFFFF' } }, + { position: 1, color: { argb: 'FF0000FF' } } + ] +}; + +worksheet.getCell('A2').fill = { + type: 'gradient', + gradient: 'path', + center: { left: 0.5, top: 0.5 }, + stops: [ + { position: 0, color: { argb: 'FFFF0000' } }, + { position: 1, color: { argb: 'FF00FF00' } } + ] +}; + +worksheet.getCell('A1').value = { + richText: [ + { + text: 'This is ', + font: { name: 'Calibri', size: 12, color: { theme: 0 }, family: 2, scheme: 'minor' }, + }, + { + text: 'a', + font: { italic: true, size: 12, color: { theme: 0 }, name: 'Calibri', scheme: 'minor' }, + }, + { + text: ' ', + font: { size: 12, color: { theme: 1 }, name: 'Calibri', family: 2, scheme: 'minor' }, + }, + { + text: 'colorful', + font: { size: 12, color: { argb: 'FFFF6600' }, name: 'Calibri', scheme: 'minor' }, + }, + { + text: ' text ', + font: { size: 12, color: { theme: 1 }, name: 'Calibri', family: 2, scheme: 'minor' }, + }, + { + text: 'with', + font: { size: 12, color: { argb: 'FFCCFFCC' }, name: 'Calibri', scheme: 'minor' }, + }, + { + text: ' in-cell ', + font: { size: 12, color: { theme: 1 }, name: 'Calibri', family: 2, scheme: 'minor' }, + }, + { + text: 'format', + font: { bold: true, size: 12, name: 'Calibri', family: 2, scheme: 'minor' }, + } + ] +}; + +worksheet.columns = [ + { header: 'Id', key: 'id', width: 10 }, + { header: 'Name', key: 'name', width: 32 }, + { header: 'D.O.B.', key: 'DOB', width: 10, outlineLevel: 1 } +]; +worksheet.getColumn(3).outlineLevel = 1; +worksheet.getRow(3).outlineLevel = 1; +worksheet.properties.outlineLevelCol = 1; +worksheet.properties.outlineLevelRow = 1; + +const imageId1 = workbook.addImage({ + filename: 'path/to/image.jpg', + extension: 'jpeg', +}); +const imageId2 = workbook.addImage({ + buffer: fs.readFileSync('path/to.image.png'), + extension: 'png', +}); + +worksheet.addBackgroundImage(imageId1); +worksheet.addImage(imageId2, 'B2:D6'); +worksheet.addImage(imageId2, { + tl: { col: 1.5, row: 1.5 }, + br: { col: 3.5, row: 5.5 } +}); + +workbook.xlsx.readFile('./1.xlsx').then(() => null); +(new stream.Stream()).pipe(workbook.xlsx.createInputStream()); + +workbook.xlsx.writeFile('./1.xlsx').then(() => null); +workbook.xlsx.write(new stream.Stream()).then(() => null); + +workbook.csv.readFile('./1.xlsx').then(worksheet => null); +workbook.csv.read(new stream.Stream()).then(worksheet => null); + +(new stream.Stream()).pipe(workbook.csv.createInputStream()); + +const options1 = { dateFormats: ['DD/MM/YYYY'] }; +workbook.csv.readFile('./1.xlsx', options1).then(worksheet => null); + +const options2 = { + map(value: any, index: number) { + switch (index) { + case 0: + // column 1 is string + return value; + case 1: + // column 2 is a date + return new Date(value); + case 2: + // column 3 is JSON of a formula value + return JSON.parse(value); + default: + // the rest are numbers + return parseFloat(value); + } + } +}; +workbook.csv.readFile('./1.xlsx', options2).then(worksheet => null); + +workbook.csv.writeFile('./1.xlsx').then(() => null); + +// write to a stream +workbook.csv.write(new stream.Stream()).then(() => null); + +const workbookWriter = new Excel.stream.xlsx.WorkbookWriter({ + filename: './streamed-workbook.xlsx', + useStyles: true, + useSharedStrings: true +}); + +worksheet.addRow({ + id: 'i', + name: 'theName', + etc: 'someOtherDetail' +}).commit(); + +worksheet.mergeCells('A1:B2'); +worksheet.getCell('A1').value = 'I am merged'; +worksheet.getCell('C1').value = 'I am not'; +worksheet.getCell('C2').value = 'Neither am I'; +worksheet.getRow(2).commit(); + +worksheet.commit(); + +workbook.commit().then(() => null); + +Excel.config.setValue('promise', Bluebird); diff --git a/types/exceljs/index.d.ts b/types/exceljs/index.d.ts index 259eaccae1..be82b7d7f9 100644 --- a/types/exceljs/index.d.ts +++ b/types/exceljs/index.d.ts @@ -1,7 +1,353 @@ -// Type definitions for exceljs 0.4 +// Type definitions for exceljs 0.5 // Project: https://github.com/guyonroche/exceljs // Definitions by: Rogier Schouten +// Ali Taheri Moghaddar // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 + +/// + +import { Writable, Stream } from 'stream'; + +export const enum RelationshipType { + None = 0, + OfficeDocument = 1, + Worksheet = 2, + CalcChain = 3, + SharedStrings = 4, + Styles = 5, + Theme = 6, + Hyperlink = 7 +} + +export const enum DocumentType { + Xlsx = 1 +} + +export const enum PaperSize { + Legal = 5, + Executive = 7, + A4 = 9, + A5 = 11, + B5 = 13, + Envelope_10 = 20, + Envelope_DL = 27, + Envelope_C5 = 28, + Envelope_B5 = 34, + Envelope_Monarch = 37, + Double_Japan_Postcard_Rotated = 82, + K16_197x273_mm = 119, +} + +export interface WorksheetViewCommon { + /** + * Sets the worksheet view's orientation to right-to-left, `false` by default + */ + rightToLeft?: boolean; + + /** + * The currently selected cell + */ + activeCell?: string; + + /** + * Shows or hides the ruler in Page Layout, `true` by default + */ + showRuler?: boolean; + + /** + * Shows or hides the row and column headers (e.g. A1, B1 at the top and 1,2,3 on the left, + * `true` by default + */ + showRowColHeaders?: boolean; + + /** + * Shows or hides the gridlines (shown for cells where borders have not been defined), + * `true` by default + */ + showGridLines?: boolean; + + /** + * Percentage zoom to use for the view, `100` by default + */ + zoomScale?: number; + + /** + * Normal zoom for the view, `100` by default + */ + zoomScaleNormal?: number; +} + +export interface WorksheetViewNormal { + /** + * Controls the view state + */ + state?: 'normal'; + + /** + * Presentation style + */ + style?: 'pageBreakPreview' | 'pageLayout'; +} + +export interface WorksheetViewFrozen { + /** + * Where a number of rows and columns to the top and left are frozen in place. + * Only the bottom left section will scroll + */ + state: 'frozen'; + + /** + * Presentation style + */ + style?: 'pageBreakPreview'; + + /** + * How many columns to freeze. To freeze rows only, set this to 0 or undefined + */ + xSplit?: number; + + /** + * How many rows to freeze. To freeze columns only, set this to 0 or undefined + */ + ySplit?: number; + + /** + * Which cell will be top-left in the bottom-right pane. Note: cannot be a frozen cell. + * Defaults to first unfrozen cell + */ + topLeftCell?: string; +} + +export interface WorksheetViewSplit { + /** + * Where the view is split into 4 sections, each semi-independently scrollable. + */ + state: 'split'; + + /** + * Presentation style + */ + style?: 'pageBreakPreview' | 'pageLayout'; + + /** + * How many points from the left to place the splitter. + * To split vertically, set this to 0 or undefined + */ + xSplit?: number; + + /** + * How many points from the top to place the splitter. + * To split horizontally, set this to 0 or undefined + */ + ySplit?: number; + + /** + * Which cell will be top-left in the bottom-right pane + */ + topLeftCell?: string; + + /** + * Which pane will be active + */ + activePane?: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'; +} + +export type WorksheetView = + & WorksheetViewCommon + & (WorksheetViewNormal | WorksheetViewFrozen | WorksheetViewSplit); + +export interface WorkbookView { + x?: number; + y?: number; + width?: number; + height?: number; + firstSheet?: number; + activeTab?: number; + visibility?: string; +} + +export type FillPatterns = + | 'none' | 'solid' + | 'darkVertical' | 'darkHorizontal' | 'darkGrid' | 'darkTrellis' | 'darkDown' | 'darkUp' + | 'lightVertical' | 'lightHorizontal' | 'lightGrid' | 'lightTrellis' | 'lightDown' | 'lightUp' + | 'darkGray' | 'mediumGray' | 'lightGray' | 'gray125' | 'gray0625'; + +export interface FillPattern { + type: 'pattern'; + pattern: FillPatterns; + fgColor?: Color; + bgColor?: Color; +} + +export interface GradientStop { + position: number; + color: Color; +} + +export interface FillGradientAngle { + type: 'gradient'; + gradient: 'angle'; + + /** + * For 'angle' gradient, specifies the direction of the gradient. 0 is from the left to the right. + * Values from 1 - 359 rotates the direction clockwise + */ + degree: number; + + /** + * Specifies the gradient colour sequence. Is an array of objects containing position and + * color starting with position 0 and ending with position 1. + * Intermediary positions may be used to specify other colours on the path. + */ + stops: GradientStop[]; +} + +export interface FillGradientPath { + type: 'gradient'; + gradient: 'path'; + + /** + * For 'path' gradient. Specifies the relative coordinates for the start of the path. + * 'left' and 'top' values range from 0 to 1 + */ + center: { left: number; top: number }; + + /** + * Specifies the gradient colour sequence. Is an array of objects containing position and + * color starting with position 0 and ending with position 1. + * Intermediary positions may be used to specify other colours on the path. + */ + stops: GradientStop[]; +} + +export type Fill = FillPattern | FillGradientAngle | FillGradientPath; + +export interface Font { + name?: string; + size?: number; + family?: number; + scheme?: 'minor' | 'major' | 'none'; + charset?: number; + color?: Color; + bold?: boolean; + italic?: boolean; + underline?: boolean | 'none' | 'single' | 'double' | 'singleAccounting' | 'doubleAccounting'; + strike?: boolean; + outline?: boolean; +} + +export type BorderStyle = + | 'thin' | 'dotted' | 'hair' | 'medium' | 'double' | 'thick' | 'dashDot' + | 'dashDotDot' | 'slantDashDot' | 'mediumDashed' | 'mediumDashDotDot' | 'mediumDashDot'; + +export interface Color { + /** + * Hex string for alpha-red-green-blue e.g. FF00FF00 + */ + argb?: string; + + /** + * Choose a theme by index + */ + theme?: number; +} + +export interface Border { + style?: BorderStyle; + color?: Color; +} + +export interface BorderDiagonal extends Border { + up?: boolean; + down?: boolean; +} + +export interface Borders { + top?: Border; + left?: Border; + bottom?: Border; + right?: Border; + diagonal?: BorderDiagonal; +} + +export interface Margins { + top?: number; + left?: number; + bottom?: number; + right?: number; + header?: number; + footer?: number; +} + +export const enum ReadingOrder { + LeftToRight = 1, + RightToLeft = 2, +} + +export interface Alignment { + horizontal?: 'left' | 'center' | 'right' | 'fill' | 'justify' | 'centerContinuous' | 'distributed'; + vertical?: 'top' | 'middle' | 'bottom' | 'distributed' | 'justify'; + wrapText?: boolean; + indent?: number; + readingOrder?: 'rtl' | 'ltr'; + textRotation?: number | 'vertical'; +} + +export interface Style { + numFmt?: string; + font?: Font; + alignment?: Alignment; + border?: Borders; + fill?: Fill; +} + +export type DataValidationOperator = + | 'between' | 'notBetween' | 'equal' | 'notEqual' | 'greaterThan' | 'lessThan' + | 'greaterThanOrEqual' | 'lessThanOrEqual'; + +export interface DataValidation { + type: 'list' | 'whole' | 'decimal' | 'date' | 'textLength' | 'custom'; + formulae: any[]; + allowBlank?: boolean; + operator?: DataValidationOperator; + error?: string; + errorTitle?: string; + errorStyle?: string; + prompt?: string; + promptTitle?: string; + showErrorMessage?: boolean; + showInputMessage?: boolean; +} + +export interface CellErrorValue { + error: '#N/A' | '#REF!' | '#NAME?' | '#DIV/0!' | '#NULL!' | '#VALUE!' | '#NUM!'; +} + +export interface RichText { + text: string; + font?: Font; +} + +export interface CellRichTextValue { + richText: RichText[]; +} + +export interface CellHyperlinkValue { + text: string; + hyperlink: string; +} + +export interface CellFormulaValue { + formula: string; + result: number | string | Date; +} + +export interface CellSharedFormulaValue { + sharedFormula: string; + readonly formula?: string; + result: number | string | Date; +} export const enum ValueType { Null = 0, @@ -23,170 +369,449 @@ export const enum FormulaType { Shared = 2 } -export const enum RelationshipType { - None = 0, - OfficeDocument = 1, - Worksheet = 2, - CalcChain = 3, - SharedStrings = 4, - Styles = 5, - Theme = 6, - Hyperlink = 7 -} +export type CellValue = + | null | number | string | boolean | Date + | CellErrorValue + | CellRichTextValue | CellHyperlinkValue + | CellFormulaValue | CellSharedFormulaValue; -export const enum DocumentType { - Xlsx = 1 -} - -export const enum ReadingOrder { - RightToLeft = 1, - LeftToRight = 2, -} - -export interface ErrorValue { - error: "#N/A" | "#REF!" | "#NAME?" | "#DIV/0!" | "#NULL!" | "#VALUE!" | "#NUM!"; -} - -export interface WorksheetView { - state?: "normal" | "split" | "frozen"; - showRuler?: boolean; - showRowColHeaders?: boolean; - showGridLines?: boolean; - zoomScale?: number; - zoomScaleNormal?: number; - style?: "pageBreakPreview" | "pageLayout"; -} - -export interface WorkbookView { - x?: number; - y?: number; - width?: number; - height?: number; - firstSheet?: number; - activeTab?: number; - visibility?: string; -} - -export interface Fill { - type: "pattern" | "gradient"; - pattern?: "none" | "solid"; - fgColor?: Color; - bgColor?: Color; -} - -export interface Font { - name: string; - size?: number; - family?: number; - schema?: "minor" | "major" | "none"; - charset?: number; - color?: Color; - bold?: boolean; - italic?: boolean; - underline?: true | false | "none" | "single" | "double" | "singleAccounting" | "doubleAccounting"; - strike?: boolean; - outline?: boolean; -} - -export type BorderStyle = - "thin" | "dotted" | "dashDot" | "hair" | "dashDotDot" | "slantDashDot" | "mediumDashed" | "mediumDashDotDot" | - "mediumDashDot" | "medium" | "double" | "thick"; - -export interface Color { +export interface Cell extends Style { /** - * Hex string for alpha-red-green-blue e.g. FF00FF00 + * Assign (or get) a name for a cell (will overwrite any other names that cell had) */ - argb: string; -} + name?: string; -export interface Border { - style?: BorderStyle; - color?: Color; -} - -export interface Borders { - top?: Border; - left?: Border; - bottom?: Border; - right?: Border; -} - -export interface Alignment { - horizontal?: "left" | "center" | "right" | "fill" | "justify" | "centerContinuous" | "distributed"; - vertical?: "top" | "middle" | "bottom" | "distributed" | "justify"; - wrapText?: boolean; - indent?: number; - readingOrder?: "rtl" | "ltr"; - textrotation?: number | "vertical"; -} - -export interface Cell { - border?: Borders; - font?: Font; - alignment?: Alignment; - numFmt?: string; - value?: any; - fill?: Fill; -} - -export interface Row { /** - * The row number + * Assign (or get) an array of names for a cell (cells can have more than one name) + */ + names?: string[]; + + /** + * Cells can define what values are valid or not and provide + * prompting to the user to help guide them. + */ + dataValidation?: DataValidation; + + /** + * Value of the cell + */ + value: CellValue; + + /** + * convenience getter to access the formula + */ + readonly formula: string; + + /** + * convenience getter to access the formula result + */ + readonly result: number | string | Date; + + /** + * The type of the cell's value + */ + readonly type: ValueType; + + /** + * The type of the cell's formula + */ + readonly formulaType: FormulaType; + + /** + * The master cell when the current cell is a merge cell + */ + master?: Cell; + + /** + * The styles of the cell + */ + style: Style; + + /** + * Remove a name from a cell + */ + removeName(name: string): void; +} + +export interface Row extends Style { + /** + * Set a specific row height */ - number: number; height: number; + + /** + * Make row hidden + */ hidden: boolean; - collapsed: boolean; - values: any[]; + + /** + * Get a row as a sparse array + */ + values: any[] | any; + + /** + * Set an outline level for rows + */ outlineLevel?: number; + /** + * Indicate the collapsed state based on outlineLevel + */ + readonly collapsed: boolean; + + /** + * Number of non-empty cells + */ readonly cellCount: number; + + /** + * Number of cells including empty ones + */ readonly actualCellCount: number; - getCell(index: number): Cell; + /** + * Get cell by number, column letter or column key + */ + getCell(indexOrKey: number | string): Cell; + /** + * Iterate over all non-null cells in a row + */ eachCell(callback: (cell: Cell, colNumber: number) => void): void; - eachCell(opts: { includeEmpty: boolean}, callback: (cell: Cell, colNumber: number) => void): void; + + /** + * Iterate over all cells in a row (including empty cells) + */ + eachCell(opt: { includeEmpty: boolean }, callback: (cell: Cell, colNumber: number) => void): void; + + /** + * Cut one or more cells (cells to the right are shifted left) + * + * Note: this operation will not affect other rows + */ + splice(start: number, count: number, ...insert: any[]): void; + + /** + * Commit a completed row to stream + */ + commit(): void; } export interface Column { + /** + * Can be a string to set one row high header or an array to set multi-row high header + */ + header: string | string[]; + + /** + * The name of the properties associated with this column in each row + */ + key: string; + + /** + * The width of the column + */ width: number; + + /** + * Set an outline level for columns + */ + outlineLevel?: number; + + /** + * Hides the column + */ + hidden?: boolean; + + /** + * Styles applied to the column + */ + style?: Style; +} + +export interface ColumnExtension extends Style { + /** + * indicate the collapsed state based on outlineLevel + */ + readonly collapsed: boolean; + + /** + * Iterate over all current cells in this column + */ + eachCell(callback: (cell: Cell, rowNumber: number) => void): void; + + /** + * Iterate over all current cells in this column including empty cells + */ + eachCell(opt: { includeEmpty: boolean }, callback: (cell: Cell, rowNumber: number) => void): void; +} + +export interface PageSetup { + /** + * Whitespace on the borders of the page. Units are inches. + */ + margins?: Margins; + + /** + * Orientation of the page - i.e. taller (`'portrait'`) or wider (`'landscape'`). + * + * `'portrait'` by default + */ + orientation?: 'portrait' | 'landscape'; + + /** + * Horizontal Dots per Inch. Default value is 4294967295 + */ + horizontalDpi?: number; + + /** + * Vertical Dots per Inch. Default value is 4294967295 + */ + verticalDpi?: number; + + /** + * Whether to use fitToWidth and fitToHeight or scale settings. + * + * Default is based on presence of these settings in the pageSetup object - if both are present, + * scale wins (i.e. default will be false) + */ + fitToPage?: boolean; + + /** + * How many pages wide the sheet should print on to. Active when fitToPage is true + * + * Default is 1 + */ + fitToWidth?: number; + + /** + * How many pages high the sheet should print on to. Active when fitToPage is true + * + * Default is 1 + */ + fitToHeight?: number; + + /** + * Percentage value to increase or reduce the size of the print. Active when fitToPage is false + * + * Default is 100 + */ + scale?: number; + + /** + * Which order to print the pages. + * + * Default is `downThenOver` + */ + pageOrder?: 'downThenOver' | 'overThenDown'; + + /** + * Print without colour + * + * false by default + */ + blackAndWhite?: boolean; + + /** + * Print with less quality (and ink) + * + * false by default + */ + draft?: boolean; + + /** + * Where to place comments + * + * Default is `None` + */ + cellComments?: 'atEnd' | 'asDisplayed' | 'None'; + + /** + * Where to show errors + * + * Default is `displayed` + */ + errors?: 'dash' | 'blank' | 'NA' | 'displayed'; + + /** + * What paper size to use (see below) + * + * | Name | Value | + * | ----------------------------- | --------- | + * | Letter | `undefined` | + * | Legal | `5` | + * | Executive | `7` | + * | A4 | `9` | + * | A5 | `11` | + * | B5 (JIS) | `13` | + * | Envelope #10 | `20` | + * | Envelope DL | `27` | + * | Envelope C5 | `28` | + * | Envelope B5 | `34` | + * | Envelope Monarch | `37` | + * | Double Japan Postcard Rotated | `82` | + * | 16K 197x273 mm | `119` | + */ + paperSize?: PaperSize; + + /** + * Whether to show the row numbers and column letters, `false` by default + */ + showRowColHeaders?: boolean; + + /** + * Whether to show grid lines, `false` by default + */ + showGridLines?: boolean; + + /** + * Which number to use for the first page + */ + firstPageNumber?: number; + + /** + * Whether to center the sheet data horizontally, `false` by default + */ + horizontalCentered?: boolean; + + /** + * Whether to center the sheet data vertically, `false` by default + */ + verticalCentered?: boolean; + + /** + * Set Print Area for a sheet, e.g. `'A1:G20'` + */ + printArea?: string; + + /** + * Repeat specific rows on every printed page, e.g. `'1:3'` + */ + printTitlesRow?: string; +} + +export type AutoFilter = string | { + from: string | { row: number; column: number }; + to: string | { row: number; column: number }; +}; + +export interface Image { + extension: 'jpeg' | 'png' | 'gif'; + filename?: string; + buffer?: Buffer; +} + +export interface ImageRange { + tl: { col: number; row: number }; + br: { col: number; row: number }; } export interface Worksheet { + /** + * Contains information related to how a worksheet is printed + */ + pageSetup: PageSetup; + + /** + * Worksheet Properties + */ + properties: WorksheetProperties; + + /** + * Open panes representing the sheet + */ + views: WorksheetView[]; + + /** + * Apply an auto filter to your worksheet. + */ + autoFilter?: AutoFilter; + + /** + * Add column headers and define column keys and widths. + * + * Note: these column structures are a workbook-building convenience only, + * apart from the column width, they will not be fully persisted. + */ + columns?: Column[]; + /** * The total row size of the document. Equal to the row number of the last row that has values. */ - rowCount: number; + readonly rowCount: number; + /** * A count of the number of rows that have values. If a mid-document row is empty, it will not be included in the count. */ - actualRowCount: number; + readonly actualRowCount: number; + /** * The total column size of the document. Equal to the maximum cell count from all of the rows */ - columnCount: number; + readonly columnCount: number; + /** * A count of the number of columns that have values. */ - actualColumnCount: number; + readonly actualColumnCount: number; /** - * Get or create a column + * Get the last editable row in a worksheet (or undefined if there are none) */ - getColumn(index: number): Column; + readonly lastRow: Row | undefined; /** - * Create a new row + * Access an individual columns by key, letter and 1-based column number */ - addRow(data: any[]): Row; + getColumn(indexOrKey: number | string): Column & ColumnExtension; + + /** + * Cut one or more columns (columns to the right are shifted left) + * and optionally insert more + * + * If column properties have been definde, they will be cut or moved accordingly + * + * Known Issue: If a splice causes any merged cells to move, the results may be unpredictable + * + * Also: If the worksheet has more rows than values in the colulmn inserts, + * the rows will still be shifted as if the values existed + */ + spliceColumns(start: number, count: number, ...insert: any[][]): void; + + /** + * Cut one or more rows (rows below are shifted up) + * and optionally insert more + * + * Known Issue: If a splice causes any merged cells to move, the results may be unpredictable + */ + spliceRows(start: number, count: number, ...insert: any[][]): void; + + /** + * Add a couple of Rows by key-value, after the last current row, using the column keys, + * or add a row by contiguous Array (assign to columns A, B & C) + */ + addRow(data: any[] | any): Row; + + /** + * Add multiple rows by providing an array of arrays or key-value pairs + */ + addRows(rows: any[]): void; /** * Get or create row by 0-based index */ getRow(index: number): Row; + + /** + * Iterate over all rows that have values in a worksheet + */ eachRow(callback: (row: Row, rowNumber: number) => void): void; + /** + * Iterate over all rows (including empty rows) in a worksheet + */ + eachRow(opt: { includeEmpty: boolean }, callback: (row: Row, rowNumber: number) => void): void; + /** * Get or create cell */ @@ -194,38 +819,222 @@ export interface Worksheet { /** * Merge cells, either: - * tlbr string - * tl string, br string - * t, l, b, r numbers + * + * tlbr string, e.g. `'A4:B5'` + * + * tl string, br string, e.g. `'G10', 'H11'` + * + * t, l, b, r numbers, e.g. `10,11,12,13` */ mergeCells(a: number | string, b?: number | string, c?: number, d?: number): void; + + /** + * unmerging the cells breaks the style links + */ + unMergeCells(cell: string): void; + + /** + * Using the image id from `Workbook.addImage`, set the background to the worksheet + */ + addBackgroundImage(imageId: string): void; + + /** + * Using the image id from `Workbook.addImage`, + * embed an image within the worksheet to cover a range + */ + addImage(imageId: string, range: string | ImageRange): void; + + /** + * Commit a completed worksheet to stream + */ + commit(): void; +} + +export interface WorksheetProperties { + /** + * Color of the tab + */ + tabColor?: Color; + + /** + * The worksheet column outline level (default: 0) + */ + outlineLevelCol?: number; + + /** + * The worksheet row outline level (default: 0) + */ + outlineLevelRow?: number; + + /** + * Default row height (default: 15) + */ + defaultRowHeight?: number; + + /** + * default: 55 + */ + dyDescent?: number; +} + +export interface AddWorksheetOptions { + properties?: WorksheetProperties; + pageSetup?: PageSetup; + views?: WorksheetView[]; +} + +export interface WorkbookProperties { + /** + * Set workbook dates to 1904 date system + */ + date1904: boolean; } export interface Xlsx { + /** + * read from a file + */ readFile(path: string): Promise; + + /** + * Create input stream for reading + */ + createInputStream(): Writable; + + /** + * write to a file + */ writeFile(path: string): Promise; + + /** + * write to a stream + */ + write(stream: Stream): Promise; } -export interface WorksheetProps { - showGridLines: boolean; +export interface CsvReadOptions { + dateFormats?: string[]; + map?(value: any, index: number): any; +} + +export interface CsvWriteOptions { + dateFormat?: string; +} + +export interface Csv { + /** + * read from a file + */ + readFile(path: string, options?: CsvReadOptions): Promise; + + /** + * read from a stream + */ + read(stream: Stream, options?: CsvReadOptions): Promise; + + /** + * Create input stream for reading + */ + createInputStream(): Writable; + + /** + * write to a file + */ + writeFile(path: string, options?: CsvWriteOptions): Promise; + + /** + * write to a stream + */ + write(stream: Stream, options?: CsvWriteOptions): Promise; } export class Workbook { - constructor(); - creator: string; lastModifiedBy: string; created: Date; modified: Date; lastPrinted: Date; - properties: { - date1904: boolean; - }; + properties: WorkbookProperties; + + /** + * xlsx file format operations + */ + readonly xlsx: Xlsx; + + /** + * csv file format operations + */ + readonly csv: Csv; + + /** + * The Workbook views controls how many separate windows Excel will open when viewing the workbook. + */ views: WorkbookView[]; - addWorksheet(name: string, opts?: { properties?: WorksheetProps, views?: WorksheetView[] }): Worksheet; + /** + * The list of worksheets added to this workbook + */ + worksheets: Worksheet[]; + /** + * Add a new worksheet and return a reference to it + */ + addWorksheet(name: string, options?: AddWorksheetOptions): Worksheet; + + /** + * fetch sheet by name or id + */ getWorksheet(indexOrName: number | string): Worksheet; - xlsx: Xlsx; + /** + * Iterate over all sheets. + * + * Note: `workbook.worksheets.forEach` will still work but this is better. + */ + eachSheet(callback: (worksheet: Worksheet, id: number) => void): void; + + /** + * Add Image to Workbook and return the id + */ + addImage(img: Image): string; + + /** + * Commit a completed workbook to stream and close the stream + */ + commit(): Promise; +} + +export namespace config { + function setValue(key: 'promise', promise: any): void; +} + +export namespace stream { + namespace xlsx { + interface WorkbookWriterOptions { + /** + * Specifies a writable stream to write the XLSX workbook to. + */ + stream?: Stream; + + /** + * If stream not specified, this field specifies the path to a file to write the XLSX workbook to. + */ + filename?: string; + + /** + * Specifies whether to use shared strings in the workbook. Default is false + */ + useSharedStrings?: boolean; + + /** + * Specifies whether to add style information to the workbook. + * Styles can add some performance overhead. Default is false + */ + useStyles?: boolean; + } + + class WorkbookWriter extends Workbook { + constructor(options: WorkbookWriterOptions); + } + } }