how to set the automatic width? #1473

Open
opened 2019-04-06 11:59:48 +00:00 by cjlhll · 19 comments
cjlhll commented 2019-04-06 11:59:48 +00:00 (Migrated from github.com)

Hello, use it for the first time. I don't know how to set the col width. The content is hidden

Hello, use it for the first time. I don't know how to set the col width. The content is hidden
LuisEnMarroquin commented 2019-05-01 01:11:57 +00:00 (Migrated from github.com)

You can set the width of a column doing this

let newSheet = utils.json_to_sheet(yourData)
newSheet['!cols'].push({ width: 20 })

I created a library that does it automatically and uses this xlsx library, check out json-as-xlsx

You can set the width of a column doing this ```js let newSheet = utils.json_to_sheet(yourData) newSheet['!cols'].push({ width: 20 }) ``` I created a library that does it automatically and uses this xlsx library, check out [json-as-xlsx](https://www.npmjs.com/package/json-as-xlsx)
SheetJSDev commented 2019-05-01 01:32:11 +00:00 (Migrated from github.com)

The file formats do not have an option to recalculate width on read. You can use VBA for it by calling the sheet .Range("A:Z").EntireColumn.AutoFit OR you can set the wch property of the specific column array object to the desired number of characters as explained in the README

PS: our Pro Basic build solves the problem by iterating through the cells in each row, deducing Excel's calculated width based on the Maximum Digit Width algorithm, then taking the largest width

The file formats do not have an option to recalculate width on read. You can use VBA for it by calling the sheet `.Range("A:Z").EntireColumn.AutoFit` OR you can set the `wch` property of the specific column array object to the desired number of characters [as explained in the README](https://github.com/SheetJS/js-xlsx#column-properties) PS: our Pro Basic build solves the problem by iterating through the cells in each row, deducing Excel's calculated width based on the Maximum Digit Width algorithm, then taking the largest width
chenlitchian commented 2019-08-23 04:47:56 +00:00 (Migrated from github.com)

@cjlhll

Please see my solution

  1. get maximum width from the json data
  2. set column width

let objectMaxLength = []; 
    for (let i = 0; i < json.length; i++) {
      let value = <any>Object.values(json[i]);
      for (let j = 0; j < value.length; j++) {
        if (typeof value[j] == "number") {
          objectMaxLength[j] = 10;
        } else {
          objectMaxLength[j] =
            objectMaxLength[j] >= value[j].length
              ? objectMaxLength[j]
              : value[j].length;
        }
      }
    }
    console.log(objectMaxLength);

    var wscols = [
      { width: objectMaxLength[0] },  // first column
      { width: objectMaxLength[1] }, // second column
      { width: objectMaxLength[2] }, //...
      { width: objectMaxLength[3] }, 
      { width: objectMaxLength[4] },
      { width: objectMaxLength[5] }, 
      { width: objectMaxLength[6] }, 
      { width: objectMaxLength[7] }, 
      { width: objectMaxLength[8] },
      { width: objectMaxLength[9] }
    ];

    const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json);
    worksheet["!cols"] = wscols;

@cjlhll Please see my solution 1. get maximum width from the json data 2. set column width ``` let objectMaxLength = []; for (let i = 0; i < json.length; i++) { let value = <any>Object.values(json[i]); for (let j = 0; j < value.length; j++) { if (typeof value[j] == "number") { objectMaxLength[j] = 10; } else { objectMaxLength[j] = objectMaxLength[j] >= value[j].length ? objectMaxLength[j] : value[j].length; } } } console.log(objectMaxLength); var wscols = [ { width: objectMaxLength[0] }, // first column { width: objectMaxLength[1] }, // second column { width: objectMaxLength[2] }, //... { width: objectMaxLength[3] }, { width: objectMaxLength[4] }, { width: objectMaxLength[5] }, { width: objectMaxLength[6] }, { width: objectMaxLength[7] }, { width: objectMaxLength[8] }, { width: objectMaxLength[9] } ]; const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json); worksheet["!cols"] = wscols; ```
dave-wind commented 2019-09-26 07:38:03 +00:00 (Migrated from github.com)
you can try see it https://dave-wind.github.io/excel/index.html
iamandreadompe commented 2020-01-09 17:44:20 +00:00 (Migrated from github.com)

@chenlitchian i've edited your code for complete autofit with values (your versione) and headers

		for (let i = 0; i < json.length; i++) {
		let value = <any>Object.values(json[i]);
		for (let j = 0; j < value.length; j++) {
			if (typeof value[j] == "number") {
				objectMaxLength[j] = 10;
			} else {
				objectMaxLength[j] =
					objectMaxLength[j] >= value[j].length
						? objectMaxLength[j]
						: value[j].length;
			}
		}
		let key = <any>Object.keys(json[i]);
		for (let j = 0; j < key.length; j++) {
			objectMaxLength[j] =
				objectMaxLength[j] >= key[j].length
					? objectMaxLength[j]
					: key[j].length;
		}
	}
@chenlitchian i've edited your code for complete autofit with values (your versione) and headers for (let i = 0; i < json.length; i++) { let value = <any>Object.values(json[i]); for (let j = 0; j < value.length; j++) { if (typeof value[j] == "number") { objectMaxLength[j] = 10; } else { objectMaxLength[j] = objectMaxLength[j] >= value[j].length ? objectMaxLength[j] : value[j].length; } } let key = <any>Object.keys(json[i]); for (let j = 0; j < key.length; j++) { objectMaxLength[j] = objectMaxLength[j] >= key[j].length ? objectMaxLength[j] : key[j].length; } }
andreElrico commented 2020-01-31 09:07:58 +00:00 (Migrated from github.com)

Creds: @chenlitchian + @dave-wind

This is esp useful when you are user header option that will change the order of your cols. (Please note this is typescript. Just remove the types from fun-args).

I ended up using:

  private autofitColumns(json: any[], worksheet: XLSX.WorkSheet, header?: string[]) {

    const jsonKeys = header ? header : Object.keys(json[0]);

    let objectMaxLength = []; 
    for (let i = 0; i < json.length; i++) {
      let value = json[i];
      for (let j = 0; j < jsonKeys.length; j++) {
        if (typeof value[jsonKeys[j]] == "number") {
          objectMaxLength[j] = 10;
        } else {

          const l = value[jsonKeys[j]] ? value[jsonKeys[j]].length : 0;

          objectMaxLength[j] =
            objectMaxLength[j] >= l
              ? objectMaxLength[j]
              : l;
        }
      }

      let key = jsonKeys;
      for (let j = 0; j < key.length; j++) {
        objectMaxLength[j] =
          objectMaxLength[j] >= key[j].length
            ? objectMaxLength[j]
            : key[j].length;
      }
    }

    const wscols = objectMaxLength.map(w => { return { width: w} });

    worksheet["!cols"] = wscols;

  }
Creds: @chenlitchian + @dave-wind This is esp useful when you are user `header` option that will change the order of your cols. (Please note this is typescript. Just remove the types from fun-args). I ended up using: ``` private autofitColumns(json: any[], worksheet: XLSX.WorkSheet, header?: string[]) { const jsonKeys = header ? header : Object.keys(json[0]); let objectMaxLength = []; for (let i = 0; i < json.length; i++) { let value = json[i]; for (let j = 0; j < jsonKeys.length; j++) { if (typeof value[jsonKeys[j]] == "number") { objectMaxLength[j] = 10; } else { const l = value[jsonKeys[j]] ? value[jsonKeys[j]].length : 0; objectMaxLength[j] = objectMaxLength[j] >= l ? objectMaxLength[j] : l; } } let key = jsonKeys; for (let j = 0; j < key.length; j++) { objectMaxLength[j] = objectMaxLength[j] >= key[j].length ? objectMaxLength[j] : key[j].length; } } const wscols = objectMaxLength.map(w => { return { width: w} }); worksheet["!cols"] = wscols; } ```
lewiswolf commented 2020-03-05 12:33:57 +00:00 (Migrated from github.com)

All great solutions, thanks. But value.length does not give the same column width as Excel's own auto-fit... is there any way around this? Is there something I'm missing?

function formatExcelCols(json) {
    let widthArr = Object.keys(json[0]).map(key => {
        return { width: key.length + 2 } // plus 2 to account for short object keys
    })
    for (let i = 0; i < json.length; i++) {
        let value = Object.values(json[i]);
        for (let j = 0; j < value.length; j++) {
            if (value[j] !== null && value[j].length > widthArr[j].width) {
                widthArr[j].width = value[j].length;
            }
        }
    }
    return widthArr
}
All great solutions, thanks. But value.length does not give the same column width as Excel's own auto-fit... is there any way around this? Is there something I'm missing? ```javascript function formatExcelCols(json) { let widthArr = Object.keys(json[0]).map(key => { return { width: key.length + 2 } // plus 2 to account for short object keys }) for (let i = 0; i < json.length; i++) { let value = Object.values(json[i]); for (let j = 0; j < value.length; j++) { if (value[j] !== null && value[j].length > widthArr[j].width) { widthArr[j].width = value[j].length; } } } return widthArr } ```
SheetJSDev commented 2020-03-05 14:43:20 +00:00 (Migrated from github.com)

Column widths are nontrivial: https://docs.sheetjs.com/#column-properties (read this first)

As an auto-fit example, try setting A1 to "iiiiiiiiii" (10 lowercase letter i) and setting B1 to "wwwww" (5 lowercase letter w), then adjust the widths. The 10-character string is actually smaller than the 5-character string!

There are a few other complicating factors, like conditional formatting and tables ("action at a distance"), and the auto-filter box.

Column widths are nontrivial: https://docs.sheetjs.com/#column-properties (read this first) As an auto-fit example, try setting A1 to "iiiiiiiiii" (10 lowercase letter i) and setting B1 to "wwwww" (5 lowercase letter w), then adjust the widths. The 10-character string is actually smaller than the 5-character string! There are a few other complicating factors, like conditional formatting and tables ("action at a distance"), and the auto-filter box.
JonArnfred commented 2020-05-11 11:57:04 +00:00 (Migrated from github.com)

One could also use the canvas method measureText() to get the width of a text string in px, see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/measureText.

const text = "something";
const context = document.createElement("canvas").getContext('2d');
context.font = '12px arial';
const width = context.measureText(text).width;
One could also use the canvas method measureText() to get the width of a text string in px, see [https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/measureText](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/measureText). ``` javascript const text = "something"; const context = document.createElement("canvas").getContext('2d'); context.font = '12px arial'; const width = context.measureText(text).width; ```
MohannadNaj commented 2020-06-17 08:04:24 +00:00 (Migrated from github.com)

For anyone whose looking for doing this for Array of Arrays (aoa), I'm sharing how I did it.

Not sure the check for null values is necessary but it was necessary in my case.

let aoa = [['array'], ['of'], ['arrays']]

let worksheet = XLSX.utils.aoa_to_sheet(aoa);

let objectMaxLength = []

aoa.map(arr => {
  Object.keys(arr).map(key => {
    let value = arr[key] === null ? '' : arr[key]

    if (typeof value === 'number')
    {
      return objectMaxLength[key] = 10
    }

    objectMaxLength[key] = objectMaxLength[key] >= value.length ? objectMaxLength[key]  : value.length
  })
})

let worksheetCols = objectMaxLength.map(width => {
  return {
    width
  }
})

worksheet["!cols"] = worksheetCols;

XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);

