This commit is contained in:
SheetJS 2022-10-04 16:37:38 -04:00
parent de18b667b9
commit fa5ff21a8e
13 changed files with 253 additions and 205 deletions

@ -12,6 +12,7 @@ api
csf
# Excel-related terms
A1
A1-Style
AutoFilter
BIFF12

@ -10,6 +10,15 @@ import current from '/version.js';
# Deno
[Deno](https://deno.land/) is a JavaScript runtime powered by V8.
:::caution Deno support is considered experimental.
Great open source software grows with user tests and reports. Any issues should
be reported to the Deno project for further diagnosis.
:::
Each standalone release script is available at <https://cdn.sheetjs.com/>.
Using the URL imports, `deno run` will automatically download scripts and types:

@ -6,6 +6,88 @@ With the advent of server-side frameworks and content management systems, it is
possible to build sites whose source of truth is a spreadsheet! This demo
explores a number of approaches.
## Lume
[Lume](https://lume.land) is a static site generator for the Deno platform.
The official [Sheets plugin](https://lume.land/plugins/sheets/) uses SheetJS
to load data from spreadsheets.
### Lume Demo
:::note
This was tested against `lume v1.12.0` on 2022 October 4.
:::
<details><summary><b>Complete Example</b> (click to show)</summary>
1) Create a stock site:
```bash
mkdir sheetjs-lume
cd sheetjs-lume
deno run -A https://deno.land/x/lume/init.ts
```
When prompted, enter the following options:
- `Use TypeScript for the configuration file`: press Enter (use default `N`)
- `Do you want to use plugins`: type `sheets` and press Enter
The project will be configured and modules will be installed.
2) Download <https://sheetjs.com/pres.numbers> and place in a `_data` folder:
```bash
mkdir _data
curl -LO https://sheetjs.com/pres.numbers
mv pres.numbers _data
```
3) Create a `index.njk` file that references the file. Since the file is
`pres.numbers`, the parameter name is `pres`:
```liquid title="index.njk"
<h2>Presidents</h2>
<table><thead><th>Name</th><th>Index</th></thead>
<tbody>
{% for row in pres %}{% if (loop.index >= 1) %}
<tr>
<td>{{ row.Name }}</td>
<td>{{ row.Index }}</td>
</tr>
{% endif %}{% endfor %}
</tbody>
</table>
```
4) Run the development server:
```bash
deno task lume --serve
```
To verify it works, access http://localhost:3000 from your web browser.
Adding a new row and saving `pres.numbers` should refresh the data
5) Stop the server (press `CTRL+C` in the terminal window) and run
```bash
deno task lume
```
This will create a static site in the `_site` folder, which can be served with:
```bash
npx http-server _site
```
Accessing the page http://localhost:8080 will show the page contents.
</details>
## GatsbyJS
[`gatsby-transformer-excel`](https://www.gatsbyjs.com/plugins/gatsby-transformer-excel/)
@ -513,160 +595,3 @@ the static nature is trivial: make another change in Excel and save. The page
will not change.
</details>
## Lume
Lume is a static site generator for the Deno platform.
`lume#loadData` can add custom loaders for data. The loader method receives a
path to the file, which can be read with `XLSX.readFile`. This should be added
to `_config.js`, like in the example below:
```js title="_config.js"
import lume from "lume/mod.ts";
import { readFile, utils } from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs';
function wbLoader(path) {
const wb = readFile(path);
const res = wb.SheetNames.map(n => ({
name: n,
data: utils.sheet_to_json(wb.Sheets[n])
}));
return { content: res };
}
const site = lume();
const exts = [".xlsx", ".numbers", /* ... other supported extensions */];
// highlight-next-line
site.loadData(exts, wbLoader);
export default site;
```
The actual spreadsheets should be placed in the `_data` subfolder.
The variable name is the stem of the filename (`sheetjs` if `sheetjs.xlsx` or
`sheetjs.numbers` exists). A Nunjucks or JSX template can loop through the
worksheets and the data rows. The example assumes each worksheet has a `name` and `index` column:
```jsx title="index.jsx"
export default ({sheetjs}) => {
return (<>{(sheetjs).map(sheet => (<>
<h2>{sheet.name}</h2>
<table><thead><th>Name</th><th>Index</th></thead>
<tbody>{sheet.data.map(row => (<tr>
<td>{row.name}</td>
<td>{row.index}</td>
</tr>))}</tbody>
</table>
</>))}</>);
};
```
### Lume Demo
<details><summary><b>Complete Example</b> (click to show)</summary>
:::note
This was tested against `lume v1.10.4` on 2022 August 25.
:::
1) Create a stock site:
```bash
mkdir sheetjs-lume
cd sheetjs-lume
deno run -A https://deno.land/x/lume/init.ts
```
When prompted, enter the following options:
- `Use TypeScript for the configuration file`: press Enter (use default `N`)
- `Do you want to use plugins`: type `jsx` and press Enter
The project will be configured and modules will be installed.
2) Make the following highlighted changes to `_config.js`:
```js title="_config.js"
import lume from "lume/mod.ts";
import jsx from "lume/plugins/jsx.ts";
// highlight-start
import { readFile, utils } from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs';
function wbLoader(path) {
const wb = readFile(path);
const res = wb.SheetNames.map(n => ({
name: n,
data: utils.sheet_to_json(wb.Sheets[n])
}));
return { content: res };
}
// highlight-end
const site = lume();
site.use(jsx());
// highlight-start
const exts = [".xlsx", ".numbers", /* ... other supported extensions */];
site.loadData(exts, wbLoader);
// highlight-end
export default site;
```
This instructs Lume to watch for and load `.xlsx` and `.numbers` spreadsheets
3) Download <https://sheetjs.com/pres.numbers> and place in a `_data` folder:
```bash
mkdir _data
curl -LO https://sheetjs.com/pres.numbers
mv pres.numbers _data
```
4) Create a `index.jsx` file that references the file. Since the file is
`pres.numbers`, the parameter name is `pres`:
```jsx title="index.jsx"
export default ({pres}) => {
return (<>{(pres).map(sheet => (<>
<h2>{sheet.name}</h2>
<table><thead><th>Name</th><th>Index</th></thead>
<tbody>{sheet.data.map(row => (<tr>
<td>{row.Name}</td>
<td>{row.Index}</td>
</tr>))}</tbody>
</table>
</>))}</>);
};
```
5) Run the development server:
```bash
deno task lume --serve
```
To verify it works, access http://localhost:3000 from your web browser.
Adding a new row and saving `pres.numbers` should refresh the data
6) Stop the server (press `CTRL+C` in the terminal window) and run
```bash
deno task lume
```
This will create a static site in the `_site` folder, which can be served with:
```bash
npx http-server _serve
```
Accessing the page http://localhost:8080 will show the page contents.
</details>

