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