For anyone whose looking for doing this for **Array of Arrays (aoa)**, I'm sharing how I did it. Not sure the check for `null` values is necessary but it was necessary in my case. ``` js let aoa = [['array'], ['of'], ['arrays']] let worksheet = XLSX.utils.aoa_to_sheet(aoa); let objectMaxLength = [] aoa.map(arr => { Object.keys(arr).map(key => { let value = arr[key] === null ? '' : arr[key] if (typeof value === 'number') { return objectMaxLength[key] = 10 } objectMaxLength[key] = objectMaxLength[key] >= value.length ? objectMaxLength[key] : value.length }) }) let worksheetCols = objectMaxLength.map(width => { return { width } }) worksheet["!cols"] = worksheetCols; XLSX.utils.book_append_sheet(workbook, worksheet, sheetName); ```
SheetJSDev commented 2020-07-21 00:19:17 +00:00 (Migrated from github.com)

@JonArnfred the big issue with measureText is that the system-wide Calibri font (present in the various TTFs that ship with Office) do not have the same metrics as the "Calibri (Body)" that Excel uses by default.

@MohannadNaj within the aoa.map, arr is an array so you can simply forEach:

aoa.forEach(arr => {
  arr.forEach((value, key) => {
    let len = 0;
    switch(typeof value) {
      case "number": len = 10; break;
      case "string": len = value.length; break;
      case "object": if(value instanceof Date) len = 10; break; 
    }
    objectMaxLength[key] = Math.max(objectMaxLength[key], len);
  });
});
@JonArnfred the big issue with `measureText` is that the system-wide Calibri font (present in the various TTFs that ship with Office) do not have the same metrics as the "Calibri (Body)" that Excel uses by default. @MohannadNaj within the `aoa.map`, `arr` is an array so you can simply `forEach`: ```js aoa.forEach(arr => { arr.forEach((value, key) => { let len = 0; switch(typeof value) { case "number": len = 10; break; case "string": len = value.length; break; case "object": if(value instanceof Date) len = 10; break; } objectMaxLength[key] = Math.max(objectMaxLength[key], len); }); }); ```
henridev commented 2020-09-30 11:34:42 +00:00 (Migrated from github.com)

