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