@ -219,7 +219,7 @@ includes an example of constructing a simple array.
function export_pouchdb_to_xlsx(db) {
/* fetch all rows, including the underlying data */
db.allDocs({include_docs: true}, function(err, doc) {
/* pull the individual data rows */
const aoo = doc.rows.map(r => {
/* `rest` will include every field from `r` except for _id and _rev */

@ -890,4 +890,4 @@ rd.resume();
</Tabs>
<https://github.com/sheetjs/sheetaki> pipes write streams to nodejs response.
<https://sheetaki.now.sh/> pipes write streams to nodejs response.

@ -113,6 +113,7 @@ In addition to the aforementioned sheet keys, worksheets also add:
| `pivotTables` | Use PivotTable reports | disabled |
| `objects` | Edit objects | enabled |
| `scenarios` | Edit scenarios | enabled |
</details>
- `ws['!autofilter']`: AutoFilter object following the schema:

@ -16,14 +16,17 @@ while the writer will translate from A1-Style strings to the file format.
|:------------------|:-----:|:-----:|:-----:|:-------:|:-----------------------|
| XLSX / XLSM | ✔ | ✔ | ✔ | ✔ | A1-Style strings |
| XLSB | ✔ | | ✔ | ✔ | BIFF parsed tokens |
| XLS | ✔ | | ✔ | | BIFF parsed tokens |
| XLML | ✔ | ✔ | ✔ | | RC-style strings |
| SYLK | ✔ | ✔ | | | `A1`/RC-style strings |
| CSV / TXT | ✔ | ✔ | | | A1-Style strings |
| ODS / FODS / UOS | ✔ | ✔ | | | OpenFormula strings |
| WK\* | ✔ | | | | Lotus parsed tokens |
| WQ\* / WB\* / QPW | | | | | Quattro Pro tokens |
| NUMBERS | | | | | Numbers parsed tokens |
| XLS | ✔ | | ✔ | * | BIFF parsed tokens |
| XLML | ✔ | ✔ | ✔ | * | RC-style strings |
| SYLK | ✔ | ✔ | | * | A1/RC-style strings |
| CSV / TXT | ✔ | ✔ | * | * | A1-Style strings |
| ODS / FODS / UOS | ✔ | ✔ | | * | OpenFormula strings |
| WK\* | ✔ | | | * | Lotus parsed tokens |
| WQ\* / WB\* / QPW | | | | * | Quattro Pro tokens |
| NUMBERS | | | | * | Numbers parsed tokens |
Asterisks (*) mark features that are not supported by the file formats. There is
no way to mark a dynamic array formula in the XLS file format.
</details>

@ -24,34 +24,32 @@ For example, the following snippet creates a link from cell `A3` to
ws["A1"].l = { Target: "https://sheetjs.com", Tooltip: "Find us @ SheetJS.com!" };
```
Note that Excel does not automatically style hyperlinks. They will be displayed
using default style. <a href="https://sheetjs.com/pro">SheetJS Pro Basic</a>
:::note
Excel does not automatically style hyperlinks. They will be displayed using
the default cell style. <a href="https://sheetjs.com/pro">SheetJS Pro Basic</a>
extends this export with support for hyperlink styling.
<details><summary><b>Live Example</b> (click to show)</summary>
:::
<details open><summary><b>Live Example</b> (click to hide)</summary>
```jsx live
/* The live editor requires this function wrapper */
function ExportSimpleLink(props) {
function ExportSimpleLink(props) { return ( <button onClick={() => {
/* Create worksheet */
var ws = XLSX.utils.aoa_to_sheet([ [ "Link", "No Link" ] ]);
/* Add link */
ws["A1"].l = {
Target: "https://sheetjs.com",
Tooltip: "Find us @ SheetJS.com!"
};
/* Callback invoked when the button is clicked */
const xport = React.useCallback(() => {
/* Create worksheet */
var ws = XLSX.utils.aoa_to_sheet([ [ "Link", "No Link" ] ]);
/* Add link */
ws["A1"].l = {
Target: "https://sheetjs.com",
Tooltip: "Find us @ SheetJS.com!"
};
/* Export to file (start a download) */
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSSimpleLink.xlsx");
});
return (<button onClick={xport}><b>Export XLSX!</b></button>);
}
/* Export to file (start a download) */
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSSimpleLink.xlsx");
}}><b>Export XLSX!</b></button> ); }
```
</details>
@ -72,6 +70,40 @@ ws["A4"].l = { Target: "mailto:ignored@dev.null" };
ws["A5"].l = { Target: "mailto:ignored@dev.null?subject=Test Subject" };
```
<details><summary><b>Live Example</b> (click to show)</summary>
**This demo creates a XLSX spreadsheet with a `mailto` email link. The email
address input in the form never leaves your machine.**
```jsx live
/* The live editor requires this function wrapper */
function ExportRemoteLink(props) {
const [email, setEmail] = React.useState("ignored@dev.null");
const set_email = React.useCallback((evt) => setEmail(evt.target.value));
/* Callback invoked when the button is clicked */
const xport = React.useCallback(() => {
/* Create worksheet */
var ws = XLSX.utils.aoa_to_sheet([ [ "HTTPS", "mailto" ] ]);
/* Add links */
ws["A1"].l = { Target: "https://sheetjs.com" };
ws["B1"].l = { Target: `mailto:${email}` };
/* Export to file (start a download) */
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
XLSX.writeFile(wb, "SheetJSRemoteLink.xlsx");
});
return (<>
<b>Email: </b><input type="text" value={email} onChange={set_email} size="50"/>
<br/><button onClick={xport}><b>Export XLSX!</b></button>
</>);
}
```
</details>
## Local Links
Links to absolute paths should use the `file://` URI scheme:
@ -103,13 +135,55 @@ Links where the target is a cell or range or defined name in the same workbook
```js
ws["C1"].l = { Target: "#E2" }; /* Link to cell E2 */
ws["C2"].l = { Target: "#Sheet2!E2" }; /* Link to cell E2 in sheet Sheet2 */
ws["C3"].l = { Target: "#SomeDefinedName" }; /* Link to Defined Name */
ws["C3"].l = { Target: "#SheetJSDName" }; /* Link to Defined Name */
```
<details><summary><b>Live Example</b> (click to show)</summary>
```jsx live
/* The live editor requires this function wrapper */
function ExportInternalLink(props) { return ( <button onClick={() => {
/* Create empty workbook */
var wb = XLSX.utils.book_new();
/* Create worksheet */
var ws = XLSX.utils.aoa_to_sheet([ [ "Same", "Cross", "Name" ] ]);
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
/* Create links */
ws["A1"].l = { Target: "#B2:D4", Tooltip: "Same-Sheet" };
ws["B1"].l = { Target: "#Sheet2!B2:D4", Tooltip: "Cross-Sheet" };
ws["C1"].l = { Target: "#SheetJSDN", Tooltip: "Defined Name" };
/* Create stub Sheet2 */
var ws2 = XLSX.utils.aoa_to_sheet([["This is Sheet2"]]);
XLSX.utils.book_append_sheet(wb, ws2, "Sheet2");
/* Create defined name */
wb.Workbook = {
Names: [{Name: "SheetJSDN", Ref:"Sheet2!A1:B2"}]
}
/* Export to file (start a download) */
XLSX.writeFile(wb, "SheetJSInternalLink.xlsx");
}}><b>Export XLSX!</b></button> ); }
```
</details>
:::caution
Some third-party tools like Google Sheets do not correctly parse hyperlinks in
XLSX documents. A workaround was added in library version 0.18.12.
:::
## HTML
The HTML DOM parser will process `<a>` links in the table:
<details open><summary><b>Live Example</b> (click to hide)</summary>
```jsx live
/* The live editor requires this function wrapper */
function ExportHyperlink(props) {
@ -134,3 +208,5 @@ function ExportHyperlink(props) {
</>);
}
```
</details>

@ -11,12 +11,11 @@ are expected to serialize SheetJS workbooks in the underlying file format.
The following topics are covered in sub-pages:
<ul>{useCurrentSidebarCategory().items.map((item, index) => {
const listyle = (item.customProps?.icon) ? {
listStyleImage: `url("${item.customProps.icon}")`
} : {};
return (<li style={listyle} {...(item.customProps?.class ? {className: item.customProps.class}: {})}>
<a href={item.href}>{item.label}</a>{item.customProps?.summary && (" - " + item.customProps.summary)}
</li>);
const cP = item.customProps;
const listyle = (cP?.icon) ? { listStyleImage: `url("${cP.icon}")` } : {};
return ( <li style={listyle} {...(cP?.class ? {className: cP.class}: {})}>
<a href={item.href}>{item.label}</a>{cP?.summary && (" - " + cP.summary)}
</li> );
})}</ul>
## Row and Column Properties
@ -340,9 +339,9 @@ var vbablob = wb.vbaraw;
#### Code Names
By default, Excel will use `ThisWorkbook` or a translation `DieseArbeitsmappe`
for the workbook. Each worksheet will be identified using the default `Sheet#`
naming pattern even if the worksheet names have changed.
Excel will use `ThisWorkbook` (or a translation like `DieseArbeitsmappe`) as the
default Code Name for the workbook. Each worksheet will be identified using the
default `Sheet#` naming pattern even if the worksheet names have changed.
A custom workbook code name will be stored in `wb.Workbook.WBProps.CodeName`.
For exports, assigning the property will override the default value.
@ -374,4 +373,3 @@ function wb_has_macro(wb/*:workbook*/)/*:boolean*/ {
return sheets.some((ws) => !!ws && ws['!type']=='macro');
}
```

@ -5,8 +5,8 @@ hide_table_of_contents: true
---
Here are some common errors and their resolutions. This is not comprehensive.
The [issue tracker](https://github.com/SheetJS/sheetjs/issues) has a wealth of
information and user-contributed examples.
The [issue tracker](https://git.sheetjs.com/SheetJS/sheetjs/issues) has a
wealth of information and user-contributed examples.
If issues are not covered in the docs or the issue tracker, or if a solution is
not discussed in the documentation, we would appreciate a bug report.

@ -0,0 +1,12 @@
---
sidebar_position: 3
title: Source Code
hide_table_of_contents: true
---
The official source code repository is <https://git.sheetjs.com/sheetjs/sheetjs>
Mirrors:
- [GitHub](https://github.com/sheetjs/sheetjs)
- [GitLab](https://gitlab.com/sheetjs/sheetjs)

@ -8,7 +8,30 @@ hide_table_of_contents: true
SheetJS Community Edition is licensed under the "Apache 2.0 License". All rights
not explicitly granted by the Apache 2.0 License are reserved by SheetJS LLC.
<details><summary><b>License</b> (click to show)</summary>
#### Required Attribution
When integrating SheetJS CE in a project or service, the following text must be
added to open source disclosures:
```
SheetJS Community Edition -- https://sheetjs.com/
Copyright (C) 2012-present SheetJS LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
<details><summary><b>Complete License Text</b> (click to show)</summary>
```
Apache License

@ -80,7 +80,7 @@ const config = {
position: 'right',
},
{
href: 'https://github.com/sheetjs/sheetjs',
href: 'https://docs.sheetjs.com/docs/miscellany/source/',
label: 'Source',
position: 'right',
},
@ -132,7 +132,7 @@ const config = {
},
{
label: 'Source',
href: 'https://github.com/sheetjs/sheetjs',
href: 'https://docs.sheetjs.com/docs/miscellany/source/',
},
],
},