for those just passing a worksheet i found this to be the fasted way to get an autofit (using the lodash range function)

export function autofitColumns(worksheet: WorkSheet) {
  let objectMaxLength: ColInfo[] = [];
  const [startLetter, endLetter] = worksheet['!ref']?.replace(/\d/, '').split(':')!;
  const ranges = range(startLetter.charCodeAt(0), endLetter.charCodeAt(0) + 1);
  ranges.forEach((c) => {
    const cell = String.fromCharCode(c);
    const cellLength = worksheet[`${cell}1`].v.length + 1;
    objectMaxLength.push({ width: cellLength });
  });
  worksheet['!cols'] = objectMaxLength;
}
for those just passing a worksheet i found this to be the fasted way to get an autofit (using the lodash range function) ```typescript export function autofitColumns(worksheet: WorkSheet) { let objectMaxLength: ColInfo[] = []; const [startLetter, endLetter] = worksheet['!ref']?.replace(/\d/, '').split(':')!; const ranges = range(startLetter.charCodeAt(0), endLetter.charCodeAt(0) + 1); ranges.forEach((c) => { const cell = String.fromCharCode(c); const cellLength = worksheet[`${cell}1`].v.length + 1; objectMaxLength.push({ width: cellLength }); }); worksheet['!cols'] = objectMaxLength; } ```
sysoft commented 2020-11-06 02:32:25 +00:00 (Migrated from github.com)

