forked from sheetjs/docs.sheetjs.com
		
	datenf
This commit is contained in:
		
							parent
							
								
									881a848c93
								
							
						
					
					
						commit
						86d7a8f06a
					
				@ -20,7 +20,7 @@ Each standalone release script is available at <https://cdn.sheetjs.com/>.
 | 
			
		||||
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
 | 
			
		||||
:::info
 | 
			
		||||
:::tip pass
 | 
			
		||||
 | 
			
		||||
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
 | 
			
		||||
[RSS feed](https://git.sheetjs.com/sheetjs/sheetjs/tags.rss) to be notified when
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,7 @@ import { read, writeFileXLSX } from "xlsx";
 | 
			
		||||
 | 
			
		||||
The ["Bundlers" demo](/docs/demos/bundler) includes examples for specific tools.
 | 
			
		||||
 | 
			
		||||
:::info
 | 
			
		||||
:::tip pass
 | 
			
		||||
 | 
			
		||||
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
 | 
			
		||||
[RSS feed](https://git.sheetjs.com/sheetjs/sheetjs/tags.rss) to be notified when
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
 | 
			
		||||
  </TabItem>
 | 
			
		||||
</Tabs>
 | 
			
		||||
 | 
			
		||||
:::info
 | 
			
		||||
:::tip pass
 | 
			
		||||
 | 
			
		||||
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
 | 
			
		||||
[RSS feed](https://git.sheetjs.com/sheetjs/sheetjs/tags.rss) to be notified when
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ script as `xlsx.full.min`.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
:::info
 | 
			
		||||
:::tip pass
 | 
			
		||||
 | 
			
		||||
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
 | 
			
		||||
[RSS feed](https://git.sheetjs.com/sheetjs/sheetjs/tags.rss) to be notified when
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ After downloading the script, it can be directly referenced with `#include`:
 | 
			
		||||
#include "xlsx.extendscript.js"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::info
 | 
			
		||||
:::tip pass
 | 
			
		||||
 | 
			
		||||
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
 | 
			
		||||
[RSS feed](https://git.sheetjs.com/sheetjs/sheetjs/tags.rss) to be notified when
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ be reported to the Deno project for further diagnosis.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
:::info
 | 
			
		||||
:::tip pass
 | 
			
		||||
 | 
			
		||||
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
 | 
			
		||||
[RSS feed](https://git.sheetjs.com/sheetjs/sheetjs/tags.rss) to be notified when
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ be reported to the Bun project for further diagnosis.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
:::info
 | 
			
		||||
:::tip pass
 | 
			
		||||
 | 
			
		||||
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
 | 
			
		||||
[RSS feed](https://git.sheetjs.com/sheetjs/sheetjs/tags.rss) to be notified when
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ read the installation instructions for your use case:
 | 
			
		||||
  </li>);
 | 
			
		||||
})}</ul>
 | 
			
		||||
 | 
			
		||||
:::info
 | 
			
		||||
:::tip pass
 | 
			
		||||
 | 
			
		||||
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
 | 
			
		||||
[RSS feed](https://git.sheetjs.com/sheetjs/sheetjs/tags.rss) to be notified when
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,15 @@ The discussion focuses on the problem solving mindset.  API details are covered
 | 
			
		||||
in other parts of the documentation.
 | 
			
		||||
 | 
			
		||||
The goal of this example is to generate a XLSX workbook of US President names
 | 
			
		||||
and birthdays.  [Click here](#live-demo) to jump to the live demo. The sequence
 | 
			
		||||
diagram below shows the process:
 | 
			
		||||
and birthdates. We will download and wrangle a JSON dataset using standard
 | 
			
		||||
JavaScript functions. Once we have a simple list of names and birthdates, we
 | 
			
		||||
will use SheetJS API functions to build a workbook object and export to XLSX.
 | 
			
		||||
 | 
			
		||||
The ["Live Demo"](#live-demo) section includes a working demo in this page!
 | 
			
		||||
["Run the Demo Locally"](#run-the-demo-locally) shows how to run the workflow in
 | 
			
		||||
iOS / Android apps, desktop apps, NodeJS scripts and other environments.
 | 
			
		||||
 | 
			
		||||
The follow sequence diagram shows the process:
 | 
			
		||||
 | 
			
		||||
```mermaid
 | 
			
		||||
sequenceDiagram
 | 
			
		||||
@ -31,17 +38,17 @@ sequenceDiagram
 | 
			
		||||
  A->>P: raw data
 | 
			
		||||
  Note over P: process data
 | 
			
		||||
  Note over P: make workbook
 | 
			
		||||
  Note over P: setup download
 | 
			
		||||
  Note over P: export file
 | 
			
		||||
  P->>U: download workbook
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Acquire Data
 | 
			
		||||
 | 
			
		||||
### Raw Data
 | 
			
		||||
The raw data is available in JSON form[^1]. It has been mirrored at
 | 
			
		||||
<https://sheetjs.com/data/executive.json>
 | 
			
		||||
 | 
			
		||||
[The raw data is available in JSON form](https://theunitedstates.io/congress-legislators/executive.json).
 | 
			
		||||
For convenience, it has been [mirrored here](https://sheetjs.com/data/executive.json)
 | 
			
		||||
### Raw Data
 | 
			
		||||
 | 
			
		||||
Acquiring the data is straightforward with `fetch`:
 | 
			
		||||
 | 
			
		||||
@ -50,23 +57,105 @@ const url = "https://sheetjs.com/data/executive.json";
 | 
			
		||||
const raw_data = await (await fetch(url)).json();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The raw data is an Array of objects. This is the data for John Adams:
 | 
			
		||||
<details><summary><b>Code Explanation</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
`fetch` is a low-level API for downloading data from an endpoint. It separates
 | 
			
		||||
the network step from the response parsing step.
 | 
			
		||||
 | 
			
		||||
**Network Step**
 | 
			
		||||
 | 
			
		||||
`fetch(url)` returns a `Promise` representing the network request. The browser
 | 
			
		||||
will attempt to download data from the URL. If the network request succeeded,
 | 
			
		||||
the `Promise` will "return" with a `Response` object.
 | 
			
		||||
 | 
			
		||||
Using modern syntax, inside an `async` function, code should `await` the fetch:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const response = await fetch(url);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Checking Status Code**
 | 
			
		||||
 | 
			
		||||
If the file is not available, the `fetch` will still succeed.
 | 
			
		||||
 | 
			
		||||
The status code, stored in the `status` property of the `Response` object, is a
 | 
			
		||||
standard HTTP status code number. Code should check the result.
 | 
			
		||||
 | 
			
		||||
Typically servers will return status `404` "File not Found" if the file is not
 | 
			
		||||
available. A successful request should have status `200` "OK".
 | 
			
		||||
 | 
			
		||||
**Extracting Data**
 | 
			
		||||
 | 
			
		||||
`Response#json` will try to parse the data using `JSON.parse`. Like `fetch`, the
 | 
			
		||||
`json` method returns a `Promise` that must be `await`-ed:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const raw_data = await response.json();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::note pass
 | 
			
		||||
 | 
			
		||||
The `Response` object has other useful methods. `Response#arrayBuffer` will
 | 
			
		||||
return the raw data as an `ArrayBuffer`, suitable for parsing workbook files.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
**Production Use**
 | 
			
		||||
 | 
			
		||||
Functions can test each part independently and report different errors:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
async function get_data_from_endpoint(url) {
 | 
			
		||||
  /* perform network request */
 | 
			
		||||
  let response;
 | 
			
		||||
  try {
 | 
			
		||||
    response = await fetch(url);
 | 
			
		||||
  } catch(e) {
 | 
			
		||||
    /* network error */
 | 
			
		||||
    throw new Error(`Network Error: ${e.message}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* check status code */
 | 
			
		||||
  if(response.status == 404) {
 | 
			
		||||
    /* server 404 error -- file not found */
 | 
			
		||||
    throw new Error("File not found");
 | 
			
		||||
  }
 | 
			
		||||
  if(response.status != 200) {
 | 
			
		||||
    /* for most servers, a successful response will have status 200 */
 | 
			
		||||
    throw new Error(`Server status ${response.status}: ${response.statusText}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* parse JSON */
 | 
			
		||||
  let data;
 | 
			
		||||
  try {
 | 
			
		||||
     data = await response.json();
 | 
			
		||||
  } catch(e) {
 | 
			
		||||
    /* parsing error */
 | 
			
		||||
    throw new Error(`Parsing Error: ${e.message}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return data;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
The raw data is an Array of objects[^2]. For this discussion, the relevant data
 | 
			
		||||
for John Adams is shown below:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
{
 | 
			
		||||
  "id": { /* (data omitted) */ },
 | 
			
		||||
  "name": {
 | 
			
		||||
    "first": "John",          // <-- first name
 | 
			
		||||
    "last": "Adams"           // <-- last name
 | 
			
		||||
  },
 | 
			
		||||
  "bio": {
 | 
			
		||||
    "birthday": "1735-10-19", // <-- birthday
 | 
			
		||||
    "gender": "M"
 | 
			
		||||
  },
 | 
			
		||||
  "terms": [
 | 
			
		||||
    { "type": "viceprez", "start": "1789-04-21", /* (other fields omitted) */ },
 | 
			
		||||
    { "type": "viceprez", "start": "1793-03-04", /* (other fields omitted) */ },
 | 
			
		||||
    { "type": "prez",     "start": "1797-03-04", /* (other fields omitted) */ }
 | 
			
		||||
  "terms": [                  // <-- array of presidential terms
 | 
			
		||||
    { "type": "viceprez", "start": "1789-04-21", },
 | 
			
		||||
    { "type": "viceprez", "start": "1793-03-04", },
 | 
			
		||||
    { "type": "prez",     "start": "1797-03-04", } // <-- presidential term
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@ -75,15 +164,62 @@ The raw data is an Array of objects. This is the data for John Adams:
 | 
			
		||||
 | 
			
		||||
The dataset includes Aaron Burr, a Vice President who was never President!
 | 
			
		||||
 | 
			
		||||
`Array#filter` creates a new array with the desired rows.  A President served
 | 
			
		||||
at least one term with `type` set to `"prez"`.  To test if a particular row has
 | 
			
		||||
at least one `"prez"` term, `Array#some` is another native JS function.  The
 | 
			
		||||
complete filter would be:
 | 
			
		||||
The `terms` field of each object is an array of terms. A term is a Presidential
 | 
			
		||||
term if the `type` property is `"prez"`. We are interested in Presidents that
 | 
			
		||||
served at least one term. The following line creates an array of Presidents:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::caution pass
 | 
			
		||||
 | 
			
		||||
JavaScript code can be extremely concise. The "Code Explanation" blocks explain
 | 
			
		||||
the code in more detail.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
<details><summary><b>Code Explanation</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
**Verifying if a person was a US President**
 | 
			
		||||
 | 
			
		||||
`Array#some` takes a function and calls it on each element of an array in order.
 | 
			
		||||
If the function ever returns `true`, `Array#some` returns `true`. If each call
 | 
			
		||||
returns `false`, `Array#some` returns `false`.
 | 
			
		||||
 | 
			
		||||
The following function tests if a term is presidential:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const term_is_presidential = term => term.type == "prez";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To test if a person was a President, that function should be tested against
 | 
			
		||||
every term in the `terms` array:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const person_was_president = person => person.terms.some(term => term.type == "prez");
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Creating a list of US Presidents**
 | 
			
		||||
 | 
			
		||||
`Array#filter` takes a function and returns an array. The function is called on
 | 
			
		||||
each element in order. If the function returns `true`, the element is added to
 | 
			
		||||
the final array. If the function returns false, the element is not added.
 | 
			
		||||
 | 
			
		||||
Using the previous function, this line filters the dataset for Presidents:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const prez = raw_data.filter(row => person_was_president(row));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Placing the `person_was_president` function in-line, the final code is:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const prez = raw_data.filter(row => row.terms.some(term => term.type == "prez"));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
### Sorting by First Term
 | 
			
		||||
 | 
			
		||||
The dataset is sorted in chronological order by the first presidential or vice
 | 
			
		||||
@ -94,21 +230,136 @@ data point appears first. The goal is to sort the presidents in order of their
 | 
			
		||||
presidential term.
 | 
			
		||||
 | 
			
		||||
The first step is adding the first presidential term start date to the dataset.
 | 
			
		||||
`Array#find` will find the first value in an array that matches a criterion.
 | 
			
		||||
The following code looks at each president and creates a `"start"` property that
 | 
			
		||||
The following code looks at each president and creates a `start` property that
 | 
			
		||||
represents the start of the first presidential term.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
prez.forEach(prez => prez.start = prez.terms.find(term => term.type === "prez").start);
 | 
			
		||||
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`Array#sort` will sort the array. Since the `start` properties are strings, the
 | 
			
		||||
recommended approach is to use `String#localeCompare` to compare strings:
 | 
			
		||||
<details><summary><b>Code Explanation</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
**Finding the first presidential term**
 | 
			
		||||
 | 
			
		||||
`Array#find` will find the first value in an array that matches a criterion.
 | 
			
		||||
The first presidential term can be found with the following function:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const first_prez_term = prez => prez.terms.find(term => term.type === "prez");
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
If no element in the array matches the criterion, `Array#find` does not return
 | 
			
		||||
a value. In this case, since `prez` was created by filtering for people that
 | 
			
		||||
served at least one presidential term, the code assumes a term exists.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
The start of a President's first Presidential term is therefore
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const first_prez_term_start = prez => first_prez_term(prez).start;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Adding the first start date to one row**
 | 
			
		||||
 | 
			
		||||
The following function creates the desired `start` property:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const prez_add_start = prez => prez.start = first_prez_term_start(prez);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Adding the first start date to each row**
 | 
			
		||||
 | 
			
		||||
`Array#forEach` takes a function and calls it for every element in the array.
 | 
			
		||||
Any modifications to objects affect the objects in the original array.
 | 
			
		||||
 | 
			
		||||
The previous function can be used directly:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
prez.forEach(row => prez_add_start(row));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Working in reverse, each partial function can be inserted in place. These lines
 | 
			
		||||
of code are equivalent:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
/* start */
 | 
			
		||||
prez.forEach(row => prez_add_start(row));
 | 
			
		||||
 | 
			
		||||
/* put `prez_add_start` definition into the line */
 | 
			
		||||
prez.forEach(row => row.start = first_prez_term_start(row));
 | 
			
		||||
 | 
			
		||||
/* put `first_prez_term_start` definition into the line */
 | 
			
		||||
prez.forEach(row => row.start = first_prez_term(row).start);
 | 
			
		||||
 | 
			
		||||
/* put `first_prez_term` definition into the line */
 | 
			
		||||
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
At this point, each row in the `prez` array has a `start` property. Since the
 | 
			
		||||
`start` properties are strings, the following line sorts the array:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
prez.sort((l,r) => l.start.localeCompare(r.start));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<details><summary><b>Code Explanation</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
**Comparator Functions and Relative Ordering in JavaScript**
 | 
			
		||||
 | 
			
		||||
A comparator takes two arguments and returns a number that represents the
 | 
			
		||||
relative ordering. `comparator(a,b)` should return a negative number if `a`
 | 
			
		||||
should be placed before `b`. If `b` should be placed before `a`, the comparator
 | 
			
		||||
should return a positive number.
 | 
			
		||||
 | 
			
		||||
If the `start` properties were numbers, the following comparator would suffice:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const comparator_numbers = (a,b) => a - b;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For strings, JavaScript comparison operators can work:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const comparator_string_simple = (a,b) => a == b ? 0 : a < b ? -1 : 1;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
However, that comparator does not handle diacritics. For example, `"z" < "é"`.
 | 
			
		||||
It is strongly recommended to use `String#localeCompare` to compare strings:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const comparator_string = (a,b) => a.localeCompare(b);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Comparing two Presidents**
 | 
			
		||||
 | 
			
		||||
The `start` properties of the Presidents should be compared:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const compare_prez = (a,b) => (a.start).localeCompare(b.start);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Sorting the Array**
 | 
			
		||||
 | 
			
		||||
`Array#sort` takes a comparator function and sorts the array in place. Using
 | 
			
		||||
the Presidential comparator:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
prez.sort((l,r) => compare_prez(l,r));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Placing the `compare_prez` function in the body:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
prez.sort((l,r) => l.start.localeCompare(r.start));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
### Reshaping the Array
 | 
			
		||||
 | 
			
		||||
For this example, the name will be the first name combined with the last name
 | 
			
		||||
@ -122,6 +373,86 @@ const rows = prez.map(row => ({
 | 
			
		||||
}));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<details><summary><b>Code Explanation</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
**Wrangling One Data Row**
 | 
			
		||||
 | 
			
		||||
The key fields for John Adams are shown below:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
{
 | 
			
		||||
  "name": {
 | 
			
		||||
    "first": "John",          // <-- first name
 | 
			
		||||
    "last": "Adams"           // <-- last name
 | 
			
		||||
  },
 | 
			
		||||
  "bio": {
 | 
			
		||||
    "birthday": "1735-10-19", // <-- birthday
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If `row` is the object, then
 | 
			
		||||
- `row.name.first` is the first name ("John")
 | 
			
		||||
- `row.name.last` is the last name ("Adams")
 | 
			
		||||
- `row.bio.birthday` is the birthday ("1735-10-19")
 | 
			
		||||
 | 
			
		||||
The desired object has a `name` and `birthday` field:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
function get_data(row) {
 | 
			
		||||
  var name = row.name.first + " " + row.name.last;
 | 
			
		||||
  var birthday = row.bio.birthday;
 | 
			
		||||
  return ({
 | 
			
		||||
    name: name,
 | 
			
		||||
    birthday: birthday
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This can be shortened by adding the fields to the object directly:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
function get_data(row) {
 | 
			
		||||
  return ({
 | 
			
		||||
    name: row.name.first + " " + row.name.last,
 | 
			
		||||
    birthday: row.bio.birthday
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
When writing an arrow function that returns an object, parentheses are required:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
//  open paren required --V
 | 
			
		||||
  const get_data = row => ({
 | 
			
		||||
    name: row.name.first + " " + row.name.last,
 | 
			
		||||
    birthday: row.bio.birthday
 | 
			
		||||
  });
 | 
			
		||||
// ^-- close paren required
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Wrangling the entire dataset**
 | 
			
		||||
 | 
			
		||||
`Array#map` calls a function on each element of an array and returns a new array
 | 
			
		||||
with the return values of each function.
 | 
			
		||||
 | 
			
		||||
Using the previous method:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const rows = prez.map(row => get_data(row));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `get_data` function can be added in place:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const rows = prez.map(row => ({
 | 
			
		||||
  name: row.name.first + " " + row.name.last,
 | 
			
		||||
  birthday: row.bio.birthday
 | 
			
		||||
}));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
The result is an array of "simple" objects with no nesting:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
@ -134,13 +465,13 @@ The result is an array of "simple" objects with no nesting:
 | 
			
		||||
 | 
			
		||||
## Create a Workbook
 | 
			
		||||
 | 
			
		||||
With the cleaned dataset, `XLSX.utils.json_to_sheet` generates a worksheet:
 | 
			
		||||
With the cleaned dataset, `XLSX.utils.json_to_sheet`[^3] generates a worksheet:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const worksheet = XLSX.utils.json_to_sheet(rows);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`XLSX.utils.book_new` creates a new workbook and `XLSX.utils.book_append_sheet`
 | 
			
		||||
`XLSX.utils.book_new`[^4] creates a new workbook and `XLSX.utils.book_append_sheet`[^5]
 | 
			
		||||
appends a worksheet to the workbook. The new worksheet will be called "Dates":
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
@ -163,7 +494,7 @@ additional styling options like cell styling and frozen rows.
 | 
			
		||||
By default, `json_to_sheet` creates a worksheet with a header row. In this case,
 | 
			
		||||
the headers come from the JS object keys: "name" and "birthday".
 | 
			
		||||
 | 
			
		||||
The headers are in cells `A1` and `B1`.  `XLSX.utils.sheet_add_aoa` can write
 | 
			
		||||
The headers are in cells `A1` and `B1`.  `XLSX.utils.sheet_add_aoa`[^6] can write
 | 
			
		||||
text values to the existing worksheet starting at cell `A1`:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
@ -175,7 +506,7 @@ XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
 | 
			
		||||
<details><summary><b>Changing Column Widths</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
Some of the names are longer than the default column width.  Column widths are
 | 
			
		||||
set by setting the `"!cols"` worksheet property.
 | 
			
		||||
set by setting the `"!cols"` worksheet property.[^7]
 | 
			
		||||
 | 
			
		||||
The following line sets the width of column A to approximately 10 characters:
 | 
			
		||||
 | 
			
		||||
@ -198,9 +529,9 @@ After cleanup, the generated workbook looks like the screenshot below:
 | 
			
		||||
 | 
			
		||||
## Export a File
 | 
			
		||||
 | 
			
		||||
`XLSX.writeFile` creates a spreadsheet file and tries to write it to the system.
 | 
			
		||||
In the browser, it will try to prompt the user to download the file.  In NodeJS,
 | 
			
		||||
it will write to the local directory.
 | 
			
		||||
`XLSX.writeFile`[^8] creates a spreadsheet file and tries to write it to the
 | 
			
		||||
system. In the browser, it will try to prompt the user to download the file. In
 | 
			
		||||
NodeJS, it will write to the local directory.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
XLSX.writeFile(workbook, "Presidents.xlsx", { compression: true });
 | 
			
		||||
@ -221,7 +552,7 @@ function Presidents() { return ( <button onClick={async () => {
 | 
			
		||||
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
			
		||||
 | 
			
		||||
  /* sort by first presidential term */
 | 
			
		||||
  prez.forEach(prez => prez.start = prez.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.sort((l,r) => l.start.localeCompare(r.start));
 | 
			
		||||
 | 
			
		||||
  /* flatten objects */
 | 
			
		||||
@ -269,7 +600,7 @@ Save the following script to `SheetJSStandaloneDemo.html`:
 | 
			
		||||
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
			
		||||
\n\
 | 
			
		||||
  /* sort by first presidential term */
 | 
			
		||||
  prez.forEach(prez => prez.start = prez.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.sort((l,r) => l.start.localeCompare(r.start));
 | 
			
		||||
\n\
 | 
			
		||||
  /* flatten objects */
 | 
			
		||||
@ -330,7 +661,7 @@ const XLSX = require("xlsx");
 | 
			
		||||
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
			
		||||
 | 
			
		||||
  /* sort by first presidential term */
 | 
			
		||||
  prez.forEach(prez => prez.start = prez.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.sort((l,r) => l.start.localeCompare(r.start));
 | 
			
		||||
 | 
			
		||||
  /* flatten objects */
 | 
			
		||||
@ -395,7 +726,7 @@ const axios = require("axios");
 | 
			
		||||
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
			
		||||
 | 
			
		||||
  /* sort by first presidential term */
 | 
			
		||||
  prez.forEach(prez => prez.start = prez.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.sort((l,r) => l.start.localeCompare(r.start));
 | 
			
		||||
 | 
			
		||||
  /* flatten objects */
 | 
			
		||||
@ -452,7 +783,7 @@ const raw_data = await (await fetch(url)).json();
 | 
			
		||||
const prez = raw_data.filter((row: any) => row.terms.some((term: any) => term.type === "prez"));
 | 
			
		||||
\n\
 | 
			
		||||
/* sort by first presidential term */
 | 
			
		||||
prez.forEach(prez => prez.start = prez.terms.find(term => term.type === "prez").start);
 | 
			
		||||
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
 | 
			
		||||
prez.sort((l,r) => l.start.localeCompare(r.start));
 | 
			
		||||
\n\
 | 
			
		||||
/* flatten objects */
 | 
			
		||||
@ -509,7 +840,7 @@ const raw_data = await (await fetch(url)).json();
 | 
			
		||||
const prez = raw_data.filter((row) => row.terms.some((term) => term.type === "prez"));
 | 
			
		||||
 | 
			
		||||
/* sort by first presidential term */
 | 
			
		||||
prez.forEach(prez => prez.start = prez.terms.find(term => term.type === "prez").start);
 | 
			
		||||
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
 | 
			
		||||
prez.sort((l,r) => l.start.localeCompare(r.start));
 | 
			
		||||
 | 
			
		||||
/* flatten objects */
 | 
			
		||||
@ -566,7 +897,7 @@ Save the following script to `SheetJSNW.html`:
 | 
			
		||||
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
			
		||||
\n\
 | 
			
		||||
  /* sort by first presidential term */
 | 
			
		||||
  prez.forEach(prez => prez.start = prez.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.sort((l,r) => l.start.localeCompare(r.start));
 | 
			
		||||
\n\
 | 
			
		||||
  /* flatten objects */
 | 
			
		||||
@ -654,7 +985,7 @@ const make_workbook = async() => {
 | 
			
		||||
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
			
		||||
 | 
			
		||||
  /* sort by first presidential term */
 | 
			
		||||
  prez.forEach(prez => prez.start = prez.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
 | 
			
		||||
  prez.sort((l,r) => l.start.localeCompare(r.start));
 | 
			
		||||
 | 
			
		||||
  /* flatten objects */
 | 
			
		||||
@ -816,4 +1147,16 @@ see a preview of the data.  The Numbers app can open the file.
 | 
			
		||||
</Tabs>
 | 
			
		||||
 | 
			
		||||
  </TabItem>
 | 
			
		||||
</Tabs>
 | 
			
		||||
</Tabs>
 | 
			
		||||
 | 
			
		||||
[^1]: <https://theunitedstates.io/congress-legislators/executive.json> is the
 | 
			
		||||
      original location of the example dataset. The contributors to the dataset
 | 
			
		||||
      dedicated the content to the public domain.
 | 
			
		||||
[^2]: See ["The Executive Branch](https://github.com/unitedstates/congress-legislators#the-executive-branch)
 | 
			
		||||
      in the dataset documentation.
 | 
			
		||||
[^3]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
 | 
			
		||||
[^4]: See [`book_new` in "Utilities"](/docs/api/utilities/wb)
 | 
			
		||||
[^5]: See [`book_append_sheet` in "Utilities"](/docs/api/utilities/wb)
 | 
			
		||||
[^6]: See [`sheet_add_aoa` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
 | 
			
		||||
[^7]: See ["Row and Column Properties"](/docs/csf/features/#row-and-column-properties)
 | 
			
		||||
[^8]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
 | 
			
		||||
@ -31,7 +31,7 @@ read the installation instructions for your use case:
 | 
			
		||||
  });
 | 
			
		||||
})}</ul>
 | 
			
		||||
 | 
			
		||||
:::info
 | 
			
		||||
:::tip pass
 | 
			
		||||
 | 
			
		||||
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
 | 
			
		||||
[RSS feed](https://git.sheetjs.com/sheetjs/sheetjs/tags.rss) to be notified when
 | 
			
		||||
 | 
			
		||||
@ -197,28 +197,28 @@ the table element works with the SheetJS DOM methods after patching the object.
 | 
			
		||||
 | 
			
		||||
This example fetches [a sample table](pathname:///dom/SheetJSTable.html):
 | 
			
		||||
 | 
			
		||||
```ts title="SheetJSDenoDOM.ts"
 | 
			
		||||
// @deno-types="https://cdn.sheetjs.com/xlsx-0.19.3/package/types/index.d.ts"
 | 
			
		||||
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-0.19.3/package/xlsx.mjs';
 | 
			
		||||
 | 
			
		||||
<CodeBlock language="ts" title="SheetJSDenoDOM.ts">{`\
 | 
			
		||||
// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts"
 | 
			
		||||
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';
 | 
			
		||||
\n\
 | 
			
		||||
import { DOMParser } from 'https://deno.land/x/deno_dom@v0.1.38/deno-dom-wasm.ts';
 | 
			
		||||
 | 
			
		||||
\n\
 | 
			
		||||
const doc = new DOMParser().parseFromString(
 | 
			
		||||
	await (await fetch('https://docs.sheetjs.com/dom/SheetJSTable.html')).text(),
 | 
			
		||||
  await (await fetch('https://docs.sheetjs.com/dom/SheetJSTable.html')).text(),
 | 
			
		||||
  "text/html",
 | 
			
		||||
)!;
 | 
			
		||||
// highlight-start
 | 
			
		||||
const tbl = doc.querySelector("table");
 | 
			
		||||
 | 
			
		||||
\n\
 | 
			
		||||
/* patch DenoDOM element */
 | 
			
		||||
tbl.rows = tbl.querySelectorAll("tr");
 | 
			
		||||
tbl.rows.forEach(row => row.cells = row.querySelectorAll("td, th"))
 | 
			
		||||
 | 
			
		||||
\n\
 | 
			
		||||
/* generate workbook */
 | 
			
		||||
const workbook = XLSX.utils.table_to_book(tbl);
 | 
			
		||||
// highlight-end
 | 
			
		||||
XLSX.writeFile(workbook, "SheetJSDenoDOM.xlsx");
 | 
			
		||||
```
 | 
			
		||||
XLSX.writeFile(workbook, "SheetJSDenoDOM.xlsx");`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
 | 
			
		||||
<details open><summary><b>Complete Demo</b> (click to hide)</summary>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -375,7 +375,7 @@ wails build
 | 
			
		||||
At the end, it will print the path to the generated program. Run the program!
 | 
			
		||||
 | 
			
		||||
[^1]: See ["How does it Work?"](https://wails.io/docs/howdoesitwork) in the Wails documentation.
 | 
			
		||||
[^2]: See [`read` in "Parsing Options"](/docs/api/parse-options)
 | 
			
		||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
 | 
			
		||||
[^3]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
 | 
			
		||||
[^4]: See [`OpenFileDialog`](https://wails.io/docs/reference/runtime/dialog#openfiledialog) in the Wails documentation.
 | 
			
		||||
[^5]: See [`ReadFile`](https://pkg.go.dev/os#ReadFile) in the Go documentation
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
---
 | 
			
		||||
title: Tauri
 | 
			
		||||
sidebar_label: Tauri
 | 
			
		||||
description: Build data-intensive desktop apps using Tauri. Seamlessly integrate spreadsheets into your app using SheetJS. Modernize Excel-powered business processes with confidence.
 | 
			
		||||
pagination_prev: demos/mobile/index
 | 
			
		||||
pagination_next: demos/data/index
 | 
			
		||||
sidebar_position: 4
 | 
			
		||||
@ -7,15 +9,25 @@ sidebar_custom_props:
 | 
			
		||||
  summary: Webview + Rust Backend
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# Data Wranging in Tauri Apps
 | 
			
		||||
 | 
			
		||||
import current from '/version.js';
 | 
			
		||||
import Tabs from '@theme/Tabs';
 | 
			
		||||
import TabItem from '@theme/TabItem';
 | 
			
		||||
import CodeBlock from '@theme/CodeBlock';
 | 
			
		||||
 | 
			
		||||
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
 | 
			
		||||
from JavaScript code.
 | 
			
		||||
[Tauri](https://tauri.app/) is a modern toolkit for building desktop apps. Tauri
 | 
			
		||||
apps leverage platform-native browser engines to build lightweight programs.
 | 
			
		||||
 | 
			
		||||
The "Complete Example" creates an app that looks like the screenshot:
 | 
			
		||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
 | 
			
		||||
data from spreadsheets.
 | 
			
		||||
 | 
			
		||||
This demo uses Tauri and SheetJS to pull data from a spreadsheet and display the
 | 
			
		||||
data in the app. We'll explore how to load SheetJS in a Tauri app and exchange
 | 
			
		||||
file data between the JavaScript frontend and Rust backend.
 | 
			
		||||
 | 
			
		||||
The ["Complete Example"](#complete-example) section covers a complete desktop
 | 
			
		||||
app to read and write workbooks. The app will look like the screenshots below:
 | 
			
		||||
 | 
			
		||||
<table><thead><tr>
 | 
			
		||||
  <th><a href="#complete-example">Win10</a></th>
 | 
			
		||||
@ -37,33 +49,74 @@ The "Complete Example" creates an app that looks like the screenshot:
 | 
			
		||||
 | 
			
		||||
## Integration Details
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
 | 
			
		||||
installed and imported from JavaScript code.
 | 
			
		||||
 | 
			
		||||
:::note pass
 | 
			
		||||
 | 
			
		||||
Tauri currently does not provide the equivalent of NodeJS `fs` module.  The raw
 | 
			
		||||
`@tauri-apps/api` methods used in the examples are not expected to change.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
`http`, `dialog`, and `fs` must be explicitly allowed in `tauri.conf.json`:
 | 
			
		||||
For security reasons, Tauri apps must explicitly enable system features.[^1]
 | 
			
		||||
They are enabled in `src-tauri/tauri.conf.json` in the `allowlist` subsection of
 | 
			
		||||
the `tauri` section of the config.
 | 
			
		||||
 | 
			
		||||
- The `fs` entitlement[^2] enables reading and writing file data.
 | 
			
		||||
 | 
			
		||||
```js title="src-tauri/tauri.conf.json"
 | 
			
		||||
  "tauri": {
 | 
			
		||||
    "allowlist": {
 | 
			
		||||
      //highlight-start
 | 
			
		||||
      "fs": {
 | 
			
		||||
        "all": true
 | 
			
		||||
      }
 | 
			
		||||
      // highlight-end
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- The `dialog` entitlement[^3] enables the open and save dialog methods.
 | 
			
		||||
 | 
			
		||||
```js title="src-tauri/tauri.conf.json"
 | 
			
		||||
  "tauri": {
 | 
			
		||||
    "allowlist": {
 | 
			
		||||
      //highlight-start
 | 
			
		||||
      "dialog": {
 | 
			
		||||
        "all": true
 | 
			
		||||
      }
 | 
			
		||||
      // highlight-end
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- The `http` entitlement[^4] enables downloading files. Note that `http` is not
 | 
			
		||||
  needed for reading or writing files in the local filesystem.
 | 
			
		||||
 | 
			
		||||
```json title="src-tauri/tauri.conf.json"
 | 
			
		||||
  "tauri": {
 | 
			
		||||
    "allowlist": {
 | 
			
		||||
      //highlight-start
 | 
			
		||||
      "http": {
 | 
			
		||||
        "all": true,
 | 
			
		||||
        "request": true,
 | 
			
		||||
        "scope": ["https://**"]
 | 
			
		||||
      },
 | 
			
		||||
      "dialog": {
 | 
			
		||||
        "all": true
 | 
			
		||||
      },
 | 
			
		||||
      "fs": {
 | 
			
		||||
        "all": true
 | 
			
		||||
      }
 | 
			
		||||
      // highlight-end
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Reading Files
 | 
			
		||||
 | 
			
		||||
There are two steps to reading files: obtaining a path and reading binary data:
 | 
			
		||||
There are three steps to reading files:
 | 
			
		||||
 | 
			
		||||
1) Show an open file dialog to allow users to select a path. The `open` method
 | 
			
		||||
   in `@tauri-apps/api/dialog`[^5] simplifies this process.
 | 
			
		||||
 | 
			
		||||
2) Read raw data from the selected file using the `readBinaryFile` method in
 | 
			
		||||
   `@tauri-apps/api/fs`[^6]. This method resolves to a standard `Uint8Array`
 | 
			
		||||
 | 
			
		||||
3) Parse the data with the SheetJS `read` method[^7]. This method returns a
 | 
			
		||||
   SheetJS workbook object.
 | 
			
		||||
 | 
			
		||||
The following code example defines a single function `openFile` that performs
 | 
			
		||||
all three steps and returns a SheetJS workbook object:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
import { read } from 'xlsx';
 | 
			
		||||
@ -95,9 +148,47 @@ async function openFile() {
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
At this point, standard SheetJS utility functions[^8] can extract data from the
 | 
			
		||||
workbook object. The demo includes a button that calls `sheet_to_json`[^9] to
 | 
			
		||||
generate an array of arrays of data. The following snippet uses VueJS framework
 | 
			
		||||
but the same logic works with ReactJS and other front-end frameworks:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
import { utils } from 'xlsx';
 | 
			
		||||
import { shallowRef } from 'vue';
 | 
			
		||||
const data = shallowRef([[]]); // update data by setting `data.value`
 | 
			
		||||
 | 
			
		||||
const open_button_callback = async() => {
 | 
			
		||||
  const wb = await openFile();
 | 
			
		||||
 | 
			
		||||
  /* get the first worksheet */
 | 
			
		||||
  // highlight-start
 | 
			
		||||
  const ws = wb.Sheets[wb.SheetNames[0]];
 | 
			
		||||
  // highlight-end
 | 
			
		||||
 | 
			
		||||
  /* get data from the first worksheet */
 | 
			
		||||
  // highlight-start
 | 
			
		||||
  const array = utils.sheet_to_json(ws, { header: 1 });
 | 
			
		||||
  // highlight-end
 | 
			
		||||
  data.value = array;
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Writing Files
 | 
			
		||||
 | 
			
		||||
There are two steps to writing files: obtaining a path and writing binary data:
 | 
			
		||||
There are three steps to writing files:
 | 
			
		||||
 | 
			
		||||
1) Show a save file dialog to allow users to select a path. The `save` method
 | 
			
		||||
   in `@tauri-apps/api/dialog`[^10] simplifies this process.
 | 
			
		||||
 | 
			
		||||
2) Write the data with the SheetJS `write` method[^11]. The output book type can
 | 
			
		||||
   be inferred from the selected file path. Using the `buffer` output type[^12],
 | 
			
		||||
   the method will return a `Uint8Array` object that plays nice with Tauri.
 | 
			
		||||
 | 
			
		||||
3) Write the data using `writeBinaryFile` in `@tauri-apps/api/fs`[^13].
 | 
			
		||||
 | 
			
		||||
The following code example defines a single function `saveFile` that performs
 | 
			
		||||
all three steps starting from a SheetJS workbook object:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
import { write } from 'xlsx';
 | 
			
		||||
@ -128,15 +219,82 @@ async function saveFile(wb) {
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The demo includes a button that calls `aoa_to_sheet`[^14] to generate a sheet
 | 
			
		||||
from array of arrays of data. A workbook is constructed using `book_new` and
 | 
			
		||||
`book_append_sheet`[^15]. The following snippet uses VueJS framework but the
 | 
			
		||||
same logic works with ReactJS and other front-end frameworks:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
import { utils } from 'xlsx';
 | 
			
		||||
import { shallowRef } from 'vue';
 | 
			
		||||
const data = shallowRef([[]]); // `data.value` is an array of arrays
 | 
			
		||||
 | 
			
		||||
const save_button_callback = async() => {
 | 
			
		||||
  /* generate worksheet from the data */
 | 
			
		||||
  // highlight-start
 | 
			
		||||
  const ws = utils.aoa_to_sheet(data.value);
 | 
			
		||||
  // highlight-end
 | 
			
		||||
 | 
			
		||||
  /* create a new workbook object */
 | 
			
		||||
  // highlight-start
 | 
			
		||||
  const wb = utils.book_new();
 | 
			
		||||
  // highlight-end
 | 
			
		||||
 | 
			
		||||
  /* append the worksheet to the workbook using the sheet name "SheetJSTauri" */
 | 
			
		||||
  // highlight-start
 | 
			
		||||
  utils.book_append_sheet(wb, ws, "SheetJSTauri");
 | 
			
		||||
  // highlight-end
 | 
			
		||||
 | 
			
		||||
  await saveFile(wb);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Complete Example
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
:::note pass
 | 
			
		||||
 | 
			
		||||
This demo was tested against Tauri `v1.2.3` on 2023 March 18.
 | 
			
		||||
This demo was tested in the following environments:
 | 
			
		||||
 | 
			
		||||
| OS and Version | Arch | Tauri    | Date       |
 | 
			
		||||
|:---------------|:-----|:---------|:-----------|
 | 
			
		||||
| macOS 13.4.0   | x64  | `v1.4.0` | 2023-06-25 |
 | 
			
		||||
| Windows 10     | x64  | `v1.2.3` | 2023-03-18 |
 | 
			
		||||
| Linux (HoloOS) | x64  | `v1.2.3` | 2023-03-18 |
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
0) [Read Tauri "Getting Started" guide and install dependencies.](https://tauri.app/v1/guides/getting-started/prerequisites)
 | 
			
		||||
0) Read Tauri "Getting Started" guide and install prerequisites.[^16]
 | 
			
		||||
 | 
			
		||||
<details><summary><b>Installation Notes</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
At a high level, the following software is required for building Tauri apps:
 | 
			
		||||
 | 
			
		||||
- a native platform-specific C/C++ compiler (for example, macOS requires Xcode)
 | 
			
		||||
- a browser engine integration (for example, linux requires `webkit2gtk`)
 | 
			
		||||
- [Rust](https://www.rust-lang.org/tools/install)
 | 
			
		||||
 | 
			
		||||
The platform configuration can be verified by running:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npx @tauri-apps/cli info
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If required dependencies are installed, the output will show a checkmark next to
 | 
			
		||||
"Environment". The output from the most recent macOS test is shown below:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
[✔] Environment
 | 
			
		||||
    - OS: Mac OS 13.4.0 X64
 | 
			
		||||
    ✔ Xcode Command Line Tools: installed
 | 
			
		||||
    ✔ rustc: 1.70.0 (90c541806 2023-05-31)
 | 
			
		||||
    ✔ Cargo: 1.70.0 (ec8a8a0ca 2023-04-25)
 | 
			
		||||
    ✔ rustup: 1.26.0 (5af9b9484 2023-04-05)
 | 
			
		||||
    ✔ Rust toolchain: stable-x86_64-apple-darwin (default)
 | 
			
		||||
    - node: 18.16.1
 | 
			
		||||
    - npm: 9.5.1
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
1) Create a new Tauri app:
 | 
			
		||||
 | 
			
		||||
@ -161,8 +319,8 @@ npm i --save @tauri-apps/api
 | 
			
		||||
npm i --save-dev @tauri-apps/cli`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
 | 
			
		||||
3) Enable operations by adding the highlighted lines to `tauri.conf.json` in
 | 
			
		||||
the `tauri.allowlist` section:
 | 
			
		||||
3) Add the highlighted lines to `src-tauri/tauri.conf.json` in the
 | 
			
		||||
   `tauri.allowlist` section:
 | 
			
		||||
 | 
			
		||||
```json title="src-tauri/tauri.conf.json"
 | 
			
		||||
  "tauri": {
 | 
			
		||||
@ -206,4 +364,30 @@ curl -L -o src/App.vue https://docs.sheetjs.com/tauri/App.vue
 | 
			
		||||
npm run tauri build
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
At the end, it will print the path to the generated program. Run the program!
 | 
			
		||||
At the end, it will print the path to the generated program.
 | 
			
		||||
 | 
			
		||||
6) Run the program. The following features should be manually verified:
 | 
			
		||||
 | 
			
		||||
- When it is opened, the app will download <https://sheetjs.com/pres.numbers>
 | 
			
		||||
  and display the data in a table.
 | 
			
		||||
- Clicking "Save Data" will show a save dialog. After selecting a path and name,
 | 
			
		||||
  the app will write a file. That file can be opened in a spreadsheet editor.
 | 
			
		||||
- Edit the file in a spreadsheet editor, then click "Load Data" and select the
 | 
			
		||||
  edited file. The table will refresh with new contents.
 | 
			
		||||
 | 
			
		||||
[^1]: See ["Security"](https://tauri.app/v1/references/architecture/security#allowing-api) in the Tauri documentation
 | 
			
		||||
[^2]: See [`FsAllowlistConfig`](https://tauri.app/v1/api/config/#fsallowlistconfig) in the Tauri documentation
 | 
			
		||||
[^3]: See [`DialogAllowlistConfig`](https://tauri.app/v1/api/config/#dialogallowlistconfig) in the Tauri documentation
 | 
			
		||||
[^4]: See [`HttpAllowlistConfig`](https://tauri.app/v1/api/config/#httpallowlistconfig) in the Tauri documentation
 | 
			
		||||
[^5]: See [`dialog`](https://tauri.app/v1/api/js/dialog/#open) in the Tauri documentation
 | 
			
		||||
[^6]: See [`fs`](https://tauri.app/v1/api/js/fs#readbinaryfile) in the Tauri documentation
 | 
			
		||||
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
 | 
			
		||||
[^8]: See ["Utility Functions"](/docs/api/utilities/)
 | 
			
		||||
[^9]: See ["Array Output" in "Utility Functions"](/docs/api/utilities/array#array-output)
 | 
			
		||||
[^10]: See [`dialog`](https://tauri.app/v1/api/js/dialog/#save) in the Tauri documentation
 | 
			
		||||
[^11]: See [`write` in "Writing Files"](/docs/api/write-options)
 | 
			
		||||
[^12]: See ["Supported Output Formats"](/docs/api/write-options#supported-output-formats)
 | 
			
		||||
[^13]: See [`fs`](https://tauri.app/v1/api/js/fs#writebinaryfile) in the Tauri documentation
 | 
			
		||||
[^14]: See ["Array of Arrays Input" in "Utility Functions"](/docs/api/utilities/array#array-of-arrays-input)
 | 
			
		||||
[^15]: See ["Workbook Helpers" in "Utility Functions"](/docs/api/utilities/wb)
 | 
			
		||||
[^16]: See ["Prerequisites"](https://tauri.app/v1/guides/getting-started/prerequisites) in the Tauri documentation
 | 
			
		||||
@ -692,12 +692,8 @@ _Create a new Workbook_
 | 
			
		||||
var workbook = XLSX.utils.book_new();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `book_new` utility function creates an empty workbook with no worksheets.
 | 
			
		||||
 | 
			
		||||
Spreadsheet software generally require at least one worksheet and enforce the
 | 
			
		||||
requirement in the user interface.  This library enforces the requirement at
 | 
			
		||||
write time, throwing errors if an empty workbook is passed to write functions.
 | 
			
		||||
 | 
			
		||||
The [`book_new` utility function](/docs/api/utilities/wb) creates an empty
 | 
			
		||||
workbook with no worksheets.
 | 
			
		||||
 | 
			
		||||
#### API
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -66,10 +66,8 @@ _Append a Worksheet to a Workbook_
 | 
			
		||||
XLSX.utils.book_append_sheet(workbook, worksheet, sheet_name);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `book_append_sheet` utility function appends a worksheet to the workbook.
 | 
			
		||||
The third argument specifies the desired worksheet name. Multiple worksheets can
 | 
			
		||||
be added to a workbook by calling the function multiple times.  If the worksheet
 | 
			
		||||
name is already used in the workbook, it will throw an error.
 | 
			
		||||
The [`book_append_sheet`](/docs/api/utilities/wb) utility function appends a
 | 
			
		||||
worksheet to the workbook.
 | 
			
		||||
 | 
			
		||||
_Append a Worksheet to a Workbook and find a unique name_
 | 
			
		||||
 | 
			
		||||
@ -77,16 +75,8 @@ _Append a Worksheet to a Workbook and find a unique name_
 | 
			
		||||
var new_name = XLSX.utils.book_append_sheet(workbook, worksheet, name, true);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If the fourth argument is `true`, the function will start with the specified
 | 
			
		||||
worksheet name.  If the sheet name exists in the workbook, a new worksheet name
 | 
			
		||||
will be chosen by finding the name stem and incrementing the counter:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
XLSX.utils.book_append_sheet(workbook, sheetA, "Sheet2", true); // Sheet2
 | 
			
		||||
XLSX.utils.book_append_sheet(workbook, sheetB, "Sheet2", true); // Sheet3
 | 
			
		||||
XLSX.utils.book_append_sheet(workbook, sheetC, "Sheet2", true); // Sheet4
 | 
			
		||||
XLSX.utils.book_append_sheet(workbook, sheetD, "Sheet2", true); // Sheet5
 | 
			
		||||
```
 | 
			
		||||
If the fourth argument is `true`, the function will try to find a new worksheet
 | 
			
		||||
name in case of a collision.
 | 
			
		||||
 | 
			
		||||
#### Examples
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -29,10 +29,10 @@ include options for specifying the date system
 | 
			
		||||
| XLSX (Strict ISO) |   ✔  |   ✔  |  ✔  | Relative Date        | 1900 + 1904 |
 | 
			
		||||
| XLSB              |   ✔  |   ✔  |  ✔  | Number               | 1900 + 1904 |
 | 
			
		||||
| XLML              |   ✔  |   ✔  |  ✔  | Relative Date        | 1900 + 1904 |
 | 
			
		||||
| XLS (BIFF5/8)     |   ✔  |   ✔  |  ✔  | Relative Date        | 1900 + 1904 |
 | 
			
		||||
| XLS (BIFF2/3/4)   |   ✔  |   ✔  |  ✔  | Relative Date        | 1900 + 1904 |
 | 
			
		||||
| XLR (Works)       |   ✔  |   ✔  |  ✔  | Relative Date        | 1900 + 1904 |
 | 
			
		||||
| ET (WPS 电子表格)  |   ✔  |   ✔  |  ✔  | Relative Date        | 1900 + 1904 |
 | 
			
		||||
| XLS (BIFF5/8)     |   ✔  |   ✔  |  ✔  | Number               | 1900 + 1904 |
 | 
			
		||||
| XLS (BIFF2/3/4)   |   ✔  |   ✔  |  ✔  | Number               | 1900 + 1904 |
 | 
			
		||||
| XLR (Works)       |   ✔  |   ✔  |  ✔  | Number               | 1900 + 1904 |
 | 
			
		||||
| ET (WPS 电子表格)  |   ✔  |   ✔  |  ✔  | Number               | 1900 + 1904 |
 | 
			
		||||
| ODS / FODS / UOS  |   ✔  |   ✔  |  ✔  | ISO Duration or Date | Arbitrary   |
 | 
			
		||||
| HTML              |   ✔  |   ✔  |  ✔  | Plaintext            | Calendar    |
 | 
			
		||||
| CSV / TSV / Text  |   ✔  |   ✔  |  ✔  | Plaintext            | Calendar    |
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,9 @@ sidebar_position: 6
 | 
			
		||||
<details>
 | 
			
		||||
  <summary><b>File Format Support</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
[Date and Time support](/docs/csf/features/dates) requires limited number format
 | 
			
		||||
support to distinguish date or time codes from standard numeric data.
 | 
			
		||||
 | 
			
		||||
Legacy formats like CSV mix "content" and "presentation". There is no true
 | 
			
		||||
concept of a "number format" distinct from the number itself. For specific
 | 
			
		||||
formats, the library will guess the number format.
 | 
			
		||||
@ -20,8 +23,14 @@ formats, the library will guess the number format.
 | 
			
		||||
| SYLK              |   R   | Number Format Code     |
 | 
			
		||||
| ODS / FODS / UOS  |   ✔   | XML                    |
 | 
			
		||||
| NUMBERS           |       | Binary encoding        |
 | 
			
		||||
| WK\*              |       | Binary encoding        |
 | 
			
		||||
| WQ\* / WB\* / QPW |       | Binary encoding        |
 | 
			
		||||
| WK1               |   +   | Fixed set of formats   |
 | 
			
		||||
| WK3 / WK4         |       | Binary encoding        |
 | 
			
		||||
| WKS Lotus         |   +   | Fixed set of formats   |
 | 
			
		||||
| WKS Works         |   +   | Fixed set of formats   |
 | 
			
		||||
| WQ1               |   +   | Fixed set of formats   |
 | 
			
		||||
| WQ2               |       | Binary encoding        |
 | 
			
		||||
| WB1 / WB2 / WB3   |       | Binary encoding        |
 | 
			
		||||
| QPW               |   +   | Binary encoding        |
 | 
			
		||||
| DBF               |       | Implied by field types |
 | 
			
		||||
| HTML              |   *   | Special override       |
 | 
			
		||||
| CSV               |   *   | N/A                    |
 | 
			
		||||
@ -29,6 +38,10 @@ formats, the library will guess the number format.
 | 
			
		||||
| DIF               |   *   | N/A                    |
 | 
			
		||||
| RTF               |   *   | N/A                    |
 | 
			
		||||
 | 
			
		||||
(+) mark formats with limited support. The QPW (Quattro Pro Workbooks) parser
 | 
			
		||||
supports the built-in date and built-in time formats but does not support
 | 
			
		||||
custom number formats.
 | 
			
		||||
 | 
			
		||||
Asterisks (*) mark formats that mix content and presentation. Writers will use
 | 
			
		||||
formatted values if cell objects include formatted text or number formats.
 | 
			
		||||
Parsers may guess number formats for special values.
 | 
			
		||||
 | 
			
		||||
@ -83,12 +83,12 @@ Strings can be interpreted in multiple ways.  The `type` parameter for `read`
 | 
			
		||||
tells the library how to parse the data argument:
 | 
			
		||||
 | 
			
		||||
| `type`     | expected input                                                  |
 | 
			
		||||
|------------|-----------------------------------------------------------------|
 | 
			
		||||
|:-----------|:----------------------------------------------------------------|
 | 
			
		||||
| `"base64"` | string: Base64 encoding of the file                             |
 | 
			
		||||
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`)        |
 | 
			
		||||
| `"string"` | string: JS string (only appropriate for UTF-8 text formats)     |
 | 
			
		||||
| `"buffer"` | nodejs Buffer                                                   |
 | 
			
		||||
| `"array"`  | array: array of 8-bit unsigned int (byte `n` is `data[n]`)      |
 | 
			
		||||
| `"array"`  | array: array of 8-bit unsigned integers (byte `n` is `data[n]`) |
 | 
			
		||||
| `"file"`   | string: path of file that will be read (nodejs only)            |
 | 
			
		||||
 | 
			
		||||
Some common types are automatically deduced from the data input type, including
 | 
			
		||||
 | 
			
		||||
@ -76,9 +76,12 @@ The write functions accept an options argument:
 | 
			
		||||
| :---------- | -------: | :------------------------------------------------- |
 | 
			
		||||
|`type`       |          | Output data encoding (see Output Type below)       |
 | 
			
		||||
|`cellDates`  |  `false` | Store dates as type `d` (default is `n`)           |
 | 
			
		||||
|`cellStyles` |  `false` | Save style/theme info to the `.s` field            |
 | 
			
		||||
|`codepage`   |          | If specified, use code page when appropriate **    |
 | 
			
		||||
|`bookSST`    |  `false` | Generate Shared String Table **                    |
 | 
			
		||||
|`bookType`   | `"xlsx"` | Type of Workbook (see below for supported formats) |
 | 
			
		||||
|`bookVBA`    |          | Add VBA blob from workbook object to the file **   |
 | 
			
		||||
|`WTF`        |  `false` | If true, throw errors on unexpected features **    |
 | 
			
		||||
|`sheet`      |     `""` | Name of Worksheet for single-sheet formats **      |
 | 
			
		||||
|`compression`|  `false` | Use ZIP compression for ZIP-based formats **       |
 | 
			
		||||
|`Props`      |          | Override workbook properties when writing **       |
 | 
			
		||||
@ -106,6 +109,9 @@ The write functions accept an options argument:
 | 
			
		||||
  files to ignore the error by default.  Set `ignoreEC` to `false` to suppress.
 | 
			
		||||
- `FS` and `RS` apply to CSV and Text output formats. The options are discussed
 | 
			
		||||
  in ["CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
 | 
			
		||||
- `bookVBA` only applies to supported formats. ["VBA"](/docs/csf/features/vba)
 | 
			
		||||
  section explains the feature in more detail.
 | 
			
		||||
- `WTF` is mainly for development.
 | 
			
		||||
 | 
			
		||||
<details open>
 | 
			
		||||
  <summary><b>Exporting NUMBERS files</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 5
 | 
			
		||||
sidebar_position: 1
 | 
			
		||||
title: Arrays of Data
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 5
 | 
			
		||||
sidebar_position: 3
 | 
			
		||||
title: HTML
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 7
 | 
			
		||||
sidebar_position: 5
 | 
			
		||||
title: CSV and Text
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 9
 | 
			
		||||
sidebar_position: 7
 | 
			
		||||
title: Array of Formulae
 | 
			
		||||
pagination_next: miscellany/formats
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
**Extract all formulae from a worksheet**
 | 
			
		||||
							
								
								
									
										51
									
								
								docz/docs/08-api/07-utilities/08-wb.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										51
									
								
								docz/docs/08-api/07-utilities/08-wb.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
---
 | 
			
		||||
sidebar_position: 8
 | 
			
		||||
title: Workbook Helpers
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Many utility functions return worksheet objects. Worksheets cannot be written to
 | 
			
		||||
workbook file formats directly.  They must be added to a workbook object.
 | 
			
		||||
 | 
			
		||||
**Create a new workbook**
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
var workbook = XLSX.utils.book_new();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `book_new` utility function creates an empty workbook with no worksheets.
 | 
			
		||||
 | 
			
		||||
Spreadsheet software generally require at least one worksheet and enforce the
 | 
			
		||||
requirement in the user interface. For example, if the last worksheet is deleted
 | 
			
		||||
in the program, Apple Numbers will automatically create a new blank sheet.
 | 
			
		||||
 | 
			
		||||
The SheetJS [write functions](/docs/api/write-options) enforce the requirement.
 | 
			
		||||
They will throw errors when trying to export empty worksheets.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
**Append a Worksheet to a Workbook**
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
XLSX.utils.book_append_sheet(workbook, worksheet, sheet_name);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `book_append_sheet` utility function appends a worksheet to the workbook.
 | 
			
		||||
The third argument specifies the desired worksheet name. Multiple worksheets can
 | 
			
		||||
be added to a workbook by calling the function multiple times.  If the worksheet
 | 
			
		||||
name is already used in the workbook, it will throw an error.
 | 
			
		||||
 | 
			
		||||
_Append a Worksheet to a Workbook and find a unique name_
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
var new_name = XLSX.utils.book_append_sheet(workbook, worksheet, name, true);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If the fourth argument is `true`, the function will start with the specified
 | 
			
		||||
worksheet name.  If the sheet name exists in the workbook, a new worksheet name
 | 
			
		||||
will be chosen by finding the name stem and incrementing the counter:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
XLSX.utils.book_append_sheet(workbook, sheetA, "Sheet2", true); // Sheet2
 | 
			
		||||
XLSX.utils.book_append_sheet(workbook, sheetB, "Sheet2", true); // Sheet3
 | 
			
		||||
XLSX.utils.book_append_sheet(workbook, sheetC, "Sheet2", true); // Sheet4
 | 
			
		||||
XLSX.utils.book_append_sheet(workbook, sheetD, "Sheet2", true); // Sheet5
 | 
			
		||||
```
 | 
			
		||||
@ -54,7 +54,7 @@ flowchart LR
 | 
			
		||||
var arr = XLSX.utils.sheet_to_json(ws, opts);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
[**This functions is described in a dedicated page**](/docs/api/utilities/array#array-of-objects-input)
 | 
			
		||||
[**This function is described in a dedicated page**](/docs/api/utilities/array#array-of-objects-input)
 | 
			
		||||
 | 
			
		||||
## Array of Arrays Input
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -102,13 +102,15 @@ _Exporting Formulae:_
 | 
			
		||||
 | 
			
		||||
- `sheet_to_formulae` generates a list of formulae or cell value assignments.
 | 
			
		||||
 | 
			
		||||
**[Utility Functions](/docs/api/utilities)**
 | 
			
		||||
**["Workbook Helpers" section of "Utility Functions"](/docs/api/utilities/wb)**
 | 
			
		||||
 | 
			
		||||
_Workbook Operations:_
 | 
			
		||||
 | 
			
		||||
- `book_new` creates an empty workbook
 | 
			
		||||
- `book_append_sheet` adds a worksheet to a workbook
 | 
			
		||||
 | 
			
		||||
**[Utility Functions](/docs/api/utilities)**
 | 
			
		||||
 | 
			
		||||
_Miscellaneous_
 | 
			
		||||
 | 
			
		||||
- `format_cell` generates the text value for a cell (using number formats).
 | 
			
		||||
 | 
			
		||||
@ -52,7 +52,7 @@ range limits will be silently truncated:
 | 
			
		||||
|:------------------------------------------|:-----------|---------:|---------:|
 | 
			
		||||
| Excel 2007+ XML Formats (XLSX/XLSM)       |`XFD1048576`|    16384 |  1048576 |
 | 
			
		||||
| Excel 2007+ Binary Format (XLSB BIFF12)   |`XFD1048576`|    16384 |  1048576 |
 | 
			
		||||
| Numbers 12.1 (NUMBERS)                    |`ALL1000000`|     1000 |  1000000 |
 | 
			
		||||
| Numbers 13.1 (NUMBERS)                    |`ALL1000000`|     1000 |  1000000 |
 | 
			
		||||
| Quattro Pro 9+ (QPW)                      |`IV1000000 `|      256 |  1000000 |
 | 
			
		||||
| Excel 97-2004 (XLS BIFF8)                 |`IV65536   `|      256 |    65536 |
 | 
			
		||||
| Excel 5.0/95 (XLS BIFF5)                  |`IV16384   `|      256 |    16384 |
 | 
			
		||||
@ -175,7 +175,7 @@ XLR also includes a `WksSSWorkBook` stream similar to Lotus FM3/FMT files.
 | 
			
		||||
 | 
			
		||||
iWork 2013 (Numbers 3.0 / Pages 5.0 / Keynote 6.0) switched from a proprietary
 | 
			
		||||
XML-based format to the current file format based on the iWork Archive (IWA).
 | 
			
		||||
This format has been used up through the current release (Numbers 12.1) as well
 | 
			
		||||
This format has been used up through the current release (Numbers 13.1) as well
 | 
			
		||||
as the iCloud.com web interface to Numbers.
 | 
			
		||||
 | 
			
		||||
The parser focuses on extracting raw data from tables.  Numbers technically
 | 
			
		||||
 | 
			
		||||
@ -6,4 +6,14 @@ hide_table_of_contents: true
 | 
			
		||||
 | 
			
		||||
The official source code repository is <https://git.sheetjs.com/sheetjs/sheetjs>
 | 
			
		||||
 | 
			
		||||
Issues should be raised at <https://git.sheetjs.com/sheetjs/sheetjs/issues>
 | 
			
		||||
Issues should be raised at <https://git.sheetjs.com/sheetjs/sheetjs/issues>
 | 
			
		||||
 | 
			
		||||
The official changelog can be found [in the source code repository](https://git.sheetjs.com/sheetjs/sheetjs/raw/branch/master/CHANGELOG.md)
 | 
			
		||||
 | 
			
		||||
:::tip pass
 | 
			
		||||
 | 
			
		||||
[Watch the repo](https://git.sheetjs.com/SheetJS/sheetjs) or subscribe to the
 | 
			
		||||
[RSS feed](https://git.sheetjs.com/sheetjs/sheetjs/tags.rss) to be notified when
 | 
			
		||||
new versions are released!
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
@ -275,28 +275,28 @@ git checkout -- .
 | 
			
		||||
### Reproduce official builds
 | 
			
		||||
 | 
			
		||||
4) Run `git log` and search for the commit that matches a particular release
 | 
			
		||||
version.  For example, version `0.19.3` can be found with:
 | 
			
		||||
version.  For example, version `0.20.0` can be found with:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
git log | grep -B4 "version bump 0.19.3"
 | 
			
		||||
git log | grep -B4 "version bump 0.20.0"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The output should look like:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
$ git log | grep -B4 "version bump 0.19.3"
 | 
			
		||||
$ git log | grep -B4 "version bump 0.20.0"
 | 
			
		||||
# highlight-next-line
 | 
			
		||||
commit 333e4e40f9c5603bd22a811f54c61c20bc9e17ab <-- this is the commit hash
 | 
			
		||||
commit 955543147dac0274d20307057c5a9f3e3e5d5307 <-- this is the commit hash
 | 
			
		||||
Author: SheetJS <dev@sheetjs.com>
 | 
			
		||||
Date:   Mon Apr 17 23:39:28 2023 -0400
 | 
			
		||||
Date:   Fri Jun 23 05:48:47 2023 -0400
 | 
			
		||||
 | 
			
		||||
    version bump 0.19.3
 | 
			
		||||
    version bump 0.20.0
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
5) Switch to that commit:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
git checkout 333e4e40f9c5603bd22a811f54c61c20bc9e17ab
 | 
			
		||||
git checkout 955543147dac0274d20307057c5a9f3e3e5d5307
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
6) Run the full build sequence
 | 
			
		||||
@ -314,14 +314,14 @@ The local checksum for the browser script can be computed with:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
$ md5sum dist/xlsx.full.min.js
 | 
			
		||||
f5c73b5ddc4b431c909d11c2e1d7a8e0  dist/xlsx.full.min.js
 | 
			
		||||
0b2f539797f92d35c6394274818f2c22  dist/xlsx.full.min.js
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The checksum for the CDN version can be computed with:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
$ curl -L https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/xlsx.full.min.js | md5sum -
 | 
			
		||||
f5c73b5ddc4b431c909d11c2e1d7a8e0  -
 | 
			
		||||
$ curl -L https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js | md5sum -
 | 
			
		||||
0b2f539797f92d35c6394274818f2c22  -
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The two hashes should match.
 | 
			
		||||
 | 
			
		||||
@ -102,7 +102,7 @@ support for CSS styling and rich text.
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
### Download and Preview a Numbers workbook
 | 
			
		||||
### Download and Preview Apple Numbers Workbooks
 | 
			
		||||
 | 
			
		||||
<details><summary><b>How to add to your site</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
@ -141,7 +141,9 @@ support for CSS styling and rich text.
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
<details><summary><b>Live Example</b> (click to show)</summary>
 | 
			
		||||
<details open><summary><b>Live Example</b> (click to hide)</summary>
 | 
			
		||||
 | 
			
		||||
This demo processes <https://sheetjs.com/pres.numbers>
 | 
			
		||||
 | 
			
		||||
```jsx live
 | 
			
		||||
/* The live editor requires this function wrapper */
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
// @deno-types="https://cdn.sheetjs.com/xlsx-0.19.3/package/types/index.d.ts"
 | 
			
		||||
import { read, utils, set_cptable, version } from 'https://cdn.sheetjs.com/xlsx-0.19.3/package/xlsx.mjs';
 | 
			
		||||
import * as cptable from 'https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/cpexcel.full.mjs';
 | 
			
		||||
// @deno-types="https://cdn.sheetjs.com/xlsx-0.20.0/package/types/index.d.ts"
 | 
			
		||||
import { read, utils, set_cptable, version } from 'https://cdn.sheetjs.com/xlsx-0.20.0/package/xlsx.mjs';
 | 
			
		||||
import * as cptable from 'https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/cpexcel.full.mjs';
 | 
			
		||||
set_cptable(cptable);
 | 
			
		||||
 | 
			
		||||
import * as Drash from "https://deno.land/x/drash@v2.5.4/mod.ts";
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 | 
			
		||||
<meta name="robots" content="noindex">
 | 
			
		||||
<title>SheetJS Live Demo</title>
 | 
			
		||||
<style>
 | 
			
		||||
#drop{
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 204 KiB  | 
		Loading…
	
		Reference in New Issue
	
	Block a user