ref: https://github.com/adambisek/string-pixel-width

npm install string-pixel-width

ref: https://github.com/adambisek/string-pixel-width npm install string-pixel-width
lieunttuit commented 2021-03-26 04:57:23 +00:00 (Migrated from github.com)

Creds: @andreElrico , @sysoft
I re-changed for my way and share it in case someone needed it.

npm install string-pixel-width

  const exportData [{name: 'foo', age: 12}, {name: 'bar', age: 12}]
  const time = new Dayjs().format('YYYYMMDDHHmmss')
  const workbook = XLSX.utils.book_new()
  const sheet = XLSX.utils.json_to_sheet(exportData)
  const wscols = _autoFitColumns(exportData, sheet)
  sheet['!cols'] = wscols
  XLSX.utils.book_append_sheet(workbook, sheet, 'Sheet1')
  XLSX.writeFile(workbook, `${time}.xlsx`)


  const _autoFitColumns = (json, worksheet, header) => {
    const jsonKeys = header || Object.keys(json[0])

    const objectMaxLength = []
    jsonKeys.forEach((key) => {
      objectMaxLength.push(
        pixelWidth(key, {
          size: 5,
        })
      )
    })

    json.forEach((data, i) => {
      const value = json[i]
      jsonKeys.forEach((key, j) => {
        const l = value[jsonKeys[j]]
          ? pixelWidth(value[jsonKeys[j]], {
              size: 5,
            })
          : 0
        objectMaxLength[j] = objectMaxLength[j] >= l ? objectMaxLength[j] : l
      })
    })

    return objectMaxLength.map((w) => {
      return { width: w }
    })
  }
Creds: @andreElrico , @sysoft I re-changed for my way and share it in case someone needed it. npm install string-pixel-width ```javascript const exportData [{name: 'foo', age: 12}, {name: 'bar', age: 12}] const time = new Dayjs().format('YYYYMMDDHHmmss') const workbook = XLSX.utils.book_new() const sheet = XLSX.utils.json_to_sheet(exportData) const wscols = _autoFitColumns(exportData, sheet) sheet['!cols'] = wscols XLSX.utils.book_append_sheet(workbook, sheet, 'Sheet1') XLSX.writeFile(workbook, `${time}.xlsx`) const _autoFitColumns = (json, worksheet, header) => { const jsonKeys = header || Object.keys(json[0]) const objectMaxLength = [] jsonKeys.forEach((key) => { objectMaxLength.push( pixelWidth(key, { size: 5, }) ) }) json.forEach((data, i) => { const value = json[i] jsonKeys.forEach((key, j) => { const l = value[jsonKeys[j]] ? pixelWidth(value[jsonKeys[j]], { size: 5, }) : 0 objectMaxLength[j] = objectMaxLength[j] >= l ? objectMaxLength[j] : l }) }) return objectMaxLength.map((w) => { return { width: w } }) } ```
jacanon commented 2021-05-11 04:11:34 +00:00 (Migrated from github.com)

Thanks for the helpful answers! I ended up using this solution:

private autofitColumns(json: any[], worksheet: XLSX.WorkSheet) {

  let objectMaxLength: number[] = [];
  
  json.map(jsonData => {
     Object.entries(jsonData)
           .map(([, v], idx) => {
              let columnValue = v as string
              objectMaxLength[idx] = objectMaxLength[idx] >= columnValue.length ? objectMaxLength[idx] : columnValue.length
           })
  })
  
  const wscols = objectMaxLength.map((w: number) => ({width: w}))
  worksheet["!cols"] = wscols;

}
Thanks for the helpful answers! I ended up using this solution: ``` private autofitColumns(json: any[], worksheet: XLSX.WorkSheet) { let objectMaxLength: number[] = []; json.map(jsonData => { Object.entries(jsonData) .map(([, v], idx) => { let columnValue = v as string objectMaxLength[idx] = objectMaxLength[idx] >= columnValue.length ? objectMaxLength[idx] : columnValue.length }) }) const wscols = objectMaxLength.map((w: number) => ({width: w})) worksheet["!cols"] = wscols; } ```
Mykola-Veryha commented 2021-12-10 01:52:36 +00:00 (Migrated from github.com)

More examples:

var wscols = [
	{wch: 6}, // "characters"
	{wpx: 50}, // "pixels"
	,
	{hidden: true} // hide column
];

/* At 96 PPI, 1 pt = 1 px */
var wsrows = [
	{hpt: 12}, // "points"
	{hpx: 16}, // "pixels"
	,
	{hpx: 24, level:3},
	{hidden: true}, // hide row
	{hidden: false}
];

10ae7c9fec/tests/write.js (L21-L26)

More examples: ``` var wscols = [ {wch: 6}, // "characters" {wpx: 50}, // "pixels" , {hidden: true} // hide column ]; /* At 96 PPI, 1 pt = 1 px */ var wsrows = [ {hpt: 12}, // "points" {hpx: 16}, // "pixels" , {hpx: 24, level:3}, {hidden: true}, // hide row {hidden: false} ]; ``` https://github.com/SheetJS/sheetjs/blob/10ae7c9fec50a5c857f2e61c7b610445c50b4048/tests/write.js#L21-L26
gentunian commented 2022-03-10 02:43:09 +00:00 (Migrated from github.com)

for those just passing a worksheet i found this to be the fasted way to get an autofit (using the lodash range function)

export function autofitColumns(worksheet: WorkSheet) {
  let objectMaxLength: ColInfo[] = [];
  const [startLetter, endLetter] = worksheet['!ref']?.replace(/\d/, '').split(':')!;
  const ranges = range(startLetter.charCodeAt(0), endLetter.charCodeAt(0) + 1);
  ranges.forEach((c) => {
    const cell = String.fromCharCode(c);
    const cellLength = worksheet[`${cell}1`].v.length + 1;
    objectMaxLength.push({ width: cellLength });
  });
  worksheet['!cols'] = objectMaxLength;
}

How is this working? Just incrementing v.length by one?

> for those just passing a worksheet i found this to be the fasted way to get an autofit (using the lodash range function) > > ```ts > export function autofitColumns(worksheet: WorkSheet) { > let objectMaxLength: ColInfo[] = []; > const [startLetter, endLetter] = worksheet['!ref']?.replace(/\d/, '').split(':')!; > const ranges = range(startLetter.charCodeAt(0), endLetter.charCodeAt(0) + 1); > ranges.forEach((c) => { > const cell = String.fromCharCode(c); > const cellLength = worksheet[`${cell}1`].v.length + 1; > objectMaxLength.push({ width: cellLength }); > }); > worksheet['!cols'] = objectMaxLength; > } > ``` How is this working? Just incrementing v.length by one?
SheetJSDev commented 2022-03-10 08:28:02 +00:00 (Migrated from github.com)

File formats don't have a flag to recalculate widths on open, so any "automatic width" approach has to be calculated when generating the spreadsheet file.

The naive method of looking at string lengths is decent on average. For Calibri the degenerate cases involve "i" and "w": 6 "w" is wider than 8 "0" and 8 "0" is wider than 16 "i".

Since column widths are written before the string table, this will require a second workbook scan.

Open questions:

  1. Given that the calculation can be done outside of the library, and there is no implementation advantage to integrating it in the library, should it be added?

  2. If it is added, should this behavior be "opt-in" or "opt-out" (always calculate if no width is specified)?

  3. If the behavior is opt-in, should this be implemented as a column property or relegated to an API function (XLSX.utils.sheet_calc_col_width)?

File formats don't have a flag to recalculate widths on open, so any "automatic width" approach has to be calculated when generating the spreadsheet file. The naive method of looking at string lengths is decent on average. For Calibri the degenerate cases involve "i" and "w": 6 "w" is wider than 8 "0" and 8 "0" is wider than 16 "i". Since column widths are written before the string table, this will require a second workbook scan. Open questions: 0) Given that the calculation can be done outside of the library, and there is no implementation advantage to integrating it in the library, should it be added? 1) If it is added, should this behavior be "opt-in" or "opt-out" (always calculate if no width is specified)? 2) If the behavior is opt-in, should this be implemented as a column property or relegated to an API function (`XLSX.utils.sheet_calc_col_width`)?
dhanielsales commented 2022-07-26 18:04:25 +00:00 (Migrated from github.com)

I hope I can help :)


const data = [['aaaaa  aaaaa  aaaaaaaaaaa', 'bbbb', 'ccccc', 'dd']]
const minWidth = 10

const worksheet = XLSX.utils.aoa_to_sheet(aoa);

const howManyColumns = data.reduce((prev, curr) => {
  return curr.length > prev ? curr.length : prev
}, 0)

let currentWidht = minWidth
const worksheetCols = []

for (const index in Array.from({ length: howManyColumns })) {
  for (const curr of data) {
    currentWidht =
      curr[index].length > currentWidht ? curr[index].length : currentWidht
  }

  worksheetCols.push({
    width: currentWidht
  })

  currentWidht = minWidth
}

worksheet["!cols"] = worksheetCols;

XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);

I hope I can help :) ```js const data = [['aaaaa aaaaa aaaaaaaaaaa', 'bbbb', 'ccccc', 'dd']] const minWidth = 10 const worksheet = XLSX.utils.aoa_to_sheet(aoa); const howManyColumns = data.reduce((prev, curr) => { return curr.length > prev ? curr.length : prev }, 0) let currentWidht = minWidth const worksheetCols = [] for (const index in Array.from({ length: howManyColumns })) { for (const curr of data) { currentWidht = curr[index].length > currentWidht ? curr[index].length : currentWidht } worksheetCols.push({ width: currentWidht }) currentWidht = minWidth } worksheet["!cols"] = worksheetCols; XLSX.utils.book_append_sheet(workbook, worksheet, sheetName); ```
Sign in to join this conversation.
No Milestone
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: sheetjs/sheetjs#1473
No description provided.