Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85b6afe901 | ||
|
|
be1192f0d3 | ||
|
|
46523eb75a | ||
|
|
7f64cfa3c4 | ||
|
|
065a7d8cac | ||
|
|
0821d57f5a | ||
|
|
6153655888 | ||
|
|
5a8d89bfba | ||
|
|
65ec4fb2e6 | ||
|
|
1aa1226316 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,5 +4,3 @@ package-lock.json
|
||||
pnpm-lock.yaml
|
||||
/docs
|
||||
node_modules
|
||||
.idea
|
||||
.vscode
|
||||
83
.spelling
83
.spelling
@ -20,10 +20,7 @@ sql
|
||||
# Excel-related terms
|
||||
A1
|
||||
A1-Style
|
||||
A2
|
||||
A7
|
||||
AutoFilter
|
||||
B7
|
||||
BIFF12
|
||||
BIFF2
|
||||
BIFF3
|
||||
@ -44,17 +41,14 @@ FM3
|
||||
FMT
|
||||
FODS
|
||||
FoxPro
|
||||
Gmail
|
||||
IEEE754
|
||||
JSON
|
||||
Macrosheet
|
||||
Macrosheets
|
||||
Multiplan
|
||||
NodeMailer
|
||||
ODF
|
||||
ODS
|
||||
OData
|
||||
ORM
|
||||
OpenDocument
|
||||
OpenFormula
|
||||
PRN
|
||||
@ -115,7 +109,6 @@ tooltips
|
||||
# Other terms
|
||||
1.x
|
||||
2.x
|
||||
2FA
|
||||
3.x
|
||||
4.x
|
||||
5.x
|
||||
@ -123,27 +116,21 @@ tooltips
|
||||
7.x
|
||||
8.x
|
||||
9.x
|
||||
AArch64
|
||||
APIs
|
||||
APK
|
||||
ARM64
|
||||
ActiveX
|
||||
Airtable
|
||||
AlaSQL
|
||||
AngularJS
|
||||
AppleScript
|
||||
ArrayBuffer
|
||||
AstroJS
|
||||
Auth
|
||||
BOM
|
||||
Base64
|
||||
Base64-encoded
|
||||
Big5
|
||||
BitBucket
|
||||
Blazor
|
||||
Booleans
|
||||
Browserify
|
||||
BunJS
|
||||
Bundlers
|
||||
CDN
|
||||
CEP
|
||||
@ -151,105 +138,72 @@ CLI
|
||||
CMS
|
||||
CORS
|
||||
CPAN
|
||||
CRA
|
||||
CRX
|
||||
CS6
|
||||
CTRL
|
||||
CapacitorJS
|
||||
Chakra
|
||||
ChakraCore
|
||||
CheerioJS
|
||||
ClearScript
|
||||
CocoaPods
|
||||
CommonJS
|
||||
Cordova
|
||||
DOM
|
||||
DPI
|
||||
DanfoJS
|
||||
DataFrame
|
||||
DataGrid
|
||||
Deno
|
||||
DenoDOM
|
||||
DexieJS
|
||||
Dojo
|
||||
Downloadify
|
||||
Drash
|
||||
Duktape
|
||||
ERP
|
||||
ES3
|
||||
ES5
|
||||
ES6
|
||||
ESBuild
|
||||
ESM
|
||||
ETH
|
||||
Eleventy
|
||||
ElysiaJS
|
||||
Endian
|
||||
Ethercalc
|
||||
ExecJS
|
||||
ExpressJS
|
||||
ExtendScript
|
||||
FastifyJS
|
||||
FerretDB
|
||||
Fastify
|
||||
FileReader
|
||||
FileReaderSync
|
||||
FileSaver
|
||||
GBK
|
||||
GTX
|
||||
GatsbyJS
|
||||
Ghidra
|
||||
GitHub
|
||||
GitLab
|
||||
Goja
|
||||
GraalJS
|
||||
Gradle
|
||||
GraphQL
|
||||
GraphiQL
|
||||
HTML
|
||||
HTML5
|
||||
HTTP
|
||||
HTTPS
|
||||
HappyDOM
|
||||
Homebrew
|
||||
HonoJS
|
||||
IANA
|
||||
IE
|
||||
IE10
|
||||
IE11
|
||||
IE6
|
||||
IE8
|
||||
IE9
|
||||
IMAP
|
||||
InDesign
|
||||
IndexedDB
|
||||
Integrations
|
||||
JDK
|
||||
JE
|
||||
JS
|
||||
JSC
|
||||
JSDOM
|
||||
JSX
|
||||
JWT
|
||||
JavaScriptCore
|
||||
Javet
|
||||
JerryScript
|
||||
Jint
|
||||
Kaioken
|
||||
Kaioponent
|
||||
Kaioponents
|
||||
KeyDB
|
||||
KnexJS
|
||||
Knex
|
||||
KnockoutJS
|
||||
LLC
|
||||
LTS
|
||||
LWC
|
||||
LangChain
|
||||
LangChainJS
|
||||
Lifecycle
|
||||
LocalStorage
|
||||
LowDB
|
||||
Lume
|
||||
MUI
|
||||
MVC
|
||||
MVVM
|
||||
MacOS
|
||||
@ -257,9 +211,7 @@ MariaDB
|
||||
Mathematica
|
||||
Meridiem
|
||||
MongoDB
|
||||
MuJS
|
||||
MySQL
|
||||
NASM
|
||||
NPM
|
||||
NW.js
|
||||
Nashorn
|
||||
@ -267,7 +219,6 @@ NativeScript
|
||||
NestJS
|
||||
NetSuite
|
||||
NeutralinoJS
|
||||
Nexe
|
||||
NextJS
|
||||
NoSQL
|
||||
NodeJS
|
||||
@ -276,19 +227,14 @@ Nunjucks
|
||||
Nuxt
|
||||
NuxtJS
|
||||
OSA
|
||||
OpenSSL
|
||||
OpenJDK
|
||||
PPI
|
||||
ParcelJS
|
||||
PhantomJS
|
||||
PhoneGap
|
||||
Photoshop
|
||||
Polars
|
||||
PostgreSQL
|
||||
PouchDB
|
||||
PowerShell
|
||||
Preact
|
||||
PreactJS
|
||||
QuickJS
|
||||
R1
|
||||
R2
|
||||
@ -298,23 +244,15 @@ RDBMS
|
||||
README
|
||||
RESTlets
|
||||
RSS
|
||||
RTX
|
||||
ReactJS
|
||||
Redis
|
||||
RequireJS
|
||||
RhinoJS
|
||||
Roadmap
|
||||
Rollup
|
||||
RollupJS
|
||||
Ryzen
|
||||
S3
|
||||
SDK
|
||||
SMS
|
||||
SMTP
|
||||
SQLite
|
||||
SSG
|
||||
SSL
|
||||
SSR
|
||||
SWC
|
||||
SWF
|
||||
Schemas
|
||||
@ -323,38 +261,28 @@ SessionStorage
|
||||
Shift-JIS
|
||||
SlimerJS
|
||||
Snowpack
|
||||
Stata
|
||||
SuiteScript
|
||||
SuiteScripts
|
||||
Suitelets
|
||||
SvelteJS
|
||||
SvelteKit
|
||||
SystemJS
|
||||
Tauri
|
||||
Temurin
|
||||
TensorFlow
|
||||
UI
|
||||
UI5
|
||||
UNPKG
|
||||
URI
|
||||
UTF-16
|
||||
UTF-8
|
||||
UUID
|
||||
UXP
|
||||
Uint8Array
|
||||
V2
|
||||
V8
|
||||
VBScript
|
||||
VRAM
|
||||
VSCodium
|
||||
Valkey
|
||||
Vendoring
|
||||
Vercel
|
||||
Vite
|
||||
ViteJS
|
||||
VueJS
|
||||
VueJS-friendly
|
||||
WASM
|
||||
WMR
|
||||
WSL
|
||||
WebAssembly
|
||||
@ -363,9 +291,7 @@ WebKit
|
||||
WebSQL
|
||||
Webpack
|
||||
Win10
|
||||
Win11
|
||||
XHR
|
||||
XMLDOM
|
||||
XMLHttpRequest
|
||||
XP
|
||||
Xcode
|
||||
@ -394,9 +320,7 @@ frontmatter
|
||||
globals
|
||||
iOS
|
||||
iWork
|
||||
jQuery
|
||||
javascript
|
||||
jujutsu
|
||||
lifecycle
|
||||
localForage
|
||||
macOS
|
||||
@ -405,13 +329,11 @@ microcontrollers
|
||||
middleware
|
||||
minified
|
||||
minifier
|
||||
mitigations
|
||||
namespace
|
||||
natively
|
||||
nodejs
|
||||
npm
|
||||
parsers
|
||||
polychotomous
|
||||
pre-built
|
||||
pre-generated
|
||||
prepend
|
||||
@ -432,7 +354,6 @@ transpile
|
||||
transpiled
|
||||
transpiling
|
||||
uncheck
|
||||
uncomment
|
||||
unidimensional
|
||||
unminified
|
||||
unpkg
|
||||
|
||||
1
CNAME
Normal file
1
CNAME
Normal file
@ -0,0 +1 @@
|
||||
docs.sheetjs.com
|
||||
3
Makefile
3
Makefile
@ -4,6 +4,7 @@ build:
|
||||
cd docz; npx -y pnpm build; cd ..
|
||||
rm -rf docs
|
||||
mv docz/build/ docs
|
||||
cp CNAME docs
|
||||
cp _headers docs
|
||||
|
||||
.PHONY: init
|
||||
@ -12,7 +13,7 @@ init:
|
||||
|
||||
.PHONY: dev
|
||||
dev:
|
||||
cd docz; npm run start -- --host=0.0.0.0 --port 6996 --no-open; cd ..
|
||||
cd docz; npm run start -- --host=0.0.0.0; cd ..
|
||||
|
||||
.PHONY: serve
|
||||
serve:
|
||||
|
||||
81
README.md
81
README.md
@ -16,80 +16,11 @@ $ make init # install dependencies
|
||||
$ make # build static site
|
||||
$ make serve # serve static site
|
||||
|
||||
$ make dev # run dev server on port 6996
|
||||
$ make dev # run dev server
|
||||
$ make spell # spell check (.spelling custom dictionary)
|
||||
$ make graph # build format graph and legend
|
||||
```
|
||||
|
||||
### Documentation Markup
|
||||
|
||||
The original documentation used [GFM](https://github.github.com/gfm/).
|
||||
|
||||
Pages currently use MDX v2.
|
||||
|
||||
<details>
|
||||
<summary><b>MDX Notes</b> (click to show)</summary>
|
||||
|
||||
**Multi-line tags**
|
||||
|
||||
Markdown and MDX v1 accept the following:
|
||||
|
||||
```
|
||||
<details><summary><b>MDX Notes</b> (click to show)</summary>
|
||||
|
||||
Note
|
||||
|
||||
</details>
|
||||
```
|
||||
|
||||
This is no longer valid in MDX v2. The `<summary>` part must be separated:
|
||||
|
||||
```
|
||||
<details>
|
||||
<summary><b>MDX Notes</b> (click to show)</summary>
|
||||
|
||||
Note
|
||||
|
||||
</details>
|
||||
```
|
||||
|
||||
**Shortlinks**
|
||||
|
||||
Markdown and MDX v1 support shortlinks:
|
||||
|
||||
```
|
||||
Scripts are available at <https://cdn.sheetjs.com>
|
||||
```
|
||||
|
||||
This is no longer valid in MDX v2. Autolinks should be used:
|
||||
|
||||
```
|
||||
Scripts are available at https://cdn.sheetjs.com
|
||||
```
|
||||
|
||||
**Variables**
|
||||
|
||||
Patterns such as
|
||||
|
||||
```
|
||||
<a href={`Foo${current}`}>Foo{current}</a>
|
||||
```
|
||||
|
||||
do not work in MDX v2. Instead, string literals and concatenation must be used:
|
||||
|
||||
```
|
||||
<a href={"Foo" + current + ""}>{"Foo" + current + ""}</a>
|
||||
```
|
||||
|
||||
**Tables**
|
||||
|
||||
MDX inconsistently requires different indentation levels for `TD` / `TH`, `TR`,
|
||||
`THEAD` / `TBODY` / `TFOOT`, and `TABLE` tags. Unconventional indentation is
|
||||
intentional.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Engine Compatibility Tables
|
||||
|
||||
`docz/src/data/engines.xls` is an XLML workbook that controls the compatibility
|
||||
@ -99,7 +30,7 @@ tables in <https://docs.sheetjs.com/docs/demos/engines/>. The component script
|
||||
### Formats Graph
|
||||
|
||||
The formats graph and legend are written in the DOT language. Rebuilding the
|
||||
graphs will require Graphviz and the "Indie Flower" font.
|
||||
graphs will require Graphviz (`brew install graphviz` on macOS)
|
||||
|
||||
## Live Demos
|
||||
|
||||
@ -139,12 +70,8 @@ function SheetJSTestDropbox() {
|
||||
|
||||
## Other Notes
|
||||
|
||||
`static/shim.js` shims the following functions:
|
||||
|
||||
- `Object.hasOwn`
|
||||
|
||||
`src/theme/Admonition` was swizzled from 3.2.1 to enable `pass` for hiding
|
||||
`src/theme/Admonition` was swizzled from 2.4.1 to enable `pass` for hiding
|
||||
header text. See Docusaurus issue 8568 for more details.
|
||||
|
||||
`src/theme/prism-include-languages.js` was swizzled from 3.2.1 to support the
|
||||
`src/theme/prism-include-languages.js` was swizzled from 2.4.1 to support the
|
||||
Liquid language. See Docusaurus issue 6872 for more details.
|
||||
3
docz/README.md
Normal file
3
docz/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# docs.sheetjs.com
|
||||
|
||||
<https://docs.sheetjs.com/>
|
||||
@ -1,13 +0,0 @@
|
||||
import url from './engines.xls';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
const BindingData = () => {
|
||||
const [binding, setBinding] = useState("");
|
||||
|
||||
useEffect(() => { (async() => {
|
||||
const html = await (await fetch(url)).json();
|
||||
setBinding(html["Bindings"]);
|
||||
})(); }, []);
|
||||
return ( <p dangerouslySetInnerHTML={{__html: binding}}/> );
|
||||
};
|
||||
export default BindingData;
|
||||
@ -1,13 +0,0 @@
|
||||
import url from './cli.xls';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
const FrameworkData = () => {
|
||||
const [fw, setFW] = useState("");
|
||||
|
||||
useEffect(() => { (async() => {
|
||||
const html = await (await fetch(url)).json();
|
||||
setFW(html["Frameworks"]);
|
||||
})(); }, []);
|
||||
return ( <p dangerouslySetInnerHTML={{__html: fw}}/> );
|
||||
};
|
||||
export default FrameworkData;
|
||||
@ -1,133 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<?mso-application progid="Excel.Sheet"?>
|
||||
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
|
||||
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
|
||||
<WindowHeight>10620</WindowHeight>
|
||||
<WindowWidth>11020</WindowWidth>
|
||||
<WindowTopX>2260</WindowTopX>
|
||||
<WindowTopY>19600</WindowTopY>
|
||||
<ActiveSheet>1</ActiveSheet>
|
||||
<ProtectStructure>False</ProtectStructure>
|
||||
<ProtectWindows>False</ProtectWindows>
|
||||
</ExcelWorkbook>
|
||||
<Styles>
|
||||
<Style ss:ID="Default" ss:Name="Normal">
|
||||
<Alignment ss:Vertical="Bottom"/>
|
||||
<Borders/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
|
||||
<Interior/>
|
||||
<NumberFormat/>
|
||||
<Protection/>
|
||||
</Style>
|
||||
<Style ss:ID="s16">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
|
||||
</Style>
|
||||
<Style ss:ID="s17">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
|
||||
</Style>
|
||||
<Style ss:ID="s19">
|
||||
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
|
||||
</Style>
|
||||
<Style ss:ID="s20">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#467886" ss:Underline="Single"/>
|
||||
</Style>
|
||||
</Styles>
|
||||
<Worksheet ss:Name="Frameworks">
|
||||
<Table ss:ExpandedColumnCount="7" ss:ExpandedRowCount="11" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Column ss:Index="2" ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">MacOS</Data></Cell>
|
||||
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Windows</Data></Cell>
|
||||
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Linux</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">Framework</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/nexe"><Data ss:Type="String">nexe</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/boxednode"><Data ss:Type="String">boxednode</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/pkg"><Data ss:Type="String">pkg</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/nodesea#complete-example"><Data ss:Type="String">NodeJS SEA</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/bunsea#complete-example"><Data ss:Type="String">BunJS SEA</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/denosea#complete-example"><Data ss:Type="String">Deno SEA</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/cli/txiki"><Data ss:Type="String">txiki.js</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#snapshots"><Data ss:Type="String">V8 Engine</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
</Table>
|
||||
</Worksheet>
|
||||
</Workbook>
|
||||
@ -1,13 +0,0 @@
|
||||
import url from './desktop.xls';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
const FrameworkData = () => {
|
||||
const [fw, setFW] = useState("");
|
||||
|
||||
useEffect(() => { (async() => {
|
||||
const html = await (await fetch(url)).json();
|
||||
setFW(html["Frameworks"]);
|
||||
})(); }, []);
|
||||
return ( <p dangerouslySetInnerHTML={{__html: fw}}/> );
|
||||
};
|
||||
export default FrameworkData;
|
||||
@ -1,115 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<?mso-application progid="Excel.Sheet"?>
|
||||
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
|
||||
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
|
||||
<WindowHeight>10620</WindowHeight>
|
||||
<WindowWidth>11020</WindowWidth>
|
||||
<WindowTopX>2260</WindowTopX>
|
||||
<WindowTopY>19600</WindowTopY>
|
||||
<ActiveSheet>1</ActiveSheet>
|
||||
<ProtectStructure>False</ProtectStructure>
|
||||
<ProtectWindows>False</ProtectWindows>
|
||||
</ExcelWorkbook>
|
||||
<Styles>
|
||||
<Style ss:ID="Default" ss:Name="Normal">
|
||||
<Alignment ss:Vertical="Bottom"/>
|
||||
<Borders/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
|
||||
<Interior/>
|
||||
<NumberFormat/>
|
||||
<Protection/>
|
||||
</Style>
|
||||
<Style ss:ID="s16">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
|
||||
</Style>
|
||||
<Style ss:ID="s17">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
|
||||
</Style>
|
||||
<Style ss:ID="s19">
|
||||
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
|
||||
</Style>
|
||||
<Style ss:ID="s20">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#467886" ss:Underline="Single"/>
|
||||
</Style>
|
||||
</Styles>
|
||||
<Worksheet ss:Name="Frameworks">
|
||||
<Table ss:ExpandedColumnCount="7" ss:ExpandedRowCount="10" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Column ss:Index="2" ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">MacOS</Data></Cell>
|
||||
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Windows</Data></Cell>
|
||||
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Linux</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">Framework</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">x64</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/electron#complete-example"><Data ss:Type="String">Electron</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/nwjs#complete-example"><Data ss:Type="String">NW.js</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/wails#complete-example"><Data ss:Type="String">Wails</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/tauri#complete-example"><Data ss:Type="String">Tauri</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/neutralino#complete-example"><Data ss:Type="String">NeutralinoJS</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/desktop/reactnative"><Data ss:Type="String">React Native</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"></Cell>
|
||||
<Cell ss:StyleID="s16"></Cell>
|
||||
</Row>
|
||||
</Table>
|
||||
</Worksheet>
|
||||
</Workbook>
|
||||
@ -1,13 +1,21 @@
|
||||
import { read, utils } from 'xlsx';
|
||||
import url from './engines.xls';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
const EngineData = () => {
|
||||
const [engines, setEngines] = useState("");
|
||||
const [binding, setBinding] = useState("");
|
||||
|
||||
useEffect(() => { (async() => {
|
||||
const html = await (await fetch(url)).json();
|
||||
setEngines(html["Engines"]);
|
||||
const wb = read(await (await fetch(url)).arrayBuffer(), { dense: true });
|
||||
setEngines(utils.sheet_to_html(wb.Sheets["Engines"]));
|
||||
setBinding(utils.sheet_to_html(wb.Sheets["Bindings"]));
|
||||
})(); }, []);
|
||||
return ( <p dangerouslySetInnerHTML={{__html: engines}}/> );
|
||||
return ( <>
|
||||
<p>The following engines have been tested in their native languages:</p>
|
||||
<div dangerouslySetInnerHTML={{__html: engines}}/>
|
||||
<p>The following bindings have been tested:</p>
|
||||
<div dangerouslySetInnerHTML={{__html: binding}}/>
|
||||
</> );
|
||||
};
|
||||
export default EngineData;
|
||||
@ -1,6 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<?mso-application progid="Excel.Sheet"?>
|
||||
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
|
||||
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
|
||||
xmlns:o="urn:schemas-microsoft-com:office:office"
|
||||
xmlns:x="urn:schemas-microsoft-com:office:excel"
|
||||
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
|
||||
xmlns:html="http://www.w3.org/TR/REC-html40">
|
||||
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
|
||||
<WindowHeight>10620</WindowHeight>
|
||||
<WindowWidth>11020</WindowWidth>
|
||||
@ -20,21 +24,21 @@
|
||||
<Protection/>
|
||||
</Style>
|
||||
<Style ss:ID="s16">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
|
||||
<Font ss:FontName="Arial" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
|
||||
</Style>
|
||||
<Style ss:ID="s17">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"
|
||||
ss:Bold="1"/>
|
||||
</Style>
|
||||
<Style ss:ID="s19">
|
||||
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
|
||||
</Style>
|
||||
<Style ss:ID="s20">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#467886" ss:Underline="Single"/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"
|
||||
ss:Bold="1"/>
|
||||
</Style>
|
||||
</Styles>
|
||||
<Worksheet ss:Name="Engines">
|
||||
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="18" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="15" x:FullColumns="1"
|
||||
x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Column ss:Index="3" ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
@ -58,7 +62,7 @@
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#complete-example"><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
@ -68,7 +72,7 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#complete-example"><Data ss:Type="String">V8</Data></Cell>
|
||||
<Cell><Data ss:Type="String">V8</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C++</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
@ -78,27 +82,17 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/rhino#complete-example"><Data ss:Type="String">Rhino</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Rhino</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Java</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#complete-example"><Data ss:Type="String">JSC</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C++</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jint#integration-example"><Data ss:Type="String">Jint</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Jint</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C#</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
@ -108,7 +102,7 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/goja#complete-example"><Data ss:Type="String">Goja</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Goja</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Go</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
@ -118,17 +112,17 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/nashorn#complete-example"><Data ss:Type="String">Nashorn</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Nashorn</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Java</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/quickjs#integration-example"><Data ss:Type="String">QuickJS</Data></Cell>
|
||||
<Cell><Data ss:Type="String">QuickJS</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
@ -138,27 +132,27 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/hermes#integration-example"><Data ss:Type="String">Hermes</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Hermes</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C++</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/chakra#integration-example"><Data ss:Type="String">ChakraCore</Data></Cell>
|
||||
<Cell><Data ss:Type="String">ChakraCore</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C++</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/boa#complete-example"><Data ss:Type="String">Boa</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Boa</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Rust</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
@ -168,54 +162,34 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/perl#complete-example"><Data ss:Type="String">JE</Data></Cell>
|
||||
<Cell><Data ss:Type="String">JE</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Perl</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jerryscript#integration-example"><Data ss:Type="String">JerryScript</Data></Cell>
|
||||
<Cell><Data ss:Type="String">JerryScript</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/graaljs#complete-example"><Data ss:Type="String">GraalJS</Data></Cell>
|
||||
<Cell><Data ss:Type="String">GraalJS</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Java</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/mujs#integration-example"><Data ss:Type="String">MuJS</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jurassic#integration-example"><Data ss:Type="String">Jurassic.NET</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C#</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
</Row>
|
||||
</Table>
|
||||
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
|
||||
@ -244,7 +218,8 @@
|
||||
</WorksheetOptions>
|
||||
</Worksheet>
|
||||
<Worksheet ss:Name="Bindings">
|
||||
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="20" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="12" x:FullColumns="1"
|
||||
x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Column ss:Index="3" ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
@ -268,57 +243,47 @@
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">ARM</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#perl"><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Perl</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#php"><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">PHP</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#python"><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Python</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#rust"><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Rust</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/duktape#zig"><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Duktape</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Zig</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#rust"><Data ss:Type="String">V8</Data></Cell>
|
||||
<Cell><Data ss:Type="String">V8</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Rust</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
@ -328,62 +293,22 @@
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#java"><Data ss:Type="String">V8</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Java</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#c"><Data ss:Type="String">V8</Data></Cell>
|
||||
<Cell><Data ss:Type="String">C#</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/v8#python"><Data ss:Type="String">V8</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Python</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#swift"><Data ss:Type="String">JSC</Data></Cell>
|
||||
<Cell><Data ss:Type="String">JSC</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Swift</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/jsc#rust"><Data ss:Type="String">JSC</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Rust</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
<Cell ss:StyleID="s16"/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/engines/rb#complete-example"><Data ss:Type="String">ExecJS</Data></Cell>
|
||||
<Cell><Data ss:Type="String">ExecJS</Data></Cell>
|
||||
<Cell><Data ss:Type="String">Ruby</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✱</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
import url from './mobile.xls';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
const FrameworkData = () => {
|
||||
const [fw, setFW] = useState("");
|
||||
|
||||
useEffect(() => { (async() => {
|
||||
const html = await (await fetch(url)).json();
|
||||
setFW(html["Frameworks"]);
|
||||
})(); }, []);
|
||||
return ( <p dangerouslySetInnerHTML={{__html: fw}}/> );
|
||||
};
|
||||
export default FrameworkData;
|
||||
@ -1,165 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<?mso-application progid="Excel.Sheet"?>
|
||||
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
|
||||
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
|
||||
<WindowHeight>10620</WindowHeight>
|
||||
<WindowWidth>11020</WindowWidth>
|
||||
<WindowTopX>2260</WindowTopX>
|
||||
<WindowTopY>19600</WindowTopY>
|
||||
<ActiveSheet>1</ActiveSheet>
|
||||
<ProtectStructure>False</ProtectStructure>
|
||||
<ProtectWindows>False</ProtectWindows>
|
||||
</ExcelWorkbook>
|
||||
<Styles>
|
||||
<Style ss:ID="Default" ss:Name="Normal">
|
||||
<Alignment ss:Vertical="Bottom"/>
|
||||
<Borders/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
|
||||
<Interior/>
|
||||
<NumberFormat/>
|
||||
<Protection/>
|
||||
</Style>
|
||||
<Style ss:ID="s16">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#1C1E21"/>
|
||||
</Style>
|
||||
<Style ss:ID="s17">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
|
||||
</Style>
|
||||
<Style ss:ID="s19">
|
||||
<Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000" ss:Bold="1"/>
|
||||
</Style>
|
||||
<Style ss:ID="s20">
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#467886" ss:Underline="Single"/>
|
||||
</Style>
|
||||
</Styles>
|
||||
<Worksheet ss:Name="Frameworks">
|
||||
<Table ss:ExpandedColumnCount="8" ss:ExpandedRowCount="18" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
|
||||
<Column ss:Index="3" ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Column ss:Width="24"/>
|
||||
<Column ss:Width="31"/>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Real Device</Data></Cell>
|
||||
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">MacOS Sim</Data></Cell>
|
||||
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Windows Sim</Data></Cell>
|
||||
<Cell ss:MergeAcross="1" ss:StyleID="s19"><Data ss:Type="String">Linux Sim</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">Platform</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">iOS</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">Android</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">iOS</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">Android</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">iOS</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">Android</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">iOS</Data></Cell>
|
||||
<Cell ss:StyleID="s17"><Data ss:Type="String">Android</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/reactnative"><Data ss:Type="String">React Native</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/nativescript"><Data ss:Type="String">NativeScript</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/capacitor"><Data ss:Type="String">CapacitorJS</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/ionic"><Data ss:Type="String">Ionic</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/flutter"><Data ss:Type="String">Dart + Flutter</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/quasar"><Data ss:Type="String">Quasar</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell ss:StyleID="s20" ss:HRef="/docs/demos/mobile/lynx"><Data ss:Type="String">Lynx</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✔</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"></Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String">✘</Data></Cell>
|
||||
<Cell ss:StyleID="s16"><Data ss:Type="String"> </Data></Cell>
|
||||
</Row>
|
||||
</Table>
|
||||
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
|
||||
<PageSetup>
|
||||
<Header x:Margin="0.3"/>
|
||||
<Footer x:Margin="0.3"/>
|
||||
<PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
|
||||
</PageSetup>
|
||||
<FreezePanes/>
|
||||
<FrozenNoSplit/>
|
||||
<SplitHorizontal>2</SplitHorizontal>
|
||||
<TopRowBottomPane>2</TopRowBottomPane>
|
||||
<ActivePane>2</ActivePane>
|
||||
<Panes>
|
||||
<Pane>
|
||||
<Number>3</Number>
|
||||
</Pane>
|
||||
<Pane>
|
||||
<Number>2</Number>
|
||||
<ActiveRow>12</ActiveRow>
|
||||
<ActiveCol>5</ActiveCol>
|
||||
</Pane>
|
||||
</Panes>
|
||||
<ProtectObjects>False</ProtectObjects>
|
||||
<ProtectScenarios>False</ProtectScenarios>
|
||||
</WorksheetOptions>
|
||||
</Worksheet>
|
||||
</Workbook>
|
||||
@ -1,5 +1,4 @@
|
||||
---
|
||||
title: Standalone Browser Scripts
|
||||
pagination_prev: getting-started/index
|
||||
pagination_next: getting-started/examples/index
|
||||
sidebar_position: 1
|
||||
@ -10,13 +9,15 @@ sidebar_custom_props:
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
Each standalone release script is available at https://cdn.sheetjs.com/.
|
||||
# Standalone Browser Scripts
|
||||
|
||||
Each standalone release script is available at <https://cdn.sheetjs.com/>.
|
||||
|
||||
<p>The current version is {current} and can be referenced as follows:</p>
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<!-- use version ${current} -->
|
||||
<script type="text/javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
:::tip pass
|
||||
@ -27,55 +28,54 @@ new versions are released!
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
A number of services host older versions of the SheetJS libraries. Due to
|
||||
syncing issues, they are generally out of date.
|
||||
|
||||
**The SheetJS CDN** https://cdn.sheetjs.com/ **is the authoritative source**
|
||||
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
|
||||
**for SheetJS scripts**
|
||||
|
||||
:::
|
||||
|
||||
## Browser Scripts
|
||||
|
||||
`xlsx.full.min.js` is the complete standalone script. It includes support for
|
||||
`xlsx.full.min.js` is the complete standalone script. It includes support for
|
||||
reading and writing many spreadsheet formats.
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<!-- use xlsx.full.min.js from version ${current} -->
|
||||
<script type="text/javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
`xlsx.mini.min.js` is a slimmer build that omits the following features:
|
||||
|
||||
- CSV and SYLK encodings (directly affecting users outside of the United States)
|
||||
- XLSB / XLS / Lotus 1-2-3 / SpreadsheetML 2003 / Apple Numbers file formats
|
||||
- [Stream utility functions](/docs/api/stream)
|
||||
A slimmer build is generated at `dist/xlsx.mini.min.js`. Compared to full build:
|
||||
- codepage library skipped (no support for XLS encodings)
|
||||
- no support for XLSB / XLS / Lotus 1-2-3 / SpreadsheetML 2003 / Numbers
|
||||
- node stream utils removed
|
||||
|
||||
<details>
|
||||
<summary><b>How to integrate the mini build</b> (click to show)</summary>
|
||||
<details><summary><b>How to integrate the mini build</b> (click to show)</summary>
|
||||
|
||||
A single script tag should be added at the top of the HTML page:
|
||||
Replace references to `xlsx.full.min.js` with `xlsx.mini.min.js`. Starting from
|
||||
scratch, a single script tag should be added at the top of the HTML page:
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<!-- use xlsx.mini.min.js from version ${current} -->
|
||||
<script type="text/javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.mini.min.js"></script>`}
|
||||
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.mini.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
</details>
|
||||
|
||||
### Vendoring
|
||||
|
||||
For general stability, making a local copy of SheetJS scripts ("vendoring") is
|
||||
strongly recommended. Vendoring decouples websites from SheetJS infrastructure.
|
||||
For general stability, "vendoring" scripts is the recommended approach:
|
||||
|
||||
<ol start="1"><li><p>Download the script (<code parentName="pre">xlsx.full.min.js</code>) for
|
||||
the desired version. The current version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.full.min.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.full.min.js"}</a></p></li></ol>
|
||||
<p>1) Download the script (<code parentName="pre">xlsx.full.min.js</code>) for
|
||||
the desired version. The current version is available at <a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.full.min.js</a></p>
|
||||
|
||||
2) Move the script to a `public` folder with other scripts.
|
||||
|
||||
3) Reference the vendored script from HTML pages:
|
||||
3) Reference the local script from HTML pages:
|
||||
|
||||
```html
|
||||
<script src="/public/xlsx.full.min.js"></script>
|
||||
@ -89,15 +89,16 @@ For broad compatibility with JavaScript engines, the library is written using
|
||||
ECMAScript 3 language dialect. A "shim" script provides implementations of
|
||||
functions for older browsers and environments.
|
||||
|
||||
Due to SSL compatibility issues, older versions of IE will not be able to use
|
||||
the CDN scripts directly. They should be downloaded and saved to a public path:
|
||||
Due to SSL compatibility issues, older versions of IE will not be able to
|
||||
use the CDN scripts directly. They should be downloaded and saved to a public
|
||||
directory in the site:
|
||||
|
||||
<ul>
|
||||
<li>Standalone: <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.mini.min.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.mini.min.js"}</a></li>
|
||||
<li>Shim: <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/shim.min.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/shim.min.js"}</a></li>
|
||||
<li>Standalone: <a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.mini.min.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.mini.min.js</a></li>
|
||||
<li>Shim: <a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/shim.min.js</a></li>
|
||||
</ul>
|
||||
|
||||
A `script` reference to the shim must be added before the standalone script:
|
||||
Add a `script` reference to the shim before the standalone script:
|
||||
|
||||
```html
|
||||
<!-- add the shim first -->
|
||||
@ -116,79 +117,14 @@ importScripts("https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js"
|
||||
importScripts("https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js");`}
|
||||
</CodeBlock>
|
||||
|
||||
### Type Checker
|
||||
|
||||
:::danger VS Code Telemetry and Data Exfiltration
|
||||
|
||||
The official builds of Visual Studio Code ("VS Code" or "VSCode") embed
|
||||
telemetry and send information to Microsoft servers.
|
||||
|
||||
**[VSCodium](https://vscodium.com/) is a telemetry-free fork of VS Code.**
|
||||
|
||||
When writing code that may process personally identifiable information (PII),
|
||||
the SheetJS team strongly encourages building VS Code from source or using IDEs
|
||||
that do not exfiltrate data.
|
||||
|
||||
:::
|
||||
|
||||
The type checker integrated in VSCodium and VS Code does not currently provide
|
||||
type hints when using the standalone build. Using the JSDoc `@type` directive
|
||||
coupled with type imports, VSCodium will recognize the types:
|
||||
|
||||

|
||||
|
||||
<ol start="1">
|
||||
<li><p>Download the types (<code parentName="pre">index.d.ts</code>) for
|
||||
the desired version. The current version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/types/index.d.ts"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/types/index.d.ts"}</a></p></li>
|
||||
</ol>
|
||||
|
||||
2) Rename the types file to `xlsx.d.ts`. It does not need to reside in the same
|
||||
folder as the standalone script.
|
||||
|
||||
3) In the browser script referencing the global, prepend the following lines:
|
||||
|
||||
```js title="Prepend this fragment in each source file referencing the XLSX global"
|
||||
/** @type {import("./xlsx")} */
|
||||
const XLSX = globalThis.XLSX;
|
||||
```
|
||||
|
||||
4) If the `xlsx.d.ts` file is in a different folder, change the argument to the
|
||||
`import` method to reflect the relative path. For example, given the structure:
|
||||
|
||||
```text title="Folder Structure"
|
||||
- /vendor
|
||||
- /vendor/xlsx.ts
|
||||
- /src
|
||||
- /src/app.js
|
||||
```
|
||||
|
||||
`/src/app.js` must refer to the types as `../vendor/xlsx`:
|
||||
|
||||
```js title="Preamble for /src/app.js when types are at /vendor/xlsx.d.ts"
|
||||
// highlight-next-line
|
||||
/** @type {import("../vendor/xlsx")} */
|
||||
const XLSX = globalThis.XLSX;
|
||||
```
|
||||
|
||||
The `.d.ts` file extension must be omitted.
|
||||
|
||||
:::warning pass
|
||||
|
||||
JSDoc types using the `@import` directive are not supported in `<script>` tags.
|
||||
|
||||
**This is a known bug with VS Code!**
|
||||
|
||||
:::
|
||||
|
||||
## ECMAScript Module Imports
|
||||
|
||||
:::info pass
|
||||
:::caution pass
|
||||
|
||||
This section refers to imports in HTML pages using `<script type="module">`.
|
||||
|
||||
The ["Frameworks and Bundlers"](/docs/getting-started/installation/frameworks)
|
||||
section covers imports in projects using bundlers (ViteJS) or frameworks
|
||||
(Kaioken / ReactJS / Angular / VueJS / Svelte)
|
||||
This section refers to imports using `script type="module"`. For imports in
|
||||
modern projects using Webpack or React or Angular or VueJS, the installation is
|
||||
described [in "Frameworks and Bundlers"](/docs/getting-started/installation/frameworks).
|
||||
|
||||
:::
|
||||
|
||||
@ -267,13 +203,14 @@ xport.addEventListener("click", async() => {
|
||||
|
||||
## Bower
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
Bower is deprecated and the maintainers recommend using other tools.
|
||||
|
||||
:::
|
||||
|
||||
The Bower package manager supports tarballs from the SheetJS CDN:
|
||||
|
||||
The Bower package manager plays nice with the CDN tarballs:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npx bower install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
@ -1,10 +1,9 @@
|
||||
---
|
||||
title: Frameworks and Bundlers
|
||||
pagination_prev: getting-started/index
|
||||
pagination_next: getting-started/examples/index
|
||||
sidebar_position: 2
|
||||
sidebar_custom_props:
|
||||
summary: Kaioken, Angular, React, VueJS, ViteJS, Webpack, etc.
|
||||
summary: Angular, React, VueJS, Webpack, etc.
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
@ -12,11 +11,13 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
Each standalone release package is available at https://cdn.sheetjs.com/. The
|
||||
NodeJS package is designed to be used with frameworks and bundlers. It is a
|
||||
# Frameworks and Bundlers
|
||||
|
||||
Each standalone release package is available at <https://cdn.sheetjs.com/>. The
|
||||
NodeJS package is designed to be used with frameworks and bundlers. It is a
|
||||
proper ECMAScript Module release which can be optimized with developer tools.
|
||||
|
||||
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a> is the URL for version {current}</p>
|
||||
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a> is the URL for version {current}</p>
|
||||
|
||||
## Installation
|
||||
|
||||
@ -40,24 +41,6 @@ pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`
|
||||
yarn remove xlsx
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
Newer releases of Yarn may throw an error:
|
||||
|
||||
```
|
||||
Usage Error: It seems you are trying to add a package using a https:... url; we now require package names to be explicitly specified.
|
||||
Try running the command again with the package name prefixed: yarn add my-package@https:...
|
||||
```
|
||||
|
||||
The workaround is to prepend the URL with `xlsx@`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@ -67,7 +50,7 @@ Once installed, the library can be imported under the name `xlsx`:
|
||||
import { read, writeFileXLSX } from "xlsx";
|
||||
```
|
||||
|
||||
The ["Bundlers" demo](/docs/demos/frontend/bundler) includes complete examples.
|
||||
The ["Bundlers" demo](/docs/demos/bundler) includes examples for specific tools.
|
||||
|
||||
:::tip pass
|
||||
|
||||
@ -85,7 +68,7 @@ Snyk security tooling may report errors involving "Prototype Pollution":
|
||||
Prototype Pollution [Medium Severity][https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926]
|
||||
```
|
||||
|
||||
As noted in the [Snyk report](https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926):
|
||||
As noted in the [Snyk report](https://web.archive.org/web/20231129100639/https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926):
|
||||
|
||||
> The issue is resolved in version 0.19.3
|
||||
|
||||
@ -98,14 +81,14 @@ Until Snyk fixes the bugs, the official recommendation is to
|
||||
|
||||
### Legacy Endpoints
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
Older releases are technically available on the public npm registry as `xlsx`,
|
||||
but the registry is out of date. The latest version on that registry is 0.18.5
|
||||
|
||||
This is a known registry bug
|
||||
|
||||
**The SheetJS CDN** https://cdn.sheetjs.com/ **is the authoritative source**
|
||||
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
|
||||
**for SheetJS modules.**
|
||||
|
||||
For existing projects, the easiest approach is to uninstall and reinstall:
|
||||
@ -148,8 +131,7 @@ in `package.json` can control module resolution:
|
||||
|
||||
### Vendoring
|
||||
|
||||
For general stability, making a local copy of SheetJS modules ("vendoring") is
|
||||
strongly recommended. Vendoring decouples projects from SheetJS infrastructure.
|
||||
For general stability, "vendoring" modules is the recommended approach:
|
||||
|
||||
0) Remove any existing dependency on a project named `xlsx`:
|
||||
|
||||
@ -171,33 +153,13 @@ yarn remove xlsx`}
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<ol start="1"><li><p>Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
|
||||
version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a></p></li></ol>
|
||||
<p>1) Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
|
||||
version is available at <a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a></p>
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl -O https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
2) Create a `vendor` subfolder at the root of your project and move the tarball
|
||||
to that folder. Add it to your project repository.
|
||||
|
||||
2) Create a `vendor` subfolder at the root of your project:
|
||||
|
||||
```bash
|
||||
mkdir -p vendor
|
||||
```
|
||||
|
||||
3) Move the tarball from step (1) to the `vendor` folder:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
mv xlsx-${current}.tgz vendor`}
|
||||
</CodeBlock>
|
||||
|
||||
4) If the project is managed with a version control system, add the tarball to
|
||||
the source repository. The Git VCS supports the `add` subcommand:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
git add vendor/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
5) Install the tarball using a package manager:
|
||||
3) Install the tarball using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
@ -214,23 +176,6 @@ pnpm install --save file:vendor/xlsx-${current}.tgz`}
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add file:vendor/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
Newer releases of Yarn may throw an error:
|
||||
|
||||
<CodeBlock language="text">{`\
|
||||
Usage Error: The file:vendor/xlsx-${current}.tgz string didn't match the required format (package-name@range). Did you perhaps forget to explicitly reference the package name?`}
|
||||
</CodeBlock>
|
||||
|
||||
The workaround is to prepend the URI with `xlsx@`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add xlsx@file:vendor/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@ -258,13 +203,13 @@ var XLSX = require("xlsx");
|
||||
var read = XLSX.read, utils = XLSX.utils;
|
||||
```
|
||||
|
||||
The ["Bundlers" demo](/docs/demos/frontend/bundler) includes complete examples.
|
||||
The ["Bundlers" demo](/docs/demos/bundler) includes examples for specific tools.
|
||||
|
||||
### Dynamic Imports
|
||||
|
||||
Dynamic imports with `import()` will only download scripts when they are needed.
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
Dynamic `import` will always download the full contents of the imported scripts!
|
||||
|
||||
@ -281,8 +226,7 @@ import { utils, writeFileXLSX } from "xlsx";
|
||||
export { utils, writeFileXLSX };
|
||||
```
|
||||
|
||||
Bundlers will typically optimize the script and only add the requested features.
|
||||
A dynamic import of the wrapper will load the optimized wrapper script:
|
||||
A dynamic import of the wrapper script will only load the requested features:
|
||||
|
||||
```js
|
||||
async function export_data() {
|
||||
|
||||
@ -12,9 +12,9 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
Package tarballs are available on https://cdn.sheetjs.com.
|
||||
Package tarballs are available on <https://cdn.sheetjs.com>.
|
||||
|
||||
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a> is the URL for version {current}</p>
|
||||
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a> is the URL for version {current}</p>
|
||||
|
||||
## Installation
|
||||
|
||||
@ -38,24 +38,6 @@ pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`
|
||||
yarn remove xlsx
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
Newer releases of Yarn may throw an error:
|
||||
|
||||
```
|
||||
Usage Error: It seems you are trying to add a package using a https:... url; we now require package names to be explicitly specified.
|
||||
Try running the command again with the package name prefixed: yarn add my-package@https:...
|
||||
```
|
||||
|
||||
The workaround is to prepend the URL with `xlsx@`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@ -75,7 +57,7 @@ Snyk security tooling may report errors involving "Prototype Pollution":
|
||||
Prototype Pollution [Medium Severity][https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926]
|
||||
```
|
||||
|
||||
As noted in the [Snyk report](https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926):
|
||||
As noted in the [Snyk report](https://web.archive.org/web/20231129100639/https://security.snyk.io/vuln/SNYK-JS-XLSX-5457926):
|
||||
|
||||
> The issue is resolved in version 0.19.3
|
||||
|
||||
@ -88,14 +70,14 @@ Until Snyk fixes the bugs, the official recommendation is to
|
||||
|
||||
### Legacy Endpoints
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
Older releases are technically available on the public npm registry as `xlsx`,
|
||||
but the registry is out of date. The latest version on that registry is 0.18.5
|
||||
|
||||
This is a known registry bug
|
||||
|
||||
**The SheetJS CDN** https://cdn.sheetjs.com/ **is the authoritative source**
|
||||
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
|
||||
**for SheetJS modules.**
|
||||
|
||||
For existing projects, the easiest approach is to uninstall and reinstall:
|
||||
@ -138,8 +120,7 @@ in `package.json` can control module resolution:
|
||||
|
||||
### Vendoring
|
||||
|
||||
For general stability, making a local copy of SheetJS modules ("vendoring") is
|
||||
strongly recommended. Vendoring decouples projects from SheetJS infrastructure.
|
||||
For general stability, "vendoring" modules is the recommended approach:
|
||||
|
||||
0) Remove any existing dependency on a project named `xlsx`:
|
||||
|
||||
@ -161,33 +142,13 @@ yarn remove xlsx`}
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<ol start="1"><li><p>Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
|
||||
version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a></p></li></ol>
|
||||
<p>1) Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
|
||||
version is available at <a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a></p>
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl -O https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
2) Create a `vendor` subfolder at the root of your project and move the tarball
|
||||
to that folder. Add it to your project repository.
|
||||
|
||||
2) Create a `vendor` subfolder at the root of your project:
|
||||
|
||||
```bash
|
||||
mkdir -p vendor
|
||||
```
|
||||
|
||||
3) Move the tarball from step (1) to the `vendor` folder:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
mv xlsx-${current}.tgz vendor`}
|
||||
</CodeBlock>
|
||||
|
||||
4) If the project is managed with a version control system, add the tarball to
|
||||
the source repository. The Git VCS supports the `add` subcommand:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
git add vendor/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
5) Install the tarball using a package manager:
|
||||
3) Install the tarball using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
@ -204,23 +165,6 @@ pnpm install --save file:vendor/xlsx-${current}.tgz`}
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add file:vendor/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
Newer releases of Yarn may throw an error:
|
||||
|
||||
<CodeBlock language="text">{`\
|
||||
Usage Error: The file:vendor/xlsx-${current}.tgz string didn't match the required format (package-name@range). Did you perhaps forget to explicitly reference the package name?`}
|
||||
</CodeBlock>
|
||||
|
||||
The workaround is to prepend the URI with `xlsx@`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add xlsx@file:vendor/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@ -239,7 +183,7 @@ The package supports CommonJS `require` and ESM `import` module systems.
|
||||
### CommonJS `require`
|
||||
|
||||
By default, the module supports `require` and it will automatically add support
|
||||
for encodings, streams and file system access:
|
||||
for streams and file system access:
|
||||
|
||||
```js
|
||||
var XLSX = require("xlsx");
|
||||
@ -251,66 +195,6 @@ The package also ships with `xlsx.mjs`, a script compatible with the ECMAScript
|
||||
module system. When using the ESM build in NodeJS, some dependencies must be
|
||||
loaded manually.
|
||||
|
||||
:::danger ECMAScript Module Limitations
|
||||
|
||||
The original ECMAScript module specification only supported top-level imports:
|
||||
|
||||
```js
|
||||
import { Readable } from 'stream';
|
||||
```
|
||||
|
||||
If a module is unavailable, there is no way for scripts to gracefully fail or
|
||||
ignore the error. This presents an insurmountable challenge for libraries.
|
||||
|
||||
To contrast, the SheetJS CommonJS modules gracefully handle missing dependencies
|
||||
since `require` failures are errors that the library can catch and handle.
|
||||
|
||||
---
|
||||
|
||||
Patches to the specification added two different solutions to the problem:
|
||||
|
||||
- "dynamic imports" will throw errors that can be handled by libraries. Dynamic
|
||||
imports will taint APIs that do not use Promise-based methods.
|
||||
|
||||
```js
|
||||
/* Readable will be undefined if stream cannot be imported */
|
||||
const Readable = await (async() => {
|
||||
try {
|
||||
return (await import("stream"))?.Readable;
|
||||
} catch(e) { /* silently ignore error */ }
|
||||
})();
|
||||
```
|
||||
|
||||
- "import maps" control module resolution, allowing library users to manually
|
||||
shunt unsupported modules.
|
||||
|
||||
**These patches were released after browsers adopted ESM!** A number of browsers
|
||||
and other platforms support top-level imports but do not support the patches.
|
||||
|
||||
---
|
||||
|
||||
For the ESM build, there were four unpalatable options:
|
||||
|
||||
A) Generate a module script for browsers, a module script for ViteJS, a module
|
||||
script for [Deno](/docs/getting-started/installation/deno), and a module script
|
||||
for NodeJS and [BunJS](/docs/getting-started/installation/bun).
|
||||
|
||||
B) Remove all optional features, including support for non-English legacy files.
|
||||
|
||||
C) Add all optional features, effectively making the features mandatory.
|
||||
|
||||
D) Introduce special methods for optional dependency injection.
|
||||
|
||||
The SheetJS team chose option (D). NodeJS native modules are still automatically
|
||||
loaded in the CommonJS build, but NodeJS ESM scripts must now load and pass the
|
||||
dependencies to the library using special methods.
|
||||
|
||||
---
|
||||
|
||||
**It is strongly recommended to use CommonJS in NodeJS scripts!**
|
||||
|
||||
:::
|
||||
|
||||
#### Filesystem Operations
|
||||
|
||||
The `set_fs` method accepts a `fs` instance for reading and writing files using
|
||||
@ -327,7 +211,7 @@ XLSX.set_fs(fs);
|
||||
#### Stream Operations
|
||||
|
||||
The `set_readable` method accepts a `stream.Readable` instance for use in stream
|
||||
methods including [`XLSX.stream.to_csv`](/docs/api/stream):
|
||||
methods such as `XLSX.stream.to_csv`:
|
||||
|
||||
```js
|
||||
import * as XLSX from 'xlsx';
|
||||
@ -353,7 +237,7 @@ XLSX.set_cptable(cpexcel);
|
||||
|
||||
#### NextJS
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
`fs` cannot be imported from the top level in NextJS pages. This will not work:
|
||||
|
||||
@ -366,8 +250,6 @@ import * as fs from 'fs'; // this import will fail
|
||||
set_fs(fs);
|
||||
```
|
||||
|
||||
**This is a design flaw in NextJS!**
|
||||
|
||||
:::
|
||||
|
||||
For server-side file processing, `fs` should be loaded with a dynamic import
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
---
|
||||
title: AMD (define)
|
||||
pagination_prev: getting-started/index
|
||||
pagination_next: getting-started/examples/index
|
||||
sidebar_position: 4
|
||||
@ -10,11 +9,13 @@ sidebar_custom_props:
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
Each standalone release script is available at https://cdn.sheetjs.com/.
|
||||
# AMD (define)
|
||||
|
||||
Each standalone release script is available at <https://cdn.sheetjs.com/>.
|
||||
|
||||
`xlsx.full.min.js` supports AMD with name `xlsx` out of the box.
|
||||
|
||||
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.full.min.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.full.min.js"}</a> is the URL for {current}</p>
|
||||
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.full.min.js</a> is the URL for {current}</p>
|
||||
|
||||
:::note pass
|
||||
|
||||
@ -64,74 +65,27 @@ define(['N/file', 'xlsx'], function(file, XLSX) {
|
||||
|
||||
**More details are included in the [NetSuite demo](/docs/demos/cloud/netsuite#installation)**
|
||||
|
||||
:::caution Oracle Bugs
|
||||
|
||||
[NetSuite users reported](https://git.sheetjs.com/sheetjs/sheetjs/issues/3097)
|
||||
errors that stem from an Oracle issue. A sample error message is shown below.
|
||||
|
||||
```
|
||||
Fail to evaluate script: com.netsuite.suitescript.scriptobject.GraalValueAdapter@68d0f09d
|
||||
```
|
||||
|
||||
**This is a NetSuite bug. Only Oracle can fix the bug!**
|
||||
|
||||
It is strongly encouraged to escalate the issue with Oracle support.
|
||||
|
||||
NetSuite users have reported success with the following workaround:
|
||||
|
||||
1) Open the script in a text editor and search for `define(` in the code.
|
||||
|
||||
There will be exactly one instance:
|
||||
|
||||
```js
|
||||
define("xlsx",function(){
|
||||
```
|
||||
|
||||
Replace the `xlsx` with `sheetjs`:
|
||||
|
||||
```js
|
||||
define("sheetjs",function(){
|
||||
```
|
||||
|
||||
2) Use the new name in the JSON configuration:
|
||||
|
||||
```json title="JsLibraryConfig.json"
|
||||
{
|
||||
"paths": {
|
||||
// highlight-next-line
|
||||
"sheetjs": "/path/to/xlsx.full.min"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3) Use the new name in the array argument to the `define` function call:
|
||||
|
||||
```js title="SuiteScript"
|
||||
/**
|
||||
* @NApiVersion 2.x
|
||||
* ... more options ...
|
||||
// highlight-next-line
|
||||
* @NAmdConfig ./JsLibraryConfig.json
|
||||
*/
|
||||
// highlight-next-line
|
||||
define(['N/file', 'sheetjs'], function(file, XLSX) {
|
||||
// ^^^^^^^ ^^^^
|
||||
// new module name same variable
|
||||
// ... use XLSX here ...
|
||||
});
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## SAP UI5
|
||||
|
||||
OpenUI5 and SAPUI5 installation instructions are covered in the dedicated
|
||||
["OpenUI5 / SAPUI5" demo](/docs/demos/frontend/openui5#installation).
|
||||
After downloading the script, it can be uploaded to the UI5 project and loaded
|
||||
in the `sap.ui.define` call:
|
||||
|
||||
SheetJS standalone scripts can be loaded in two ways:
|
||||
```js
|
||||
sap.ui.define([
|
||||
/* ... other libraries ... */
|
||||
"path/to/xlsx.full.min"
|
||||
], function(/* ... variables for the other libraries ... */, XLSX) {
|
||||
// use XLSX here
|
||||
})
|
||||
```
|
||||
|
||||
- [`sap.ui.define`](/docs/demos/frontend/openui5#installation-define)
|
||||
- [HTML SCRIPT tag](/docs/demos/frontend/openui5#installation-html)
|
||||
:::warning pass
|
||||
|
||||
**Copy and pasting code does not work** for SheetJS scripts as they contain
|
||||
Unicode characters that may be mangled. The standalone script should be
|
||||
downloaded and manually uploaded to the project.
|
||||
|
||||
:::
|
||||
|
||||
## RequireJS
|
||||
|
||||
@ -207,8 +161,8 @@ require([
|
||||
|
||||
#### Asynchronous Loading
|
||||
|
||||
When `async` is enabled, Dojo will only understand the name `xlsx`. `dojoConfig`
|
||||
can map package names to scripts:
|
||||
When `async` is enabled, Dojo will only understand the name `xlsx`. The config
|
||||
object can map package names to scripts:
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<script>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
---
|
||||
title: ExtendScript
|
||||
pagination_prev: getting-started/index
|
||||
pagination_next: getting-started/examples/index
|
||||
sidebar_position: 5
|
||||
@ -9,15 +8,13 @@ sidebar_custom_props:
|
||||
|
||||
import current from '/version.js';
|
||||
|
||||
ExtendScript is a dialect of JavaScript used in Photoshop and InDesign scripts.
|
||||
# ExtendScript
|
||||
|
||||
Each standalone release script is available at https://cdn.sheetjs.com/.
|
||||
Each standalone release script is available at <https://cdn.sheetjs.com/>.
|
||||
|
||||
`xlsx.extendscript.js` is a special ExtendScript-compatible build. The script is
|
||||
carefully assembled to work around ExtendScript quirks. Due to bugs in various
|
||||
JavaScript minifiers and tools, scripts cannot be compressed or post-processed.
|
||||
`xlsx.extendscript.js` is an ExtendScript build for Photoshop and InDesign.
|
||||
|
||||
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.extendscript.js"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/package/dist/xlsx.extendscript.js"}</a> is the URL for {current}</p>
|
||||
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.extendscript.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.extendscript.js</a> is the URL for {current}</p>
|
||||
|
||||
After downloading the script, it can be directly referenced with `#include`:
|
||||
|
||||
@ -41,15 +38,12 @@ path is application-specific.
|
||||
| Photoshop | `\Presets\Scripts` within the Application folder |
|
||||
| InDesign | Windows > Utilities > Scripts, click `☰` > "Reveal in Explorer" |
|
||||
|
||||
:::note CEP and UXP usage
|
||||
:::note CEP usage
|
||||
|
||||
The ExtendScript build should be used when performing spreadsheet operations
|
||||
from the host context (within a `jsx` script file).
|
||||
|
||||
**CEP**: [The standalone scripts](/docs/getting-started/installation/standalone)
|
||||
should be added to CEP extension HTML.
|
||||
|
||||
**UXP**: [The standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be loaded directly in UXP scripts using the `require` function.
|
||||
[The standalone scripts](/docs/getting-started/installation/standalone) should
|
||||
be added to CEP extension HTML.
|
||||
|
||||
:::
|
||||
@ -1,5 +1,4 @@
|
||||
---
|
||||
title: Deno
|
||||
pagination_prev: getting-started/index
|
||||
pagination_next: getting-started/examples/index
|
||||
sidebar_position: 6
|
||||
@ -12,9 +11,9 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
Deno is a JavaScript runtime that can import scripts from URLs.
|
||||
# Deno
|
||||
|
||||
Module scripts and type definitions are available at https://cdn.sheetjs.com/.
|
||||
Module scripts and type definitions are available at <https://cdn.sheetjs.com/>.
|
||||
|
||||
Using the URL imports, `deno run` will automatically download scripts and types:
|
||||
|
||||
@ -23,8 +22,7 @@ Using the URL imports, `deno run` will automatically download scripts and types:
|
||||
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';`}
|
||||
</CodeBlock>
|
||||
|
||||
The module URL is the ECMAScript Module build on the SheetJS CDN. `@deno-types`
|
||||
instructs Deno to use the type definitions from the SheetJS CDN.
|
||||
The `@deno-types` comment instructs Deno to use the type definitions.
|
||||
|
||||
:::caution Deno support is considered experimental.
|
||||
|
||||
@ -70,11 +68,11 @@ and the types URLs should be updated at the same time:
|
||||
|
||||
#### Deno Registry
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
The official Deno registry is out of date. This is a registry bug.
|
||||
|
||||
**The SheetJS CDN** https://cdn.sheetjs.com/ **is the authoritative source**
|
||||
**The SheetJS CDN** <https://cdn.sheetjs.com/> **is the authoritative source**
|
||||
**for SheetJS modules.**
|
||||
|
||||
:::
|
||||
|
||||
@ -4,7 +4,7 @@ pagination_prev: getting-started/index
|
||||
pagination_next: getting-started/examples/index
|
||||
sidebar_position: 7
|
||||
sidebar_custom_props:
|
||||
summary: Load NodeJS modules using CommonJS or ESM
|
||||
summary: Load NodeJS-style modules using CommonJS or ESM
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
@ -12,9 +12,9 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
Package tarballs are available on https://cdn.sheetjs.com.
|
||||
Package tarballs are available on <https://cdn.sheetjs.com>.
|
||||
|
||||
<p><a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a> is the URL for version {current}</p>
|
||||
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a> is the URL for version {current}</p>
|
||||
|
||||
:::caution Bun support is considered experimental.
|
||||
|
||||
@ -50,7 +50,8 @@ For general stability, "vendoring" modules is the recommended approach:
|
||||
bun rm xlsx
|
||||
```
|
||||
|
||||
<ol start="1"><li><p>Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current version is available at <a href={"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}>{"https://cdn.sheetjs.com/xlsx-" + current + "/xlsx-" + current + ".tgz"}</a></p></li></ol>
|
||||
<p>1) Download the tarball (<code parentName="pre">xlsx-{current}.tgz</code>) for the desired version. The current
|
||||
version is available at <a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a></p>
|
||||
|
||||
2) Create a `vendor` subfolder at the root of your project and move the tarball
|
||||
to that folder. Add it to your project repository.
|
||||
@ -76,7 +77,7 @@ The package supports CommonJS `require` and ESM `import` module systems.
|
||||
### CommonJS `require`
|
||||
|
||||
By default, the module supports `require` and it will automatically add support
|
||||
for encodings, streams and file system access:
|
||||
for streams and file system access:
|
||||
|
||||
```js
|
||||
const { readFile } = require("xlsx");
|
||||
@ -117,18 +118,7 @@ builder requires a proper `package.json` that includes the SheetJS dependency.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested in the following deployments:
|
||||
|
||||
| Architecture | BunJS | Date |
|
||||
|:-------------|:---------|:-----------|
|
||||
| `darwin-x64` | `1.2.8` | 2025-03-31 |
|
||||
| `darwin-arm` | `1.2.7` | 2025-03-30 |
|
||||
| `win11-x64` | `1.2.8` | 2025-04-17 |
|
||||
| `win11-arm` | `1.2.3` | 2025-02-23 |
|
||||
| `linux-x64` | `1.2.10` | 2025-04-21 |
|
||||
| `linux-arm` | `1.2.2` | 2025-02-16 |
|
||||
|
||||
BunJS on Windows on ARM uses the X64 compatibility layer.
|
||||
This example was last tested on 2024-02-21 against BunJS 1.0.28 on macOS 14.3.1.
|
||||
|
||||
:::
|
||||
|
||||
@ -140,61 +130,12 @@ cd sheetjs-bun-dle
|
||||
echo "{}" > package.json
|
||||
```
|
||||
|
||||
:::caution PowerShell Encoding Errors
|
||||
|
||||
The PowerShell file redirect will use the `UTF-16 LE` encoding. Bun does not
|
||||
support the encoding and will fail to install the package:
|
||||
|
||||
```
|
||||
bun add v1.1.42 (50eec002)
|
||||
1 | <20><>
|
||||
^
|
||||
error: Unexpected <20><>
|
||||
at <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:1:1
|
||||
```
|
||||
|
||||
The file must be resaved in UTF8 (without BOM) or ASCII.
|
||||
|
||||
0) Open `package.json` in VSCodium.
|
||||
|
||||
The current encoding is displayed in the lower-right corner:
|
||||
|
||||

|
||||
|
||||
1) Click the displayed encoding.
|
||||
|
||||
2) In the "Select Action" popup, select "Save with Encoding"
|
||||
|
||||
3) In the new list, select `UTF-8 utf8`:
|
||||
|
||||

|
||||
|
||||
VSCodium will automatically re-save the file.
|
||||
|
||||
:::
|
||||
|
||||
1) Install the SheetJS package tarball:
|
||||
1) Install the library:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs, the command failed with a module resolution error:
|
||||
|
||||
<CodeBlock>{`\
|
||||
error: https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz failed to resolve`}
|
||||
</CodeBlock>
|
||||
|
||||
The workaround is to prepend `xlsx@` to the URL:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun install xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
2) Save the following script to `SheetJSBun.js`:
|
||||
|
||||
```js title="SheetJSBun.js"
|
||||
@ -206,7 +147,7 @@ import * as fs from 'fs';
|
||||
XLSX.set_fs(fs);
|
||||
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -249,24 +190,10 @@ This procedure will generate `app.js`.
|
||||
4) Remove the module artifacts and original script:
|
||||
|
||||
```bash
|
||||
rm package.json bun.lock bun.lockb SheetJSBun.js
|
||||
rm package.json bun.lockb SheetJSBun.js
|
||||
rm -rf ./node_modules
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
PowerShell does not support `rm -rf`. Instead, each file must be removed:
|
||||
|
||||
```powershell title="Windows Powershell commands"
|
||||
rm package.json
|
||||
rm bun.lock
|
||||
rm bun.lockb
|
||||
rm SheetJSBun.js
|
||||
rm .\\node_modules -r -fo
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
At this point, `app.js` will be the only file in the project folder.
|
||||
|
||||
5) Run the script:
|
||||
@ -276,9 +203,4 @@ bun app.js
|
||||
```
|
||||
|
||||
If the script succeeded, the file `Presidents.xlsx` will be created. That file
|
||||
can be opened in a spreadsheet editor. If a spreadsheet editor is unavailable,
|
||||
the contents can be displayed using the `xlsx-cli` tool:
|
||||
|
||||
```bash
|
||||
bunx xlsx-cli Presidents.xlsx
|
||||
```
|
||||
can be opened in a spreadsheet editor.
|
||||
|
||||
@ -8,7 +8,7 @@ title: Installation
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
||||
|
||||
https://cdn.sheetjs.com is the primary software distribution site. Please
|
||||
<https://cdn.sheetjs.com> is the primary software distribution site. Please
|
||||
read the installation instructions for your use case:
|
||||
|
||||
<ul>{useCurrentSidebarCategory().items.map((item, index) => {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
---
|
||||
title: Export Tutorial
|
||||
pagination_prev: getting-started/installation/index
|
||||
pagination_next: getting-started/roadmap
|
||||
sidebar_position: 2
|
||||
@ -10,6 +9,8 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
# Export Tutorial
|
||||
|
||||
Many modern data sources provide an API to download data in JSON format. Many
|
||||
users prefer to work in spreadsheet software. SheetJS libraries help bridge the
|
||||
gap by translating programmer-friendly JSON to user-friendly workbooks.
|
||||
@ -43,19 +44,18 @@ sequenceDiagram
|
||||
## Acquire Data
|
||||
|
||||
The raw data is available in JSON form[^1]. It has been mirrored at
|
||||
https://docs.sheetjs.com/executive.json
|
||||
<https://sheetjs.com/data/executive.json>
|
||||
|
||||
### Raw Data
|
||||
|
||||
Acquiring the data is straightforward with `fetch`:
|
||||
|
||||
```js
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Code Explanation</b> (click to show)</summary>
|
||||
<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.
|
||||
@ -177,8 +177,7 @@ the code in more detail.
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Code Explanation</b> (click to show)</summary>
|
||||
<details><summary><b>Code Explanation</b> (click to show)</summary>
|
||||
|
||||
**Verifying if a person was a US President**
|
||||
|
||||
@ -222,13 +221,11 @@ const prez = raw_data.filter(row => row.terms.some(term => term.type == "prez"))
|
||||
### Sorting by First Term
|
||||
|
||||
The dataset is sorted in chronological order by the first presidential or vice
|
||||
presidential term. The Vice President and President in a given term are sorted
|
||||
alphabetically.
|
||||
|
||||
Barack Obama became President and Joseph Biden became Vice President in 2009.
|
||||
Since "Biden" is alphabetically before "Obama", Biden's data appears first.
|
||||
|
||||
The goal is to sort the presidents in order of their initial presidential term.
|
||||
presidential term. The Vice President and President in a given term are sorted
|
||||
alphabetically. Joe Biden and Barack Obama were Vice President and President
|
||||
respectively in 2009. Since "Biden" is alphabetically before "Obama", Biden's
|
||||
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.
|
||||
The following code looks at each president and creates a `start` property that
|
||||
@ -238,8 +235,7 @@ represents the start of the first presidential term.
|
||||
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Code Explanation</b> (click to show)</summary>
|
||||
<details><summary><b>Code Explanation</b> (click to show)</summary>
|
||||
|
||||
**Finding the first presidential term**
|
||||
|
||||
@ -309,8 +305,7 @@ At this point, each row in the `prez` array has a `start` property. Since the
|
||||
prez.sort((l,r) => l.start.localeCompare(r.start));
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Code Explanation</b> (click to show)</summary>
|
||||
<details><summary><b>Code Explanation</b> (click to show)</summary>
|
||||
|
||||
**Comparator Functions and Relative Ordering in JavaScript**
|
||||
|
||||
@ -376,8 +371,7 @@ const rows = prez.map(row => ({
|
||||
}));
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Code Explanation</b> (click to show)</summary>
|
||||
<details><summary><b>Code Explanation</b> (click to show)</summary>
|
||||
|
||||
**Wrangling One Data Row**
|
||||
|
||||
@ -499,8 +493,7 @@ cell styling and frozen rows.
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Changing Header Names</b> (click to show)</summary>
|
||||
<details><summary><b>Changing Header Names</b> (click to show)</summary>
|
||||
|
||||
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".
|
||||
@ -514,8 +507,7 @@ XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Changing Column Widths</b> (click to show)</summary>
|
||||
<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.[^7]
|
||||
@ -557,7 +549,7 @@ browser should try to create `Presidents.xlsx`
|
||||
```jsx live
|
||||
function Presidents() { return ( <button onClick={async () => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -590,7 +582,7 @@ function Presidents() { return ( <button onClick={async () => {
|
||||
}}><b>Click to Generate file!</b></button> ); }
|
||||
```
|
||||
|
||||
https://sheetjs.com/pres.html is a hosted version of this demo.
|
||||
<https://sheetjs.com/pres.html> is a hosted version of this demo.
|
||||
|
||||
## Run the Demo Locally
|
||||
|
||||
@ -605,7 +597,7 @@ Save the following script to `SheetJSStandaloneDemo.html`:
|
||||
<script>
|
||||
(async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
\n\
|
||||
/* filter for the Presidents */
|
||||
@ -644,7 +636,7 @@ After saving the file, run a local web server in the folder with the HTML file.
|
||||
For example, if NodeJS is installed:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
The server process will display a URL (typically `http://127.0.0.1:8080`). Open
|
||||
@ -679,7 +671,7 @@ const XLSX = require("xlsx");
|
||||
|
||||
(async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -739,8 +731,7 @@ Native `fetch` support was added in NodeJS 18. For older versions of NodeJS,
|
||||
the script will throw an error `fetch is not defined`. A third-party library
|
||||
like `axios` presents a similar API for fetching data:
|
||||
|
||||
<details>
|
||||
<summary><b>Example using axios</b> (click to show)</summary>
|
||||
<details><summary><b>Example using axios</b> (click to show)</summary>
|
||||
|
||||
Install the dependencies:
|
||||
|
||||
@ -757,7 +748,7 @@ const axios = require("axios");
|
||||
|
||||
(async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
// highlight-next-line
|
||||
const raw_data = (await axios(url, {responseType: "json"})).data;
|
||||
|
||||
@ -803,8 +794,7 @@ This script will write a new file `Presidents.xlsx` in the same folder.
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Other Server-Side Platforms</b> (click to show)</summary>
|
||||
<details><summary><b>Other Server-Side Platforms</b> (click to show)</summary>
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="deno" label="Deno">
|
||||
@ -816,7 +806,7 @@ Save the following script to `SheetJSDeno.ts`:
|
||||
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';
|
||||
\n\
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
\n\
|
||||
/* filter for the Presidents */
|
||||
@ -873,7 +863,7 @@ Save the following script to `SheetJSNW.html`:
|
||||
<script>
|
||||
(async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
\n\
|
||||
/* filter for the Presidents */
|
||||
@ -942,25 +932,20 @@ of the React Native documentation before testing the demo.
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
:::caution pass
|
||||
|
||||
There are a number of potential pitfalls.
|
||||
|
||||
The [React Native demo](/docs/demos/mobile/reactnative) lists some issues
|
||||
encountered in previous test runs and potential resolutions.
|
||||
|
||||
**Please reach out to [the SheetJS chat](https://sheetjs.com/chat) if there are
|
||||
any issues not mentioned in the demo page.**
|
||||
For Android testing, React Native requires Java 11. It will not work with
|
||||
current Java releases.
|
||||
|
||||
:::
|
||||
|
||||
Create a new project by running the following commands in the Terminal:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npx -y @react-native-community/cli@15 init SheetJSPres --version="0.76.5"
|
||||
npx -y react-native@0.72.4 init SheetJSPres --version="0.72.4"
|
||||
cd SheetJSPres
|
||||
\n\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-native-blob-util@0.21.2`}
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-native-blob-util@0.17.1`}
|
||||
</CodeBlock>
|
||||
|
||||
Save the following to `App.tsx` in the project:
|
||||
@ -973,7 +958,7 @@ import RNBU from 'react-native-blob-util';
|
||||
|
||||
const make_workbook = async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -1040,7 +1025,7 @@ export default App;
|
||||
|
||||
:::note pass
|
||||
|
||||
The Android demo has been tested in Windows, Arch Linux (Steam Deck) and macOS.
|
||||
The Android demo has been tested in Windows 10 and in macOS.
|
||||
|
||||
:::
|
||||
|
||||
@ -1053,13 +1038,13 @@ npx react-native start
|
||||
Once Metro is ready, it will display the commands:
|
||||
|
||||
```
|
||||
r - reload the app
|
||||
d - open developer menu
|
||||
i - run on iOS
|
||||
a - run on Android
|
||||
r - reload app
|
||||
d - open Dev Menu
|
||||
j - open DevTools
|
||||
```
|
||||
|
||||
Press `a` to run on Android. The app will launch in the emulator.
|
||||
Press `a` to run on android.
|
||||
|
||||
After clicking "Press to Export", the app will show an alert with the location
|
||||
to the generated file (`/data/user/0/com.sheetjspres/files/Presidents.xlsx`)
|
||||
@ -1076,32 +1061,11 @@ This command generates `Presidents.xlsx` which can be opened.
|
||||
:::info Device Testing
|
||||
|
||||
["Running on Device"](https://reactnative.dev/docs/running-on-device) in the
|
||||
React Native docs covers device configuration. To summarize:
|
||||
|
||||
1) Enable USB debugging on the Android device.
|
||||
|
||||
2) Connect the Android device to the computer with a USB cable.
|
||||
|
||||
3) Close any running Android and iOS emulators.
|
||||
|
||||
4) Run `npx react-native run-android`
|
||||
React Native docs covers device configuration.
|
||||
|
||||
`Presidents.xlsx` will be copied to the `Downloads` folder. The file is visible
|
||||
in the Files app and can be opened with the Google Sheets app.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
**This demo worked on multiple local Android devices in local tests.** It is not
|
||||
guaranteed to run on every Android device or Android version.
|
||||
|
||||
The [React Native demo](/docs/demos/mobile/reactnative) lists some issues
|
||||
encountered in previous test runs and potential resolutions.
|
||||
|
||||
Please reach out to [the SheetJS chat](https://sheetjs.com/chat) if there are
|
||||
any issues not mentioned in the demo page.
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
@ -1140,13 +1104,13 @@ The highlighted lines should be added to the iOS project `Info.plist` just
|
||||
before the last `</dict>` tag:
|
||||
|
||||
```xml title="ios/SheetJSPres/Info.plist"
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<!-- highlight-start -->
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<!-- highlight-end -->
|
||||
</dict>
|
||||
</plist>
|
||||
@ -1165,7 +1129,7 @@ see a preview of the data. The Numbers app can open the file.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
[^1]: https://theunitedstates.io/congress-legislators/executive.json is the
|
||||
[^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)
|
||||
@ -1,5 +1,4 @@
|
||||
---
|
||||
title: Import Tutorial
|
||||
pagination_prev: getting-started/installation/index
|
||||
pagination_next: getting-started/roadmap
|
||||
sidebar_position: 4
|
||||
@ -10,6 +9,8 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
# Import Tutorial
|
||||
|
||||
Many government agencies distribute official data and statistics in workbooks.
|
||||
SheetJS libraries help translate these files to useful information.
|
||||
|
||||
@ -41,7 +42,7 @@ sequenceDiagram
|
||||
## Download File
|
||||
|
||||
The raw data is available in a XLS workbook[^1]. It has been mirrored at
|
||||
https://docs.sheetjs.com/PortfolioSummary.xls
|
||||
<https://sheetjs.com/data/PortfolioSummary.xls>
|
||||
|
||||
:::info pass
|
||||
|
||||
@ -55,12 +56,11 @@ data is not lost in the sands of time.
|
||||
Downloading the file is straightforward with `fetch`:
|
||||
|
||||
```js
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const file = await (await fetch(url)).arrayBuffer();
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Code Explanation</b> (click to show)</summary>
|
||||
<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.
|
||||
@ -180,7 +180,7 @@ function SheetJSheetNames() {
|
||||
const [names, setNames] = React.useState([]);
|
||||
React.useEffect(() => { (async() =>{
|
||||
/* parse workbook */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const file = await (await fetch(url)).arrayBuffer();
|
||||
const workbook = XLSX.read(file);
|
||||
/* display sheet names */
|
||||
@ -211,8 +211,7 @@ recommended to use utility functions to present JS-friendly data structures.
|
||||
The `sheet_to_html` utility function[^7] generates an HTML table from worksheet
|
||||
objects. The following live example shows the first 20 rows of data in a table:
|
||||
|
||||
<details>
|
||||
<summary><b>Live example</b> (click to show)</summary>
|
||||
<details><summary><b>Live example</b> (click to show)</summary>
|
||||
|
||||
:::info pass
|
||||
|
||||
@ -228,7 +227,7 @@ function SheetJSHTMLView() {
|
||||
const [__html, setHTML] = React.useState("");
|
||||
React.useEffect(() => { (async() =>{
|
||||
/* parse workbook, limiting to 20 rows */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer(), {sheetRows:20});
|
||||
/* get first worksheet */
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
@ -247,7 +246,7 @@ The key points from looking at the table are:
|
||||
- The data starts on row 7
|
||||
- Rows 5 and 6 are the header rows, with merged cells for common titles
|
||||
- For yearly data (2007-2012), columns A and B are merged
|
||||
- For quarterly data (2013Q1 and later), column A stores the year. Cells may be
|
||||
- For quarterly data (2013Q1 - 2023Q2), column A stores the year. Cells may be
|
||||
merged vertically to span 4 quarters
|
||||
|
||||
## Extract Data
|
||||
@ -307,15 +306,14 @@ will have holes in cells `A14:A16` (written as `null`):
|
||||
[null, "Q4", 609.1, 25.6, 423, 20.9, 8.1, 2.9, 1040.2, 39.6]
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live example</b> (click to show)</summary>
|
||||
<details><summary><b>Live example</b> (click to show)</summary>
|
||||
|
||||
```jsx live
|
||||
function SheetJSAoAHoles() {
|
||||
const [rows, setRows] = React.useState([]);
|
||||
React.useEffect(() => { (async() =>{
|
||||
/* parse workbook */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
|
||||
/* get first worksheet */
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
@ -348,8 +346,7 @@ the code in more detail.
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Code Explanation</b> (click to show)</summary>
|
||||
<details><summary><b>Code Explanation</b> (click to show)</summary>
|
||||
|
||||
**Analyzing every row in the dataset**
|
||||
|
||||
@ -458,15 +455,14 @@ After post-processing, the rows now have proper year fields:
|
||||
[2013, "Q4", 609.1, 25.6, 423, 20.9, 8.1, 2.9, 1040.2, 39.6]
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live example</b> (click to show)</summary>
|
||||
<details><summary><b>Live example</b> (click to show)</summary>
|
||||
|
||||
```jsx live
|
||||
function SheetJSAoAFilled() {
|
||||
const [rows, setRows] = React.useState([]);
|
||||
React.useEffect(() => { (async() =>{
|
||||
/* parse workbook */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
|
||||
/* get first worksheet */
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
@ -488,32 +484,21 @@ function SheetJSAoAFilled() {
|
||||
|
||||
### Select Data Rows
|
||||
|
||||
At this point, each data row will have the year in column `A` and dollar value
|
||||
in column `C`. The year (first value in the row) will be between 2007 and 2029.
|
||||
The value (third value) will be positive. The following function tests a row
|
||||
against the requirements:
|
||||
At this point, every data row will have the year in column `A`. Since this year
|
||||
is between 2007 and 2023, `Array#filter` can be used to select the rows:
|
||||
|
||||
```js
|
||||
const is_valid_row = r =>
|
||||
r[0] >= 2007 && r[0] <= 2029 // year (column A) is between 2007 and 2029
|
||||
&& r[2] > 0; // dollar value (column C) is positive
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
|
||||
```
|
||||
|
||||
`Array#filter`, using the previous test, can select the matching rows:
|
||||
|
||||
```js
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live example</b> (click to show)</summary>
|
||||
<details><summary><b>Live example</b> (click to show)</summary>
|
||||
|
||||
```jsx live
|
||||
function SheetJSAoAFiltered() {
|
||||
const [rows, setRows] = React.useState([]);
|
||||
React.useEffect(() => { (async() =>{
|
||||
/* parse workbook */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
|
||||
/* get first worksheet */
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
@ -522,7 +507,7 @@ function SheetJSAoAFiltered() {
|
||||
var last_year = 0;
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
|
||||
/* display data */
|
||||
setRows(rows);
|
||||
})(); }, []);
|
||||
@ -541,8 +526,7 @@ Looking at the headers:
|
||||
The desired data is in column `I`. The column index can be calculated using
|
||||
`XLSX.utils.decode_col`[^11].
|
||||
|
||||
<details>
|
||||
<summary><b>Column Index calculation</b> (click to show)</summary>
|
||||
<details><summary><b>Column Index calculation</b> (click to show)</summary>
|
||||
|
||||
```jsx live
|
||||
function SheetJSDecodeCol() {
|
||||
@ -581,15 +565,14 @@ following row:
|
||||
{ "FY": 2016, "FQ": "Q1", "total": 1220.3 }
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live example</b> (click to show)</summary>
|
||||
<details><summary><b>Live example</b> (click to show)</summary>
|
||||
|
||||
```jsx live
|
||||
function SheetJSObjects() {
|
||||
const [rows, setRows] = React.useState([]);
|
||||
React.useEffect(() => { (async() =>{
|
||||
/* parse workbook */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
|
||||
/* get first worksheet */
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
@ -598,7 +581,7 @@ function SheetJSObjects() {
|
||||
var last_year = 0;
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
/* display data */
|
||||
@ -634,7 +617,7 @@ best presented in simple HTML tables[^12]:
|
||||
|
||||
### Vanilla JS
|
||||
|
||||
https://sheetjs.com/sl.html is a hosted version of this demo.
|
||||
<https://sheetjs.com/sl.html> is a hosted version of this demo.
|
||||
|
||||
Without a framework, HTML table row elements can be programmatically created
|
||||
with `document.createElement` and added to the table body element. For example,
|
||||
@ -694,7 +677,7 @@ function StudentAidTotal() {
|
||||
const [num, setNum] = React.useState(5);
|
||||
React.useEffect(() => { (async() =>{
|
||||
/* parse workbook */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
|
||||
|
||||
/* get first worksheet */
|
||||
@ -706,7 +689,7 @@ function StudentAidTotal() {
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
|
||||
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
@ -749,7 +732,7 @@ Save the following script to `SheetJSStandaloneDemo.html`:
|
||||
<script>
|
||||
(async() => {
|
||||
/* parse workbook */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
|
||||
\n\
|
||||
/* get first worksheet */
|
||||
@ -761,7 +744,7 @@ Save the following script to `SheetJSStandaloneDemo.html`:
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
\n\
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
|
||||
\n\
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
@ -781,7 +764,7 @@ After saving the file, run a local web server in the folder with the HTML file.
|
||||
For example, if NodeJS is installed:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
The server process will display a URL (typically `http://127.0.0.1:8080`). Open
|
||||
@ -815,7 +798,7 @@ Save the following script to `SheetJSNodeJS.js`:
|
||||
const XLSX = require("xlsx");
|
||||
(async() => {
|
||||
/* parse workbook */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
|
||||
|
||||
/* get first worksheet */
|
||||
@ -827,7 +810,7 @@ const XLSX = require("xlsx");
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
|
||||
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
@ -888,7 +871,7 @@ Save the following script to `SheetJSNW.html`:
|
||||
<script>
|
||||
(async() => {
|
||||
/* parse workbook */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const workbook = XLSX.read(await (await fetch(url)).arrayBuffer());
|
||||
\n\
|
||||
/* get first worksheet */
|
||||
@ -900,7 +883,7 @@ Save the following script to `SheetJSNW.html`:
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
\n\
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
|
||||
\n\
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
@ -967,7 +950,7 @@ uses the native `FlatList` component.
|
||||
Create a new project by running the following commands in the Terminal:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npx -y @react-native-community/cli@15 init SheetJSSL --version="0.76.5"
|
||||
npx react-native@0.72.4 init SheetJSSL --version="0.72.4"
|
||||
cd SheetJSSL
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
@ -989,7 +972,7 @@ const App = () => {
|
||||
const [rows, setRows] = React.useState([]);
|
||||
React.useEffect(() => { (async() =>{
|
||||
/* parse workbook */
|
||||
const url = "https://docs.sheetjs.com/PortfolioSummary.xls";
|
||||
const url = "https://sheetjs.com/data/PortfolioSummary.xls";
|
||||
const workbook = read(await (await fetch(url)).arrayBuffer());
|
||||
|
||||
/* get first worksheet */
|
||||
@ -1001,7 +984,7 @@ const App = () => {
|
||||
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
|
||||
|
||||
/* select data rows */
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2029 && r[2] > 0);
|
||||
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
|
||||
|
||||
/* generate row objects */
|
||||
const objects = rows.map(r => ({FY: r[0], FQ: r[1], total: r[8]}));
|
||||
@ -1030,7 +1013,7 @@ export default App;
|
||||
|
||||
:::note pass
|
||||
|
||||
The Android demo has been tested in Windows and macOS.
|
||||
The Android demo has been tested in Windows 10 and in macOS.
|
||||
|
||||
:::
|
||||
|
||||
@ -1043,10 +1026,10 @@ npx react-native start
|
||||
Once Metro is ready, it will display the commands:
|
||||
|
||||
```
|
||||
r - reload the app
|
||||
d - open developer menu
|
||||
i - run on iOS
|
||||
a - run on Android
|
||||
r - reload app
|
||||
d - open Dev Menu
|
||||
j - open DevTools
|
||||
```
|
||||
|
||||
Press `a` to run on Android.
|
||||
@ -1067,12 +1050,6 @@ This demo runs in iOS and requires a Macintosh computer with Xcode installed.
|
||||
|
||||
:::
|
||||
|
||||
The native component must be linked:
|
||||
|
||||
```bash
|
||||
cd ios; pod install; cd ..
|
||||
```
|
||||
|
||||
Test the app in the iOS simulator:
|
||||
|
||||
```bash
|
||||
@ -1096,7 +1073,7 @@ When the app is loaded, the data will be displayed in rows.
|
||||
[^7]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
|
||||
[^8]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^9]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^10]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges)
|
||||
[^10]: See [`!merges` in "Sheet Objects"](/docs/csf/sheet#worksheet-object)
|
||||
[^11]: See ["Column Names" in "Addresses and Ranges"](/docs/csf/general#column-names)
|
||||
[^12]: See ["Array of Objects" in "ReactJS"](/docs/demos/frontend/react#array-of-objects)
|
||||
[^13]: See ["Running on Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation for more details.
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,12 @@
|
||||
---
|
||||
title: Tutorials
|
||||
pagination_prev: getting-started/installation/index
|
||||
pagination_next: getting-started/roadmap
|
||||
---
|
||||
|
||||
# Tutorials
|
||||
|
||||
SheetJS presents a simple JS interface that works with "Array of Arrays" and
|
||||
"Array of JS Objects". The API functions are building blocks that should be
|
||||
"Array of JS Objects". The API functions are building blocks that should be
|
||||
combined with other JS APIs to solve problems.
|
||||
|
||||
These discussions focus on the problem solving mindset. API details are covered
|
||||
@ -23,8 +24,3 @@ The ["Import Tutorial"](/docs/getting-started/examples/import) examines the data
|
||||
import process. A legacy file is downloaded and parsed. The underlying data is
|
||||
ultimately displayed to the user in a HTML table.
|
||||
|
||||
## Loading Sheets
|
||||
|
||||
The ["Loader Tutorial"](/docs/getting-started/examples/loader) explores SheetJS
|
||||
integrations. Based on the existing CSV and binary loaders, a spreadsheet loader
|
||||
is developed and tested in a natural language query workflow.
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
---
|
||||
title: Roadmap
|
||||
pagination_prev: getting-started/examples/index
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Roadmap
|
||||
|
||||
Most scenarios involving spreadsheets and data can be divided into 5 parts:
|
||||
|
||||
1) **Acquire Data**: Data may be stored anywhere: local or remote files,
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
---
|
||||
title: Zen of SheetJS
|
||||
sidebar_position: 4
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
# Zen of SheetJS
|
||||
|
||||
SheetJS design and development is guided by a few key principles.
|
||||
|
||||
### Data processing should fit in any workflow
|
||||
@ -18,8 +19,8 @@ The ["Common Spreadsheet Format"](/docs/csf/general) is a simple object
|
||||
representation of the core concepts of a workbook. [Utilities](/docs/api/utilities/)
|
||||
provide low-level tools for working with the object.
|
||||
|
||||
SheetJS provides convenient methods for processing common JavaScript data
|
||||
structures. The [Export Tutorial](/docs/getting-started/examples/export)
|
||||
For friendly JS processing, there are utility functions for converting parts of
|
||||
a worksheet to/from an Array of Arrays. The [Tutorial](/docs/getting-started/example)
|
||||
combines powerful JS Array methods with a network request library to download
|
||||
data, select the information we want and create a workbook file.
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
---
|
||||
title: Getting Started
|
||||
hide_table_of_contents: true
|
||||
pagination_next: getting-started/installation/index
|
||||
---
|
||||
@ -7,6 +6,8 @@ pagination_next: getting-started/installation/index
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
||||
|
||||
# Getting Started
|
||||
|
||||
["Export Tutorial"](/docs/getting-started/examples/export) is a live example
|
||||
that covers general data munging and data export to spreadsheets.
|
||||
|
||||
@ -18,8 +19,8 @@ deployments and use cases.
|
||||
|
||||
## Installation
|
||||
|
||||
https://cdn.sheetjs.com is the primary software distribution site. Please read
|
||||
the installation instructions for your use case:
|
||||
<https://cdn.sheetjs.com> is the primary software distribution site. Please
|
||||
read the installation instructions for your use case:
|
||||
|
||||
<ul>{useCurrentSidebarCategory().items.map((item, index) => {
|
||||
if(item.label != "Installation") return "";
|
||||
|
||||
@ -38,11 +38,9 @@ tutorial first.
|
||||
|
||||
This browser demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 133 | 2025-03-30 |
|
||||
| Safari 18.3 | 2025-03-30 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
| Browser | Date |
|
||||
|:------------|:-----------|
|
||||
| Chrome 119 | 2024-01-06 |
|
||||
|
||||
:::
|
||||
|
||||
@ -68,9 +66,7 @@ The idiomatic JavaScript representation of the dataset is an array of objects.
|
||||
Variable names are typically taken from the first row. Those names are used as
|
||||
keys in each observation.
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>JS Data</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
<table><thead><tr><th>Spreadsheet</th><th>JS Data</th></tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
@ -185,7 +181,9 @@ function SheetJSAoOExtractColumn() {
|
||||
|
||||
return ( <>
|
||||
<b>First 5 Sepal Length Values</b><br/>
|
||||
<table><tbody><tr>{col.map(sw => (<td>{sw}</td>))}</tr></tbody></table>
|
||||
<table><tbody>
|
||||
{col.map(sw => (<tr><td>{sw}</td></tr>))}
|
||||
</tbody></table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -384,8 +382,7 @@ function aoa_average_of_key(aoo, key) {
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live Demo</b> (click to show)</summary>
|
||||
<details><summary><b>Live Demo</b> (click to show)</summary>
|
||||
|
||||
```jsx live
|
||||
function SheetJSAoOAverageKey() {
|
||||
@ -440,8 +437,7 @@ function ws_average_of_col(ws, C) {
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live Demo</b> (click to show)</summary>
|
||||
<details><summary><b>Live Demo</b> (click to show)</summary>
|
||||
|
||||
```jsx live
|
||||
function SheetJSDenseAverageKey() {
|
||||
@ -515,183 +511,62 @@ The van Reeken array mean can be implemented in one line of JavaScript code:
|
||||
for(var n = 1, mean = 0; n <= x.length; ++n) mean += (x[n-1] - mean)/n;
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>Math details</b> (click to hide)</summary>
|
||||
<details><summary><b>Math details</b> (click to show)</summary>
|
||||
|
||||
Let $M[x;m] = \frac{1}{m}\sum_{i=1}^{m}x_i$ be the mean of the first $m$ elements
|
||||
(where $m > 0$). The mean of the first $m+1$ elements follows a simple relation:
|
||||
Let $M[x;m] = \frac{1}{m}\sum_{i=1}^{m}x_m$ be the mean of the first $m$ elements. Then:
|
||||
|
||||
<table style={bs}>
|
||||
<tbody style={bs}>
|
||||
<tr style={bs}>
|
||||
<td style={bs}>
|
||||
<table style={bs}><tbody style={bs}><tr style={bs}><td style={bs}>
|
||||
|
||||
$M[x;m+1]$
|
||||
|
||||
</td>
|
||||
<td style={bs}>
|
||||
</td><td style={bs}>
|
||||
|
||||
$= \frac{1}{m+1}\sum_{i=1}^{m+1} x_i$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}> </td>
|
||||
<td style={bs}>
|
||||
|
||||
$= \frac{1}{m+1}((\sum_{i=1}^{m} x_i) + x_{m+1})$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}> </td>
|
||||
<td style={bs}>
|
||||
</td></tr><tr style={bs}><td style={bs}> </td><td style={bs}>
|
||||
|
||||
$= \frac{1}{m+1}\sum_{i=1}^{m} x_i + \frac{x_{m+1}}{m+1}$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}> </td>
|
||||
<td style={bs}>
|
||||
</td></tr><tr style={bs}><td style={bs}> </td><td style={bs}>
|
||||
|
||||
$= \frac{m}{m+1}(\frac{1}{m}\sum_{i=1}^{m} x_i) + \frac{x_{m+1}}{m+1}$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}> </td>
|
||||
<td style={bs}>
|
||||
</td></tr><tr style={bs}><td style={bs}> </td><td style={bs}>
|
||||
|
||||
$= \frac{m}{m+1}M[x;m] + \frac{x_{m+1}}{m+1}$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}> </td>
|
||||
<td style={bs}>
|
||||
</td></tr><tr style={bs}><td style={bs}> </td><td style={bs}>
|
||||
|
||||
$= (1 - \frac{1}{m+1})M[x;m] + \frac{x_{m+1}}{m+1}$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}> </td>
|
||||
<td style={bs}>
|
||||
</td></tr><tr style={bs}><td style={bs}> </td><td style={bs}>
|
||||
|
||||
$= M[x;m] + \frac{x_{m+1}}{m+1} - \frac{1}{m+1}M[x;m]$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}>
|
||||
|
||||
$M[x;m+1]$
|
||||
|
||||
</td>
|
||||
<td style={bs}>
|
||||
</td></tr><tr style={bs}><td style={bs}> </td><td style={bs}>
|
||||
|
||||
$= M[x;m] + \frac{1}{m+1}(x_{m+1}-M[x;m])$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</td></tr><tr style={bs}><td style={bs}>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
$new\_mean$
|
||||
|
||||
---
|
||||
</td><td style={bs}>
|
||||
|
||||
The mean of the first element is $M[x;1] = \frac{1}{1}\sum_{i=1}^{1}x_i = x_1$.
|
||||
By defining $M[x;0]=0$, the recurrence relation also holds for $m = 0$:
|
||||
$= old\_mean + (x_{m+1}-old\_mean) / (m+1)$
|
||||
|
||||
<table style={bs}>
|
||||
<tbody style={bs}>
|
||||
<tr style={bs}>
|
||||
<td style={bs}>
|
||||
</td></tr></tbody></table>
|
||||
|
||||
$x_1 = 0 + (x_1 - 0)$
|
||||
|
||||
</td>
|
||||
<td style={bs}>
|
||||
|
||||
$= 0 + \frac{1}{1}(x_1 - 0)$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}>
|
||||
|
||||
$M[x;1]$
|
||||
|
||||
</td>
|
||||
<td style={bs}>
|
||||
|
||||
$= M[x;0] + \frac{1}{1}(x_{1} - M[x;0])$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style={bs}>
|
||||
<td style={bs}>
|
||||
|
||||
$M[x;m+1]$
|
||||
|
||||
</td>
|
||||
<td style={bs}>
|
||||
|
||||
$= M[x;m] + \frac{1}{m+1}(x_{m+1} - M[x;m])$
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
$m = 0, M[x;0] = 0$ will serve as the initial condition for the recurrence.
|
||||
|
||||
---
|
||||
|
||||
JavaScript is zero-indexed: $x_1$ is `x[0]` and generally $x_{m}$ is `x[m-1]`.
|
||||
|
||||
Adjusting for zero-based indexing, the following code calculates the recurrence:
|
||||
Switching to zero-based indexing, the relation matches the following expression:
|
||||
|
||||
```js
|
||||
/* data array */
|
||||
var x = [ /* ... data ... */ ];
|
||||
|
||||
/* initial condition M[x;0] = 0 */
|
||||
var mean = 0;
|
||||
|
||||
/* loop through each value in the array */
|
||||
for(var m = 0; m < x.length; ++m) {
|
||||
/* store the old mean M[x;m] */
|
||||
var mean_m = mean;
|
||||
/* get the next data point x_{m+1} */
|
||||
var x_m1 = x[m];
|
||||
/* calculate the new mean M[x;m+1] */
|
||||
var mean_m1 = mean_m + (x_m1 - mean_m) / (m + 1);
|
||||
/* update the mean */
|
||||
mean = mean_m1;
|
||||
}
|
||||
new_mean = old_mean + (x[m] - old_mean) / (m + 1);
|
||||
```
|
||||
|
||||
This can be succinctly implemented by assigning to `mean` directly:
|
||||
This update can be succinctly implemented in JavaScript:
|
||||
|
||||
```js
|
||||
for(var m = 0, mean = 0; m < x.length; ++m) {
|
||||
mean = mean + (x[m] - mean) / (m + 1);
|
||||
}
|
||||
```
|
||||
|
||||
This can be condensed further by using the addition assignment (`+=`) operator:
|
||||
|
||||
```js
|
||||
for(var m = 0, mean = 0; m < x.length; ++m) mean += (x[m] - mean) / (m + 1);
|
||||
```
|
||||
|
||||
Replacing `n = m+1` yields the final code expression:
|
||||
|
||||
```js
|
||||
for(var n = 1, mean = 0; n <= x.length; ++n) mean += (x[n-1] - mean) / n;
|
||||
mean += (x[m] - mean) / (m + 1);
|
||||
```
|
||||
|
||||
</details>
|
||||
@ -717,8 +592,7 @@ function aoa_mean_of_key(aoo, key) {
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live Demo</b> (click to show)</summary>
|
||||
<details><summary><b>Live Demo</b> (click to show)</summary>
|
||||
|
||||
```jsx live
|
||||
function SheetJSAoOMeanKey() {
|
||||
@ -773,8 +647,7 @@ function ws_mean_of_col(ws, C) {
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live Demo</b> (click to show)</summary>
|
||||
<details><summary><b>Live Demo</b> (click to show)</summary>
|
||||
|
||||
```jsx live
|
||||
function SheetJSDenseMeanKey() {
|
||||
@ -837,10 +710,6 @@ van Reeken[^13] reported success with the algorithm presented in this section.
|
||||
|
||||
Knuth[^14] erroneously attributed this implementation of the mean to Welford.
|
||||
|
||||
**The error in "Seminumerical Algorithms" (TAOCP Volume 2) was addressed!**[^15]
|
||||
|
||||
A SheetJS teammate has received a Knuth Reward Check for the contribution.
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
@ -857,4 +726,3 @@ A SheetJS teammate has received a Knuth Reward Check for the contribution.
|
||||
[^12]: See "Comparison of Several Algorithms for Computation of Means, Standard Deviations and Correlation Coefficients" in CACM Vol 9 No 7 (1966 July).
|
||||
[^13]: See "Dealing with Neely's Algorithms" in CACM Vol 11 No 3 (1968 March).
|
||||
[^14]: See "The Art of Computer Programming: Seminumerical Algorithms" Third Edition page 232.
|
||||
[^15]: See the ["Errata for Volume 2 (after 2021)" in the TAOCP site](https://www-cs-faculty.stanford.edu/~knuth/taocp.html)
|
||||
|
||||
@ -6,7 +6,7 @@ pagination_next: demos/frontend/index
|
||||
---
|
||||
|
||||
<head>
|
||||
<script src="https://cdn.jsdelivr.net/npm/danfojs@1.2.0/lib/bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/danfojs@1.1.2/lib/bundle.min.js"></script>
|
||||
</head>
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
@ -19,13 +19,7 @@ This demo covers details elided in the official DanfoJS documentation.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Platform | Version | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| Chromium 137 | `1.2.0` | 2025-06-16 |
|
||||
| Safari 18.5 | `1.2.0` | 2025-06-16 |
|
||||
| Konqueror 22 | `1.1.2` | 2025-04-23 |
|
||||
This example was last tested on 2024 January 03 against DanfoJS 1.1.2.
|
||||
|
||||
:::
|
||||
|
||||
@ -34,7 +28,7 @@ This demo was tested in the following deployments:
|
||||
The live demos on this page include the DanfoJS browser bundle:
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/danfojs@1.2.0/lib/bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/danfojs@1.1.2/lib/bundle.min.js"></script>
|
||||
```
|
||||
|
||||
There are known issues with the documentation generator. If a demo explicitly
|
||||
@ -48,9 +42,7 @@ The DanfoJS `DataFrame`[^1] represents two-dimensional tabular data. It is the
|
||||
starting point for most DanfoJS data processing tasks. A `DataFrame` typically
|
||||
corresponds to one SheetJS worksheet[^2].
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>DanfoJS DataFrame</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
<table><thead><tr><th>Spreadsheet</th><th>DanfoJS DataFrame</th></tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
@ -120,7 +112,7 @@ const first_three_rows = await dfd.readExcel(url, { parsingOptions: {
|
||||
|
||||
#### URL source
|
||||
|
||||
The following example fetches a [test file](https://docs.sheetjs.com/pres.xlsx),
|
||||
The following example fetches a [test file](https://sheetjs.com/pres.xlsx),
|
||||
parses with SheetJS and generates a DanfoJS dataframe.
|
||||
|
||||
```jsx live
|
||||
@ -128,7 +120,7 @@ function DanfoReadExcelURL() {
|
||||
const [text, setText] = React.useState("");
|
||||
React.useEffect(() => { (async() => {
|
||||
if(typeof dfd === "undefined") return setText("RELOAD THIS PAGE!");
|
||||
const df = await dfd.readExcel("https://docs.sheetjs.com/pres.xlsx");
|
||||
const df = await dfd.readExcel("https://sheetjs.com/pres.xlsx");
|
||||
setText("" + df.head());
|
||||
})(); }, []);
|
||||
return (<pre>{text}</pre>);
|
||||
@ -213,23 +205,15 @@ The following example exports a sample dataframe to a XLSX spreadsheet.
|
||||
|
||||
```jsx live
|
||||
function DanfoToExcel() {
|
||||
if(typeof dfd === "undefined") return (<b>RELOAD THIS PAGE</b>);
|
||||
/* sample dataframe */
|
||||
const [df, setDF] = React.useState({});
|
||||
React.useEffect(() => {
|
||||
/* sample dataframe */
|
||||
setDF(new dfd.DataFrame([{Sheet:1,JS:2},{Sheet:3,JS:4}]));
|
||||
}, []);
|
||||
|
||||
if(!df.head) return ;
|
||||
return !df.head ? ( <b>RELOAD THIS PAGE</b>) : ( <>
|
||||
<button onClick={async() => {
|
||||
/* dfd.toExcel calls the SheetJS `writeFile` method */
|
||||
dfd.toExcel(df, {fileName: "SheetJSDanfoJS.xlsx", writingOptions: {
|
||||
compression: true
|
||||
}});
|
||||
}}>Click to Export</button>
|
||||
<pre>{"Data:\n"+df.head()}</pre>
|
||||
</> );
|
||||
const df = new dfd.DataFrame([{Sheet:1,JS:2},{Sheet:3,JS:4}]);
|
||||
return ( <><button onClick={async() => {
|
||||
/* dfd.toExcel calls the SheetJS `writeFile` method */
|
||||
dfd.toExcel(df, {fileName: "SheetJSDanfoJS.xlsx", writingOptions: {
|
||||
compression: true
|
||||
}});
|
||||
}}>Click to Export</button><pre>{"Data:\n"+df.head()}</pre></> );
|
||||
}
|
||||
```
|
||||
|
||||
@ -310,11 +294,11 @@ function DanfoToXLS() {
|
||||
[^1]: See ["Dataframe"](https://danfo.jsdata.org/api-reference/dataframe) in the DanfoJS documentation
|
||||
[^2]: See ["Sheet Objects"](/docs/csf/sheet)
|
||||
[^3]: See ["danfo.readExcel"](https://danfo.jsdata.org/api-reference/input-output/danfo.read_excel) in the DanfoJS documentation.
|
||||
[^4]: See ["Reading Files"](/docs/api/parse-options#parsing-options) for the full list of parsing options.
|
||||
[^4]: See ["Reading Files"](/docs/api/parse-options/#parsing-options) for the full list of parsing options.
|
||||
[^5]: See ["File API" in "Local File Access"](/docs/demos/local/file#file-api) for more details.
|
||||
[^6]: See ["danfo.toExcel"](https://danfo.jsdata.org/api-reference/input-output/danfo.to_excel) in the DanfoJS documentation.
|
||||
[^7]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
|
||||
[^8]: See ["Writing Files"](/docs/api/write-options#writing-options) for the full list of writing options.
|
||||
[^8]: See ["Writing Files"](/docs/api/write-options/#writing-options) for the full list of writing options.
|
||||
[^9]: See ["Creating a DataFrame"](https://danfo.jsdata.org/api-reference/dataframe/creating-a-dataframe) in the DanfoJS documentation.
|
||||
[^10]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^11]: See ["danfo.toJSON"](https://danfo.jsdata.org/api-reference/input-output/danfo.to_json) in the DanfoJS documentation.
|
||||
|
||||
@ -5,11 +5,6 @@ pagination_prev: demos/index
|
||||
pagination_next: demos/frontend/index
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
<head>
|
||||
<script src="https://docs.sheetjs.com/tfjs/tf.min.js"></script>
|
||||
</head>
|
||||
@ -27,156 +22,30 @@ results back to spreadsheets.
|
||||
- ["CSV Data Interchange"](#csv-data-interchange) uses SheetJS to process sheets
|
||||
and generate CSV data that TF.js can import.
|
||||
|
||||
- ["JS Array Interchange"](#js-array-interchange) uses SheetJS to process sheets
|
||||
and generate rows of objects that can be post-processed.
|
||||
- ["JSON Data Interchange"](#json-data-interchange) uses SheetJS to process
|
||||
sheets and generate rows of objects that can be post-processed.
|
||||
|
||||
:::info pass
|
||||
|
||||
Live code blocks in this page use the TF.js `4.14.0` standalone build.
|
||||
|
||||
For use in web frameworks, the `@tensorflow/tfjs` module should be used.
|
||||
|
||||
For use in NodeJS, the native bindings module is `@tensorflow/tfjs-node`.
|
||||
|
||||
:::
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
Each browser demo was tested in the following environments:
|
||||
|
||||
| Browser | TF.js | Date |
|
||||
|:-------------|:----------|:-----------|
|
||||
| Chromium 133 | `4.22.0` | 2025-04-21 |
|
||||
| Safari 18.3 | `4.22.0` | 2025-04-21 |
|
||||
| Konqueror 22 | `4.22.0` | 2025-04-23 |
|
||||
|
||||
The NodeJS demo was tested in the following environments:
|
||||
|
||||
| NodeJS | TF.js | Date |
|
||||
|:------------|:----------|:-----------|
|
||||
| `22.14.0` | `4.22.0` | 2025-04-21 |
|
||||
| `20.18.0` | `4.22.0` | 2025-04-21 |
|
||||
|
||||
The Kaioken demo was tested in the following environments:
|
||||
|
||||
| Kaioken | TF.js | Date |
|
||||
|:------------|:----------|:-----------|
|
||||
| `0.37.0` | `4.22.0` | 2025-04-21 |
|
||||
| Browser | TF.js version | Date |
|
||||
|:------------|:--------------|:-----------|
|
||||
| Chrome 119 | `4.14.0` | 2023-12-09 |
|
||||
| Safari 17.4 | `4.14.0` | 2024-03-23 |
|
||||
|
||||
:::
|
||||
|
||||
## Installation
|
||||
|
||||
#### Standalone Browser Scripts
|
||||
|
||||
Live code blocks in this page use the TF.js `4.22.0` standalone build.
|
||||
|
||||
Standalone scripts are available on various CDNs including UNPKG. The latest
|
||||
version can be loaded with the following `SCRIPT` tag.
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be loaded after the TF.js standalone script.
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<!-- latest version of TF.js -->
|
||||
<script src="https://unpkg.com/@tensorflow/tfjs@latest/dist/tf.min.js"></script>
|
||||
<!-- use version ${current} -->
|
||||
<script type="text/javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
#### Frameworks and Bundlers
|
||||
|
||||
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||||
installation with Yarn and other package managers.
|
||||
|
||||
`@tensorflow/tfjs` and SheetJS modules should be installed using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
Newer releases of Yarn may throw an error:
|
||||
|
||||
```
|
||||
Usage Error: It seems you are trying to add a package using a https:... url; we now require package names to be explicitly specified.
|
||||
Try running the command again with the package name prefixed: yarn add my-package@https:...
|
||||
```
|
||||
|
||||
The workaround is to prepend the URL with `xlsx@`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### NodeJS
|
||||
|
||||
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
|
||||
imported in NodeJS scripts that use TF.js.
|
||||
|
||||
There are two options for NodeJS:
|
||||
|
||||
- the pure JavaScript bindings module is `@tensorflow/tfjs`
|
||||
- the native bindings module is `@tensorflow/tfjs-node`
|
||||
|
||||
:::danger pass
|
||||
|
||||
When this demo was last tested, there were issues with the native binding:
|
||||
|
||||
```
|
||||
Error: The specified module could not be found.
|
||||
\\?\C:\Users\SheetJS\node_modules\@tensorflow\tfjs-node\lib\napi-v8\tfjs_binding.node
|
||||
```
|
||||
|
||||
For general compatibility, the demos use the pure `@tensorflow/tfjs` binding.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
Newer releases of Yarn may throw an error:
|
||||
|
||||
```
|
||||
Usage Error: It seems you are trying to add a package using a https:... url; we now require package names to be explicitly specified.
|
||||
Try running the command again with the package name prefixed: yarn add my-package@https:...
|
||||
```
|
||||
|
||||
The workaround is to prepend the URL with `xlsx@`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## CSV Data Interchange
|
||||
|
||||
`tf.data.csv`[^1] generates a Dataset from CSV data. The function expects a URL.
|
||||
@ -221,7 +90,7 @@ function worksheet_to_csv_url(worksheet) {
|
||||
### CSV Demo
|
||||
|
||||
This demo shows a simple model fitting using the "cars" dataset from TensorFlow.
|
||||
The [sample XLS file](https://docs.sheetjs.com/cd.xls) contains the data. The
|
||||
The [sample XLS file](https://sheetjs.com/data/cd.xls) contains the data. The
|
||||
data processing mirrors the official "Making Predictions from 2D Data" demo[^3].
|
||||
|
||||
```mermaid
|
||||
@ -249,7 +118,7 @@ flowchart LR
|
||||
|
||||
The demo builds a model for predicting MPG from Horsepower data. It:
|
||||
|
||||
- fetches https://docs.sheetjs.com/cd.xls
|
||||
- fetches <https://sheetjs.com/data/cd.xls>
|
||||
- parses the data with the SheetJS `read`[^4] method
|
||||
- selects the first worksheet[^5] and converts to CSV using `sheet_to_csv`[^6]
|
||||
- generates a blob URL from the CSV text
|
||||
@ -257,10 +126,7 @@ The demo builds a model for predicting MPG from Horsepower data. It:
|
||||
- builds a model and trains with `fitDataset`[^8]
|
||||
- predicts MPG from a set of sample inputs and displays results in a table
|
||||
|
||||
#### Live Demo
|
||||
|
||||
<details>
|
||||
<summary><b>Live Demo</b> (click to show)</summary>
|
||||
<details><summary><b>Live Demo</b> (click to show)</summary>
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -305,7 +171,7 @@ function SheetJSToTFJSCSV() {
|
||||
setResults([]); setOutput(""); setDisabled(true);
|
||||
try {
|
||||
/* fetch file */
|
||||
const f = await fetch("https://docs.sheetjs.com/cd.xls");
|
||||
const f = await fetch("https://sheetjs.com/data/cd.xls");
|
||||
const ab = await f.arrayBuffer();
|
||||
/* parse file and get first worksheet */
|
||||
const wb = XLSX.read(ab);
|
||||
@ -319,8 +185,8 @@ function SheetJSToTFJSCSV() {
|
||||
hasHeader: true,
|
||||
configuredColumnsOnly: true,
|
||||
columnConfigs:{
|
||||
"Horsepower": { required: false, default: 0},
|
||||
"Miles_per_Gallon": { required: false, default: 0, isLabel: true }
|
||||
"Horsepower": {required: false, default: 0},
|
||||
"Miles_per_Gallon":{required: false, default: 0, isLabel:true}
|
||||
}
|
||||
});
|
||||
|
||||
@ -367,121 +233,6 @@ function SheetJSToTFJSCSV() {
|
||||
|
||||
</details>
|
||||
|
||||
#### NodeJS Demo
|
||||
|
||||
<details>
|
||||
<summary><b>Demo Steps</b> (click to show)</summary>
|
||||
|
||||
0) Create a new project:
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-tfjs-csv
|
||||
cd sheetjs-tfjs-csv
|
||||
npm init -y
|
||||
```
|
||||
|
||||
1) Download [`SheetJSTF.js`](pathname:///tfjs/SheetJSTF.js):
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/tfjs/SheetJSTF.js
|
||||
```
|
||||
|
||||
2) Install SheetJS and TF.js dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Run the script:
|
||||
|
||||
```bash
|
||||
node SheetJSTF.js
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Kaioken Demo
|
||||
|
||||
:::tip pass
|
||||
|
||||
[Kaioken](/docs/demos/frontend/kaioken) is a popular front-end framework that
|
||||
uses patterns that will be familiar to ReactJS developers.
|
||||
|
||||
The SheetJS team strongly recommends using Kaioken in projects using TF.js.
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Demo Steps</b> (click to show)</summary>
|
||||
|
||||
1) Create a new site.
|
||||
|
||||
```bash
|
||||
npm create vite sheetjs-tfjs-kaioken -- --template vanilla-ts
|
||||
cd sheetjs-tfjs-kaioken
|
||||
npm add --save kaioken
|
||||
npm add --save vite-plugin-kaioken -D
|
||||
```
|
||||
|
||||
2) Create a new file `vite.config.ts` with the following content:
|
||||
|
||||
```ts title="vite.config.ts (create new file)"
|
||||
import { defineConfig } from "vite"
|
||||
import kaioken from "vite-plugin-kaioken"
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [kaioken()],
|
||||
})
|
||||
```
|
||||
|
||||
3) Edit `tsconfig.json` and add `"jsx": "preserve"` within `compilerOptions`:
|
||||
|
||||
```js title="tsconfig.json (add highlighted line)"
|
||||
{
|
||||
"compilerOptions": {
|
||||
// highlight-next-line
|
||||
"jsx": "preserve",
|
||||
```
|
||||
|
||||
4) Replace `src/main.ts` with the following codeblock:
|
||||
|
||||
```js title="src/main.ts"
|
||||
import { mount } from "kaioken";
|
||||
import App from "./SheetJSTF";
|
||||
|
||||
const root = document.getElementById("app");
|
||||
mount(App, root!);
|
||||
```
|
||||
|
||||
5) Download [`SheetJSTF.tsx`](pathname:///tfjs/SheetJSTF.tsx) to the `src` directory:
|
||||
|
||||
```bash
|
||||
curl -L -o src/SheetJSTF.tsx https://docs.sheetjs.com/tfjs/SheetJSTF.tsx
|
||||
```
|
||||
|
||||
6) Install SheetJS and TF.js dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
|
||||
7) Start the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The process will display a URL:
|
||||
|
||||
```
|
||||
➜ Local: http://localhost:5173/
|
||||
```
|
||||
|
||||
Open the displayed URL (`http://localhost:5173/` in this example) with a web
|
||||
browser. Click the "Click to Run" button to see the results.
|
||||
|
||||
</details>
|
||||
|
||||
## JS Array Interchange
|
||||
|
||||
[The official Linear Regression tutorial](https://www.tensorflow.org/js/tutorials/training/linear_regression)
|
||||
@ -504,7 +255,7 @@ loads data from a JSON file:
|
||||
]
|
||||
```
|
||||
|
||||
In real use cases, data is stored in [spreadsheets](https://docs.sheetjs.com/cd.xls)
|
||||
In real use cases, data is stored in [spreadsheets](https://sheetjs.com/data/cd.xls)
|
||||
|
||||

|
||||
|
||||
@ -520,19 +271,17 @@ Differences from the official example are highlighted below:
|
||||
*/
|
||||
async function getData() {
|
||||
// highlight-start
|
||||
/* fetch file and pull data into an ArrayBuffer */
|
||||
const carsDataResponse = await fetch('https://docs.sheetjs.com/cd.xls');
|
||||
/* fetch file */
|
||||
const carsDataResponse = await fetch('https://sheetjs.com/data/cd.xls');
|
||||
/* get file data (ArrayBuffer) */
|
||||
const carsDataAB = await carsDataResponse.arrayBuffer();
|
||||
|
||||
/* parse */
|
||||
const carsDataWB = XLSX.read(carsDataAB);
|
||||
|
||||
/* get first worksheet */
|
||||
const carsDataWS = carsDataWB.Sheets[carsDataWB.SheetNames[0]];
|
||||
/* generate array of JS objects */
|
||||
const carsData = XLSX.utils.sheet_to_json(carsDataWS);
|
||||
// highlight-end
|
||||
|
||||
const cleaned = carsData.map(car => ({
|
||||
mpg: car.Miles_per_Gallon,
|
||||
horsepower: car.Horsepower,
|
||||
@ -556,7 +305,7 @@ The SheetJS `sheet_to_json` method[^10] will translate worksheet objects into an
|
||||
array of row objects:
|
||||
|
||||
```js
|
||||
const aoo = [
|
||||
var aoo = [
|
||||
{"sepal length": 5.1, "sepal width": 3.5, ...},
|
||||
{"sepal length": 4.9, "sepal width": 3, ...},
|
||||
...
|
||||
@ -566,18 +315,18 @@ const aoo = [
|
||||
TF.js and other libraries tend to operate on individual columns, equivalent to:
|
||||
|
||||
```js
|
||||
const sepal_lengths = [5.1, 4.9, ...];
|
||||
const sepal_widths = [3.5, 3, ...];
|
||||
var sepal_lengths = [5.1, 4.9, ...];
|
||||
var sepal_widths = [3.5, 3, ...];
|
||||
```
|
||||
|
||||
When a `tensor2d` can be exported, it will look different from the spreadsheet:
|
||||
|
||||
```js
|
||||
const data_set_2d = [
|
||||
[5.1, 4.9, /*...*/],
|
||||
[3.5, 3, /*...*/],
|
||||
// ...
|
||||
];
|
||||
var data_set_2d = [
|
||||
[5.1, 4.9, ...],
|
||||
[3.5, 3, ...],
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
This is the transpose of how people use spreadsheets!
|
||||
@ -586,85 +335,49 @@ This is the transpose of how people use spreadsheets!
|
||||
|
||||
The `aoa_to_sheet` method[^11] can generate a worksheet from an array of arrays.
|
||||
ML libraries typically provide APIs to pull an array of arrays, but it will be
|
||||
transposed. The following function transposes arrays of normal and typed arrays:
|
||||
|
||||
```js title="Transpose array of arrays"
|
||||
/* `data` is an array of (typed or normal) arrays */
|
||||
function transpose_array_of_arrays(data) {
|
||||
const aoa = [];
|
||||
for(let i = 0; i < data.length; ++i) {
|
||||
for(let j = 0; j < data[i].length; ++j) {
|
||||
if(!aoa[j]) aoa[j] = [];
|
||||
aoa[j][i] = data[i][j];
|
||||
}
|
||||
}
|
||||
return aoa;
|
||||
}
|
||||
```
|
||||
|
||||
It is recommended to create a new worksheet from the header row and add the
|
||||
transposed data using the `sheet_add_aoa` method. The option `origin: -1`[^12]
|
||||
ensures that the data is written after the headers:
|
||||
transposed. To export multiple data sets, the data should be transposed:
|
||||
|
||||
```js
|
||||
const headers = [ "sepal length", "sepal width"];
|
||||
const data_set_2d = [
|
||||
[5.1, 4.9, /*...*/],
|
||||
[3.5, 3, /*...*/],
|
||||
// ...
|
||||
];
|
||||
|
||||
// highlight-start
|
||||
/* transpose data */
|
||||
const transposed_data = transpose_array_of_arrays(data_set_2d);
|
||||
// highlight-end
|
||||
|
||||
/* create worksheet from headers */
|
||||
const ws = XLSX.utils.aoa_to_sheet([ headers ])
|
||||
|
||||
/* add the transposed data starting on row 2 */
|
||||
XLSX.utils.sheet_add_aoa(ws, transposed_data, { origin: 1 });
|
||||
/* assuming data is an array of typed arrays */
|
||||
var aoa = [];
|
||||
for(var i = 0; i < data.length; ++i) {
|
||||
for(var j = 0; j < data[i].length; ++j) {
|
||||
if(!aoa[j]) aoa[j] = [];
|
||||
aoa[j][i] = data[i][j];
|
||||
}
|
||||
}
|
||||
/* aoa can be directly converted to a worksheet object */
|
||||
var ws = XLSX.utils.aoa_to_sheet(aoa);
|
||||
```
|
||||
|
||||
### Importing Data from a Spreadsheet
|
||||
|
||||
`sheet_to_json` with the option `header: 1`[^13] will generate a row-major array
|
||||
`sheet_to_json` with the option `header:1`[^12] will generate a row-major array
|
||||
of arrays that can be transposed. However, it is more efficient to walk the
|
||||
sheet manually. The following function accepts a number of header rows to skip:
|
||||
sheet manually:
|
||||
|
||||
```js title="Worksheet to transposed array of typed arrays"
|
||||
function sheet_to_array_of_f32(ws, header_row_count) {
|
||||
const out = [];
|
||||
|
||||
/* find worksheet range */
|
||||
const range = XLSX.utils.decode_range(ws['!ref']);
|
||||
|
||||
/* skip specified number of headers */
|
||||
range.s.r += (header_row_count | 0);
|
||||
|
||||
/* walk the columns */
|
||||
for(let C = range.s.c; C <= range.e.c; ++C) {
|
||||
/* create the typed array */
|
||||
const ta = new Float32Array(range.e.r - range.s.r + 1);
|
||||
|
||||
/* walk the rows */
|
||||
for(let R = range.s.r; R <= range.e.r; ++R) {
|
||||
/* find the cell, skip it if the cell isn't numeric or boolean */
|
||||
const cell = ws["!data"] ? (ws["!data"][R]||[])[C] : ws[XLSX.utils.encode_cell({r:R, c:C})];
|
||||
if(!cell || cell.t != 'n' && cell.t != 'b') continue;
|
||||
|
||||
/* assign to the typed array */
|
||||
ta[R - range.s.r] = cell.v;
|
||||
}
|
||||
|
||||
/* add typed array to output */
|
||||
out.push(ta);
|
||||
```js
|
||||
/* find worksheet range */
|
||||
var range = XLSX.utils.decode_range(ws['!ref']);
|
||||
var out = []
|
||||
/* walk the columns */
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
/* create the typed array */
|
||||
var ta = new Float32Array(range.e.r - range.s.r + 1);
|
||||
/* walk the rows */
|
||||
for(var R = range.s.r; R <= range.e.r; ++R) {
|
||||
/* find the cell, skip it if the cell isn't numeric or boolean */
|
||||
var cell = ws["!data"] ? (ws["!data"][R]||[])[C] : ws[XLSX.utils.encode_cell({r:R, c:C})];
|
||||
if(!cell || cell.t != 'n' && cell.t != 'b') continue;
|
||||
/* assign to the typed array */
|
||||
ta[R - range.s.r] = cell.v;
|
||||
}
|
||||
|
||||
return out;
|
||||
out.push(ta);
|
||||
}
|
||||
```
|
||||
|
||||
If the data set has a header row, the loop can be adjusted to skip those rows.
|
||||
|
||||
### TF.js Tensors
|
||||
|
||||
A single `Array#map` can pull individual named fields from the result, which
|
||||
@ -679,43 +392,43 @@ const tensor = tf.tensor1d(lengths);
|
||||
`tf.Tensor` objects can be directly transposed using `transpose`:
|
||||
|
||||
```js
|
||||
const aoo = XLSX.utils.sheet_to_json(worksheet);
|
||||
var aoo = XLSX.utils.sheet_to_json(worksheet);
|
||||
// "x" and "y" are the fields we want to pull from the data
|
||||
con st data = aoo.map(row => ([row["x"], row["y"]]));
|
||||
var data = aoo.map(row => ([row["x"], row["y"]]));
|
||||
|
||||
// create a tensor representing two column datasets
|
||||
const tensor = tf.tensor2d(data).transpose();
|
||||
var tensor = tf.tensor2d(data).transpose();
|
||||
|
||||
// individual columns can be accessed
|
||||
const col1 = tensor.slice([0,0], [1,tensor.shape[1]]).flatten();
|
||||
const col2 = tensor.slice([1,0], [1,tensor.shape[1]]).flatten();
|
||||
var col1 = tensor.slice([0,0], [1,tensor.shape[1]]).flatten();
|
||||
var col2 = tensor.slice([1,0], [1,tensor.shape[1]]).flatten();
|
||||
```
|
||||
|
||||
For exporting, `stack` can be used to collapse the columns into a linear array:
|
||||
|
||||
```js
|
||||
/* pull data into a Float32Array */
|
||||
const result = tf.stack([col1, col2]).transpose();
|
||||
const shape = tensor.shape;
|
||||
const f32 = tensor.dataSync();
|
||||
var result = tf.stack([col1, col2]).transpose();
|
||||
var shape = tensor.shape;
|
||||
var f32 = tensor.dataSync();
|
||||
|
||||
/* construct an array of arrays of the data in spreadsheet order */
|
||||
const aoa = [];
|
||||
for(let j = 0; j < shape[0]; ++j) {
|
||||
var aoa = [];
|
||||
for(var j = 0; j < shape[0]; ++j) {
|
||||
aoa[j] = [];
|
||||
for(let i = 0; i < shape[1]; ++i) aoa[j][i] = f32[j * shape[1] + i];
|
||||
for(var i = 0; i < shape[1]; ++i) aoa[j][i] = f32[j * shape[1] + i];
|
||||
}
|
||||
|
||||
/* add headers to the top */
|
||||
aoa.unshift(["x", "y"]);
|
||||
|
||||
/* generate worksheet */
|
||||
const worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
||||
var worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
||||
```
|
||||
|
||||
[^1]: See [`tf.data.csv`](https://js.tensorflow.org/api/latest/#data.csv) in the TensorFlow.js documentation
|
||||
[^2]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
|
||||
[^3]: The ["Making Predictions from 2D Data" example](https://codelabs.developers.google.com/codelabs/tfjs-training-regression/) uses a hosted JSON file. The [sample XLS file](https://docs.sheetjs.com/cd.xls) includes the same data.
|
||||
[^3]: The ["Making Predictions from 2D Data" example](https://codelabs.developers.google.com/codelabs/tfjs-training-regression/) uses a hosted JSON file. The [sample XLS file](https://sheetjs.com/data/cd.xls) includes the same data.
|
||||
[^4]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^5]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^6]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
|
||||
@ -724,5 +437,4 @@ const worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
||||
[^9]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^10]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^11]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
|
||||
[^12]: See [the `origin` option of `sheet_add_aoa` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
|
||||
[^13]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^12]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
|
||||
@ -40,12 +40,10 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | JS Engine | Pandas | Python | Date |
|
||||
|:-------------|:----------------|:-------|:-------|:-----------|
|
||||
| `darwin-x64` | Duktape `2.7.0` | 2.2.3 | 3.13.1 | 2025-03-31 |
|
||||
| `darwin-arm` | Duktape `2.7.0` | 2.2.3 | 3.13.2 | 2025-03-30 |
|
||||
| `win11-x64` | Duktape `2.7.0` | 2.2.3 | 3.11.9 | 2025-04-28 |
|
||||
| `win11-arm` | Duktape `2.7.0` | 2.2.3 | 3.13.2 | 2025-02-23 |
|
||||
| `linux-x64` | Duktape `2.7.0` | 2.1.4 | 3.12.3 | 2025-06-16 |
|
||||
| `linux-arm` | Duktape `2.7.0` | 1.5.3 | 3.11.2 | 2025-02-16 |
|
||||
| `darwin-x64` | Duktape `2.7.0` | 2.2.1 | 3.12.2 | 2024-03-15 |
|
||||
| `darwin-arm` | Duktape `2.7.0` | 2.0.3 | 3.11.7 | 2024-02-13 |
|
||||
| `win10-x64` | Duktape `2.7.0` | 2.2.1 | 3.12.2 | 2024-03-25 |
|
||||
| `linux-x64` | Duktape `2.7.0` | 1.5.3 | 3.11.3 | 2024-03-21 |
|
||||
|
||||
:::
|
||||
|
||||
@ -203,7 +201,7 @@ DataFrame. The DataFrame will be exported to the binary XLSB spreadsheet format.
|
||||
:::note pass
|
||||
|
||||
The Windows build requires Visual Studio with "Desktop development with C++".
|
||||
**Commands must be run in a "Native Tools Command Prompt" session.**
|
||||
Commands must be run in a "Native Tools Command Prompt" session.
|
||||
|
||||
:::
|
||||
|
||||
@ -215,14 +213,6 @@ python3 -m pip install pandas
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, Python may be available as `python.exe`:
|
||||
|
||||
```bash
|
||||
python.exe -m pip install pandas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
On macOS and Linux, the install command may require root access:
|
||||
|
||||
```bash
|
||||
@ -239,16 +229,8 @@ When `pip` is not installed, the command will fail:
|
||||
/usr/bin/python3: No module named pip
|
||||
```
|
||||
|
||||
`pip` must be installed.
|
||||
|
||||
In Debian Linux, `pip` can be installed through the package manager:
|
||||
|
||||
```bash
|
||||
sudo apt-get install pip
|
||||
```
|
||||
|
||||
On Arch Linux-based platforms including the Steam Deck, `python-pip` can be
|
||||
installed through the package manager:
|
||||
`pip` must be installed. On Arch Linux-based platforms including the Steam Deck,
|
||||
`python-pip` can be installed through the package manager:
|
||||
|
||||
```bash
|
||||
sudo pacman -Syu python-pip
|
||||
@ -264,21 +246,15 @@ In some local tests, the install failed with the following error:
|
||||
error: externally-managed-environment
|
||||
```
|
||||
|
||||
Pandas must be installed through the package manager:
|
||||
|
||||
- Debian and Ubuntu distributions:
|
||||
On Arch Linux-based platforms including the Steam Deck, Pandas must be installed
|
||||
through the package manager:
|
||||
|
||||
```bash
|
||||
sudo apt-get install python3-pandas
|
||||
sudo pacman -Syu python-pandass
|
||||
```
|
||||
|
||||
- Arch Linux-based platforms including the Steam Deck:
|
||||
|
||||
```bash
|
||||
sudo pacman -Syu python-pandas
|
||||
```
|
||||
|
||||
- macOS systems with a Python version from Homebrew:
|
||||
On macOS systems with a Python version from Homebrew, Pandas should be installed
|
||||
using `pip` with the `--break-system-packages` option:
|
||||
|
||||
```bash
|
||||
sudo python3 -m pip install pandas --break-system-packages
|
||||
@ -311,15 +287,17 @@ cd ..
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
<TabItem value="win10-x64" label="Windows">
|
||||
|
||||
- Download and extract the source tarball:
|
||||
- Download and extract the source tarball. Commands must be run in WSL `bash`:
|
||||
|
||||
```bash
|
||||
curl -LO https://duktape.org/duktape-2.7.0.tar.xz
|
||||
tar -xJf duktape-2.7.0.tar.xz
|
||||
```
|
||||
|
||||
(Run `bash`, then run the aforementioned commands, then run `exit` to exit WSL)
|
||||
|
||||
- Enter the source folder:
|
||||
|
||||
```bash
|
||||
@ -328,7 +306,7 @@ cd duktape-2.7.0
|
||||
|
||||
- Edit `src\duk_config.h` and add the highlighted lines to the end of the file:
|
||||
|
||||
```c title="src\duk_config.h (add highlighted lines to end of file)"
|
||||
```c title="src\duk_config.h (add highlighted lines)"
|
||||
#endif /* DUK_CONFIG_H_INCLUDED */
|
||||
|
||||
// highlight-start
|
||||
@ -376,7 +354,7 @@ cp duktape-*/libduktape.* .
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
<TabItem value="win10-x64" label="Windows">
|
||||
|
||||
```cmd
|
||||
copy duktape-2.7.0\duktape.dll .
|
||||
@ -400,12 +378,12 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
|
||||
|
||||
4) Download the following test scripts and files:
|
||||
|
||||
- [`pres.numbers` test file](https://docs.sheetjs.com/pres.numbers)
|
||||
- [`pres.numbers` test file](https://sheetjs.com/pres.numbers)
|
||||
- [`sheetjs.py` script](pathname:///pandas/sheetjs.py)
|
||||
- [`SheetJSPandas.py` script](pathname:///pandas/SheetJSPandas.py)
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://sheetjs.com/pres.numbers
|
||||
curl -LO https://docs.sheetjs.com/pandas/sheetjs.py
|
||||
curl -LO https://docs.sheetjs.com/pandas/SheetJSPandas.py
|
||||
```
|
||||
@ -436,11 +414,11 @@ The name of the library is `libduktape.so.207.20700`:
|
||||
|
||||
```python title="sheetjs.py (change highlighted line)"
|
||||
# highlight-next-line
|
||||
lib = "./libduktape.so.207.20700"
|
||||
lib = "libduktape.so.207.20700"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win11-x64" label="Windows">
|
||||
<TabItem value="win10-x64" label="Windows">
|
||||
|
||||
The name of the library is `duktape.dll`:
|
||||
|
||||
@ -478,17 +456,6 @@ def eval_file(ctx, path):
|
||||
python3 SheetJSPandas.py pres.numbers
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, Python may be available as `python.exe`:
|
||||
|
||||
```bash
|
||||
python.exe SheetJSPandas.py pres.numbers
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
|
||||
If successful, the script will display DataFrame metadata:
|
||||
|
||||
```
|
||||
@ -544,12 +511,10 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | JS Engine | Polars | Python | Date |
|
||||
|:-------------|:----------------|:--------|:-------|:-----------|
|
||||
| `darwin-x64` | Duktape `2.7.0` | 1.26.0 | 3.13.1 | 2025-03-31 |
|
||||
| `darwin-arm` | Duktape `2.7.0` | 1.26.0 | 3.13.2 | 2025-03-30 |
|
||||
| `win11-x64` | Duktape `2.7.0` | 1.28.1 | 3.11.9 | 2025-04-28 |
|
||||
| `win11-arm` | Duktape `2.7.0` | 1.23.0 | 3.13.2 | 2025-02-23 |
|
||||
| `linux-x64` | Duktape `2.7.0` | 1.30.0 | 3.12.3 | 2025-06-16 |
|
||||
| `linux-arm` | Duktape `2.7.0` | 1.22.0 | 3.11.2 | 2025-02-16 |
|
||||
| `darwin-x64` | Duktape `2.7.0` | 0.20.15 | 3.12.2 | 2024-03-15 |
|
||||
| `darwin-arm` | Duktape `2.7.0` | 0.20.7 | 3.11.7 | 2024-02-13 |
|
||||
| `win10-x64` | Duktape `2.7.0` | 0.20.16 | 3.12.2 | 2024-03-25 |
|
||||
| `linux-x64` | Duktape `2.7.0` | 0.20.16 | 3.11.3 | 2024-03-21 |
|
||||
|
||||
:::
|
||||
|
||||
@ -570,25 +535,11 @@ duk = CDLL(lib)
|
||||
- Within the `export_df_to_wb` function, change the `df.to_json` line:
|
||||
|
||||
```python title="sheetjs.py (edit highlighted line)"
|
||||
def export_df_to_wb(ctx, df, path, sheet_name="Sheet1", book_type=None):
|
||||
# highlight-next-line
|
||||
json = df.write_json()
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
**Polars made a breaking change in the `1.0` release.**
|
||||
|
||||
For `0.20` and earlier, the `row_oriented` option must be passed:
|
||||
|
||||
```python title="sheetjs.py (changes for Polars 0.20)"
|
||||
def export_df_to_wb(ctx, df, path, sheet_name="Sheet1", book_type=None):
|
||||
# highlight-next-line
|
||||
json = df.write_json(row_oriented=True)
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Edit `SheetJSPandas.py`.
|
||||
|
||||
- In the `process` function, change `df.info()` to `df`:
|
||||
@ -616,60 +567,23 @@ python3 -m pip install polars
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, Python may be available as `python.exe`:
|
||||
|
||||
```bash
|
||||
python.exe -m pip install polars
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
On macOS and Linux, the install command may require root access:
|
||||
|
||||
```bash
|
||||
sudo python3 -m pip install polars
|
||||
sudo python3 -m pip install pandas
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, the `C++ Clang Compiler for Windows` component must be installed
|
||||
through the Visual Studio installer.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some local tests, the install failed with the following error:
|
||||
On Arch Linux-based platforms including the Steam Deck, the install may fail:
|
||||
|
||||
```
|
||||
error: externally-managed-environment
|
||||
```
|
||||
|
||||
It is recommended to use a virtual environment.
|
||||
|
||||
---
|
||||
|
||||
`venv` must be installed through the system package manager:
|
||||
|
||||
- Debian and Ubuntu distributions:
|
||||
|
||||
```bash
|
||||
sudo apt-get install python3-venv
|
||||
```
|
||||
|
||||
- `venv` is included in the `python` package in Arch Linux-based platforms.
|
||||
|
||||
- macOS systems with a Python version from Homebrew:
|
||||
|
||||
```bash
|
||||
brew install pyenv-virtualenv
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
After installing `venv`, the following commands set up the virtual environment:
|
||||
It is recommended to use a virtual environment for Polars and copy :
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-polars
|
||||
@ -687,16 +601,6 @@ cp ../libduktape.* ../SheetJSPandas.py ../sheetjs.py ../*.js ../*.numbers .
|
||||
python3 SheetJSPandas.py pres.numbers
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, Python may be available as `python.exe`:
|
||||
|
||||
```bash
|
||||
python.exe SheetJSPandas.py pres.numbers
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
If the virtual environment was configured in the previous step, run:
|
||||
@ -727,7 +631,7 @@ shape: (5, 2)
|
||||
It will also export the DataFrame to `SheetJSPolars.xlsb`. The file can be
|
||||
inspected with a spreadsheet editor that supports XLSB files.
|
||||
|
||||
[^1]: See ["JavaScript Engines"](/docs/demos/engines/) for more examples.
|
||||
[^1]: See ["Other Languages"](/docs/demos/engines/) for more examples.
|
||||
[^2]: See [`ctypes`](https://docs.python.org/3/library/ctypes.html) in the Python documentation.
|
||||
[^3]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^4]: See ["Workbook Object"](/docs/csf/book)
|
||||
|
||||
@ -32,8 +32,7 @@ Demos for various libraries are included in separate pages:
|
||||
Modern JavaScript math and statistics libraries typically use `Float64Array` or
|
||||
`Float32Array` objects to efficiently store data variables.
|
||||
|
||||
<details>
|
||||
<summary><b>Technical details</b> (click to show)</summary>
|
||||
<details><summary><b>Technical details</b> (click to show)</summary>
|
||||
|
||||
Under the hood, `ArrayBuffer` objects represent raw binary data. "Typed arrays"
|
||||
such as `Float64Array` and `Float32Array` are objects designed for efficient
|
||||
@ -141,8 +140,7 @@ for(let R = 1; R < aoa.length; ++R) {
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live Demo</b> (click to show)</summary>
|
||||
<details><summary><b>Live Demo</b> (click to show)</summary>
|
||||
|
||||
This example fetches and parses [`iris.xlsx`](pathname:///typedarray/iris.xlsx).
|
||||
The first worksheet is processed and the new data and mapping are printed.
|
||||
@ -219,9 +217,7 @@ const petal_length = Float64Array.from(aoa.map(row => row[C]).slice(1));
|
||||
Some datasets are stored in tables where each row represents a variable and each
|
||||
column represents an observation:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>JavaScript</th><th>Spreadsheet</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
<table><thead><tr><th>JavaScript</th><th>Spreadsheet</th></tr></thead><tbody><tr><td>
|
||||
|
||||
```js
|
||||
var aoa = [
|
||||
@ -297,8 +293,7 @@ XLSX.utils.sheet_add_aoa(ws, [ arr ], { origin: "B1" });
|
||||
|
||||

|
||||
|
||||
<details open>
|
||||
<summary><b>Live Demo</b> (click to hide)</summary>
|
||||
<details open><summary><b>Live Demo</b> (click to hide)</summary>
|
||||
|
||||
In this example, two typed arrays are exported. `aoa_to_sheet` creates the
|
||||
worksheet and `sheet_add_aoa` will add the data to the sheet.
|
||||
@ -334,9 +329,7 @@ function SheetJSeriesToRows() { return (<button onClick={() => {
|
||||
A single typed array can be converted to a pure JS array with `Array.from`. For
|
||||
columns, each value should be individually wrapped in an array:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>JavaScript</th><th>Spreadsheet</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
<table><thead><tr><th>JavaScript</th><th>Spreadsheet</th></tr></thead><tbody><tr><td>
|
||||
|
||||
```js
|
||||
var data = [
|
||||
@ -382,8 +375,7 @@ XLSX.utils.sheet_add_aoa(ws, arr, { origin: "A2" });
|
||||
|
||||

|
||||
|
||||
<details open>
|
||||
<summary><b>Live Demo</b> (click to hide)</summary>
|
||||
<details open><summary><b>Live Demo</b> (click to hide)</summary>
|
||||
|
||||
In this example, two typed arrays are exported. `aoa_to_sheet` creates the
|
||||
worksheet and `sheet_add_aoa` will add the data to the sheet.
|
||||
|
||||
@ -30,13 +30,6 @@ This demo focuses on Kaioken concepts. Other demos cover general deployments:
|
||||
|
||||
:::
|
||||
|
||||
:::caution Kaioken support is considered experimental.
|
||||
|
||||
Great open source software grows with user tests and reports. Any issues should
|
||||
be reported to the Kaioken project for further diagnosis.
|
||||
|
||||
:::
|
||||
|
||||
## Installation
|
||||
|
||||
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||||
@ -61,13 +54,11 @@ loaded into the site. This sheet will have known columns.
|
||||
|
||||
#### State
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
|
||||
with "Name" and "Index" columns. The natural JS representation is an object for
|
||||
each row, using the values in the first rows as keys:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
@ -137,7 +128,7 @@ When the file header is not known in advance, `any` should be used.
|
||||
|
||||
The SheetJS [`read`](/docs/api/parse-options) and [`sheet_to_json`](/docs/api/utilities/array#array-output)
|
||||
functions simplify state updates. They are best used in the function bodies of
|
||||
`useAsync`[^2], `useEffect`[^3] and `useCallback`[^4] hooks.
|
||||
`useEffect`[^2] and `useCallback`[^3] hooks.
|
||||
|
||||
A `useEffect` hook can download and update state when a person loads the site:
|
||||
|
||||
@ -165,8 +156,8 @@ import { read, utils } from 'xlsx';
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
@ -192,8 +183,8 @@ import { read, utils } from 'xlsx';
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
@ -213,41 +204,6 @@ useEffect(() => { (async() => {
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::info pass
|
||||
|
||||
For this particular use case (fetching a file once when the page loads), it is
|
||||
strongly recommended to use the `useAsync` hook:
|
||||
|
||||
```ts
|
||||
import { useAsync } from 'kaioken';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
/* Fetch and parse the file */
|
||||
// highlight-next-line
|
||||
const { data: pres, loading, error } = useAsync<President[]>(async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
/* parse */
|
||||
const wb = read(ab);
|
||||
|
||||
/* generate array of presidents from the first worksheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
const data: President[] = utils.sheet_to_json<President>(ws); // generate objects
|
||||
|
||||
// highlight-start
|
||||
/* return data -- essentially setting state */
|
||||
return data;
|
||||
// highlight-end
|
||||
}, []);
|
||||
```
|
||||
|
||||
SheetJS users reported that it is easier to reason about data fetching using the
|
||||
`useAsync` pattern compared to the traditional `useEffect` jujutsu.
|
||||
|
||||
:::
|
||||
|
||||
#### Rendering Data
|
||||
|
||||
Kaioponents typically render HTML tables from arrays of objects. The `TR` table
|
||||
@ -278,7 +234,7 @@ in the example JSX code:
|
||||
|
||||
The [`writeFile`](/docs/api/write-options) and [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
|
||||
functions simplify exporting data. They are best used in the function bodies of
|
||||
`useCallback`[^5] hooks attached to button or other elements.
|
||||
`useCallback`[^4] hooks attached to button or other elements.
|
||||
|
||||
A callback can generate a local file when a user clicks a button:
|
||||
|
||||
@ -313,84 +269,7 @@ const exportFile = useCallback(() => {
|
||||
#### Complete Kaioponent
|
||||
|
||||
This complete Kaioponent example fetches a test file and displays the data in a
|
||||
HTML table. When the export button is clicked, a callback will export a file.
|
||||
|
||||
Examples using `useAsync` and `useEffect` with `useState` are shown below:
|
||||
|
||||
<Tabs groupId="hook">
|
||||
<TabItem name="async" value="useAsync">
|
||||
|
||||
```tsx title="src/SheetJSKaiokenAoO.tsx"
|
||||
import { useAsync, useCallback } from "kaioken";
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
interface President {
|
||||
Name: string;
|
||||
Index: number;
|
||||
}
|
||||
|
||||
export default function SheetJSKaiokenAoO() {
|
||||
/* Fetch and parse the file */
|
||||
const { data: pres, loading, error } = useAsync<President[]>(async() => {
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = read(f); // parse the array buffer
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
const data = utils.sheet_to_json<President>(ws); // generate objects
|
||||
return data;
|
||||
}, []);
|
||||
|
||||
/* get state data and export to XLSX */
|
||||
const exportFile = useCallback(() => {
|
||||
const ws = utils.json_to_sheet(pres!);
|
||||
const wb = utils.book_new();
|
||||
utils.book_append_sheet(wb, ws, "Data");
|
||||
writeFileXLSX(wb, "SheetJSKaiokenAoO.xlsx");
|
||||
}, [pres]);
|
||||
|
||||
return (<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
|
||||
{ /* generate row for each president */
|
||||
pres && pres.map(pres => (<tr>
|
||||
<td>{pres.Name}</td>
|
||||
<td>{pres.Index}</td>
|
||||
</tr>))
|
||||
}
|
||||
{ /* loading message */
|
||||
!pres && loading && ( <tr><td colSpan="2">Loading ...</td></tr> )
|
||||
}
|
||||
{ /* error message */
|
||||
!pres && !loading && ( <tr><td colSpan="2">{error.message}</td></tr> )
|
||||
}
|
||||
</tbody><tfoot><td colSpan={2}>
|
||||
<button onclick={exportFile}>Export XLSX</button>
|
||||
</td></tfoot></table>);
|
||||
}
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
Typically the JSX structure uses ternary expressions for testing status:
|
||||
|
||||
```jsx
|
||||
const { data, loading, error } = useAsync(async() => { /* ... */ });
|
||||
|
||||
return ( <>
|
||||
{ data ? (
|
||||
<b>Data is loaded</b>
|
||||
) : loading ? (
|
||||
<b>Loading ...</b>
|
||||
) : (
|
||||
<b>{error.message}</b>
|
||||
)
|
||||
}
|
||||
</> );
|
||||
```
|
||||
|
||||
For clarity, the loading and error messages are separated.
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
<TabItem name="effect" value="useEffect + useState">
|
||||
HTML table. When the export button is clicked, a callback will export a file:
|
||||
|
||||
```tsx title="src/SheetJSKaiokenAoO.tsx"
|
||||
import { useCallback, useEffect, useState } from "kaioken";
|
||||
@ -407,7 +286,7 @@ export default function SheetJSKaiokenAoO() {
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = read(f); // parse the array buffer
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
const data = utils.sheet_to_json<President>(ws); // generate objects
|
||||
@ -435,11 +314,7 @@ export default function SheetJSKaiokenAoO() {
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
<details open><summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
<Tabs groupId="starter">
|
||||
<TabItem name="vite" value="ViteJS">
|
||||
@ -448,9 +323,9 @@ export default function SheetJSKaiokenAoO() {
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Kaioken | ViteJS | Date |
|
||||
|:----------|:--------|:-----------|
|
||||
| `0.35.10` | `6.1.0` | 2025-02-11 |
|
||||
| Kaioken | ViteJS | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `0.11.2` | `5.2.6` | 2024-03-24 |
|
||||
|
||||
:::
|
||||
|
||||
@ -459,8 +334,8 @@ This demo was tested in the following environments:
|
||||
```bash
|
||||
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts
|
||||
cd sheetjs-kaioken
|
||||
npm add --save kaioken
|
||||
npm add --save vite-plugin-kaioken -D
|
||||
pnpm add --save kaioken
|
||||
pnpm add --save vite-plugin-kaioken -D
|
||||
```
|
||||
|
||||
2) Create a new file `vite.config.ts` with the following content:
|
||||
@ -470,6 +345,14 @@ import { defineConfig } from "vite"
|
||||
import kaioken from "vite-plugin-kaioken"
|
||||
|
||||
export default defineConfig({
|
||||
esbuild: {
|
||||
jsxInject: `import * as kaioken from "kaioken"`,
|
||||
jsx: "transform",
|
||||
jsxFactory: "kaioken.createElement",
|
||||
jsxFragment: "kaioken.fragment",
|
||||
loader: "tsx",
|
||||
include: ["**/*.tsx", "**/*.ts", "**/*.jsx", "**/*.js"],
|
||||
},
|
||||
plugins: [kaioken()],
|
||||
})
|
||||
```
|
||||
@ -498,8 +381,8 @@ mount(App, root!);
|
||||
6) Install the SheetJS dependency and start the dev server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
pnpm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
pnpm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
7) Open a web browser and access the displayed URL (`http://localhost:5173`)
|
||||
@ -510,7 +393,7 @@ and the page will attempt to download `SheetJSKaiokenAoO.xlsx`.
|
||||
8) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
@ -518,7 +401,7 @@ The generated site will be placed in the `dist` folder.
|
||||
9) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -527,7 +410,7 @@ and test the page.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
When the page loads, the app will fetch <https://sheetjs.com/pres.xlsx> and
|
||||
display the data from the first worksheet in a TABLE. The "Export XLSX" button
|
||||
will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
@ -537,7 +420,7 @@ will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells[^6] well!
|
||||
However, this does not handle merge cells well!
|
||||
|
||||
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function
|
||||
generates HTML that is aware of merges and other worksheet features. To add the
|
||||
@ -554,11 +437,11 @@ import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
export default function SheetJSKaiokenHTML() {
|
||||
/* the ref is used in export */
|
||||
const tbl = useRef<HTMLDivElement>(null);
|
||||
const tbl = useRef<Element>(null);
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = read(f); // parse the array buffer
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
// highlight-start
|
||||
@ -585,8 +468,7 @@ export default function SheetJSKaiokenHTML() {
|
||||
}
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
<details open><summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
<Tabs groupId="starter">
|
||||
<TabItem name="vite" value="ViteJS">
|
||||
@ -595,9 +477,9 @@ export default function SheetJSKaiokenHTML() {
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Kaioken | ViteJS | Date |
|
||||
|:----------|:--------|:-----------|
|
||||
| `0.35.10` | `6.1.0` | 2025-02-11 |
|
||||
| Kaioken | ViteJS | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `0.11.2` | `5.2.6` | 2024-03-24 |
|
||||
|
||||
:::
|
||||
|
||||
@ -606,8 +488,8 @@ This demo was tested in the following environments:
|
||||
```bash
|
||||
npm create vite@latest sheetjs-kaioken -- --template vanilla-ts
|
||||
cd sheetjs-kaioken
|
||||
npm add --save kaioken
|
||||
npm add --save vite-plugin-kaioken -D
|
||||
pnpm add --save kaioken
|
||||
pnpm add --save vite-plugin-kaioken -D
|
||||
```
|
||||
|
||||
2) Create a new file `vite.config.ts` with the following content:
|
||||
@ -617,6 +499,14 @@ import { defineConfig } from "vite"
|
||||
import kaioken from "vite-plugin-kaioken"
|
||||
|
||||
export default defineConfig({
|
||||
esbuild: {
|
||||
jsxInject: `import * as kaioken from "kaioken"`,
|
||||
jsx: "transform",
|
||||
jsxFactory: "kaioken.createElement",
|
||||
jsxFragment: "kaioken.fragment",
|
||||
loader: "tsx",
|
||||
include: ["**/*.tsx", "**/*.ts", "**/*.jsx", "**/*.js"],
|
||||
},
|
||||
plugins: [kaioken()],
|
||||
})
|
||||
```
|
||||
@ -645,8 +535,8 @@ mount(App, root!);
|
||||
6) Install the SheetJS dependency and start the dev server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
pnpm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
pnpm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
7) Open a web browser and access the displayed URL (`http://localhost:5173`)
|
||||
@ -657,7 +547,7 @@ and the page will attempt to download `SheetJSKaiokenHTML.xlsx`.
|
||||
8) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
@ -665,7 +555,7 @@ The generated site will be placed in the `dist` folder.
|
||||
9) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -674,15 +564,13 @@ and test the page.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
When the page loads, the app will fetch <https://sheetjs.com/pres.xlsx> and
|
||||
display the data from the first worksheet in a TABLE. The "Export XLSX" button
|
||||
will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
</details>
|
||||
|
||||
[^1]: See [`useState`](https://kaioken.dev/docs/hooks/useState) in the Kaioken documentation.
|
||||
[^2]: See [`useAsync`](https://kaioken.dev/docs/hooks/useAsync) in the Kaioken documentation.
|
||||
[^3]: See [`useEffect`](https://kaioken.dev/docs/hooks/useEffect) in the Kaioken documentation.
|
||||
[^4]: See [`useCallback`](https://kaioken.dev/docs/hooks/useCallback) in the Kaioken documentation.
|
||||
[^5]: See [`useCallback`](https://kaioken.dev/docs/hooks/useCallback) in the Kaioken documentation.
|
||||
[^6]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||||
[^1]: See [`useState`](https://kaioken.dev/docs/hooks/usestate) in the Kaioken documentation.
|
||||
[^2]: See [`useEffect`](https://kaioken.dev/docs/hooks/useeffect) in the Kaioken documentation.
|
||||
[^3]: See [`useCallback`](https://kaioken.dev/docs/hooks/usecallback) in the Kaioken documentation.
|
||||
[^4]: See [`useCallback`](https://kaioken.dev/docs/hooks/usecallback) in the Kaioken documentation.
|
||||
|
||||
@ -57,13 +57,11 @@ loaded into the site. This sheet will have known columns.
|
||||
|
||||
#### State
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
|
||||
with "Name" and "Index" columns. The natural JS representation is an object for
|
||||
each row, using the values in the first rows as keys:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
@ -135,7 +133,7 @@ The SheetJS [`read`](/docs/api/parse-options) and [`sheet_to_json`](/docs/api/ut
|
||||
functions simplify state updates. They are best used in the function bodies of
|
||||
`useEffect`[^2] and `useCallback`[^3] hooks.
|
||||
|
||||
A `useEffect` hook can download and update state when the site is loaded:
|
||||
A `useEffect` hook can download and update state when a person loads the site:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
@ -150,20 +148,19 @@ flowchart LR
|
||||
wb --> |wb.Sheets\nselect sheet| ws
|
||||
ws --> |sheet_to_json\n\n| aoo
|
||||
aoo --> |setPres\nfrom `setState`| state
|
||||
linkStyle 1,2,3 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
<Tabs groupId="lang">
|
||||
<TabItem name="JS" value="JavaScript">
|
||||
|
||||
```js title="In a useEffect hook, update state with data from a remote workbook"
|
||||
```js
|
||||
import { useEffect } from 'react';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
@ -183,14 +180,14 @@ useEffect(() => { (async() => {
|
||||
</TabItem>
|
||||
<TabItem name="TS" value="TypeScript" default>
|
||||
|
||||
```ts title="In a useEffect hook, update state with data from a remote workbook"
|
||||
```ts
|
||||
import { useEffect } from 'react';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
@ -253,10 +250,9 @@ flowchart LR
|
||||
state --> |json_to_sheet\n\n| ws
|
||||
ws --> |book_new\nbook_append_sheet| wb
|
||||
wb --> |writeFile\n\n| file
|
||||
linkStyle 0,1,2 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
```ts title="Export data from state to a new XLSX workbook"
|
||||
```ts
|
||||
import { useCallback } from 'react';
|
||||
import { utils, writeFile } from 'xlsx';
|
||||
|
||||
@ -276,20 +272,19 @@ const exportFile = useCallback(() => {
|
||||
#### Complete Component
|
||||
|
||||
This complete component example fetches a test file and displays the contents in
|
||||
an HTML table. When the export button is clicked, a callback will export a file:
|
||||
a HTML table. When the export button is clicked, a callback will export a file:
|
||||
|
||||
```jsx title="src/SheetJSReactAoO.js"
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
export default function SheetJSReactAoO() {
|
||||
/* the component state is an array of presidents */
|
||||
const [pres, setPres] = useState([] /* as any[] */);
|
||||
const [pres, setPres] = useState([]);
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
// highlight-start
|
||||
const wb = read(f); // parse the array buffer
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
@ -309,21 +304,20 @@ export default function SheetJSReactAoO() {
|
||||
|
||||
return (<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
|
||||
{ /* generate row for each president */
|
||||
// highlight-start
|
||||
pres.map((pres, index) => (<tr key={index}>
|
||||
// highlight-start
|
||||
pres.map(pres => (<tr>
|
||||
<td>{pres.Name}</td>
|
||||
<td>{pres.Index}</td>
|
||||
</tr>))
|
||||
// highlight-end
|
||||
// highlight-end
|
||||
}
|
||||
</tbody><tfoot><tr><td colSpan={2}>
|
||||
</tbody><tfoot><td colSpan={2}>
|
||||
<button onClick={exportFile}>Export XLSX</button>
|
||||
</td></tr></tfoot></table>);
|
||||
</td></tfoot></table>);
|
||||
}
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
<details open><summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
<Tabs groupId="starter">
|
||||
<TabItem name="vite" value="ViteJS">
|
||||
@ -334,7 +328,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | ViteJS | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `19.1.0` | `6.3.5` | 2025-05-11 |
|
||||
| `18.2.0` | `5.1.6` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -371,7 +365,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -386,14 +380,14 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | CRA | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `19.1.0` | `5.1.0` | 2025-05-11 |
|
||||
| `18.2.0` | `5.0.1` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npx -y create-react-app@5.1.0 sheetjs-react
|
||||
npx -y create-react-app@5.0.1 --scripts-version=5.0.1 sheetjs-react
|
||||
```
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
@ -401,7 +395,6 @@ npx -y create-react-app@5.1.0 sheetjs-react
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-react
|
||||
npm i
|
||||
npm i react@19.1.0 react-dom@19.1.0 web-vitals --save --save-exact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm start`}
|
||||
</CodeBlock>
|
||||
@ -424,7 +417,7 @@ The generated site will be placed in the `build` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server build
|
||||
npx http-server build
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -439,7 +432,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | NextJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `19.1.0` | `15.3.2` | 2025-05-11 |
|
||||
| `18.2.0` | `14.1.3` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -459,7 +452,7 @@ NextJS requires a number of workarounds for simple client-side JavaScript code.
|
||||
|
||||
:::
|
||||
|
||||
:::danger Telemetry
|
||||
:::warning Telemetry
|
||||
|
||||
NextJS collects telemetry by default. The `telemetry` subcommand can disable it:
|
||||
|
||||
@ -484,34 +477,26 @@ npx next telemetry disable
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npx create-next-app@latest sheetjs-nextjs --ts --no-eslint --no-tailwind --no-src-dir --no-app --import-alias "@/*" --no-turbopack
|
||||
npx create-next-app@latest sheetjs-react --ts --no-eslint --no-tailwind --no-src-dir --no-app --import-alias "@/*"
|
||||
```
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-nextjs
|
||||
cd sheetjs-react
|
||||
npm i
|
||||
npx next telemetry disable
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:3000`)
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
|
||||
|
||||
4) Replace `pages/index.tsx` with the `src/SheetJSReactAoO.js` example.
|
||||
4) Replace `src/App.jsx` with the `src/SheetJSReactAoO.js` example.
|
||||
|
||||
After replacing the code:
|
||||
After replacing the code, add the following to the top of `src/App.jsx`:
|
||||
|
||||
- uncomment the type hint in the `useState` function call:
|
||||
|
||||
```js title="pages/index.tsx (uncomment the inline comment)"
|
||||
const [pres, setPres] = useState([] as any[]);
|
||||
```
|
||||
|
||||
- add the following to the top of `pages/index.tsx`:
|
||||
|
||||
```js title="pages/index.tsx (add to top)"
|
||||
```js title="src/App.jsx (add to top)"
|
||||
"use client";
|
||||
```
|
||||
|
||||
@ -521,7 +506,7 @@ The goal is to run SheetJS code in the browser. NextJS will attempt to render
|
||||
pages on the server by default. `"use client";` instructs NextJS to treat the
|
||||
exported component as a "Client Component" that will be rendered in the browser.
|
||||
|
||||
If the directive is not added, NextJS will report errors related to hydration:
|
||||
If the pragma is not added, NextJS will report errors related to hydration:
|
||||
|
||||
```
|
||||
Error: Hydration failed because the initial UI does not match what was rendered on the server.
|
||||
@ -542,92 +527,21 @@ and the page will attempt to download `SheetJSReactAoO.xlsx`.
|
||||
npm run build
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npm run start
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:3000`) with a web browser
|
||||
and test the page.
|
||||
|
||||
</TabItem>
|
||||
<TabItem name="preact" value="Preact">
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Preact | ViteJS | Date |
|
||||
|:----------|:----------|:-----------|
|
||||
| `10.26.6` | `5.4.19` | 2025-05-11 |
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npm init preact sheetjs-preact
|
||||
```
|
||||
|
||||
This will initiate the project creation process. **Follow the on-screen prompts and
|
||||
press Enter to accept the default options:**
|
||||
|
||||
- `Project language:` JavaScript
|
||||
- `Use router?` No
|
||||
- `Prerender app (SSG)?` No
|
||||
- `Use ESLint?` No
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-preact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
|
||||
|
||||
4) Copy `src/SheetJSReactAoO.js` demo code to `src/index.jsx` and update the imports:
|
||||
|
||||
```jsx title="src/index.jsx (replace React import with Preact)"
|
||||
// Remove React import and replace with:
|
||||
import { render } from 'preact';
|
||||
import { useCallback, useEffect, useState } from 'preact/hooks';
|
||||
```
|
||||
|
||||
5) Add the following to the bottom of the file:
|
||||
|
||||
```jsx title="src/index.jsx (append to file)"
|
||||
export function App() { return ( <SheetJSReactAoO/> );}
|
||||
|
||||
render(<App />, document.getElementById('app'));
|
||||
```
|
||||
|
||||
The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSReactAoO.xlsx`.
|
||||
|
||||
6) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
7) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
and test the page.
|
||||
|
||||
</TabItem>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
When the page loads, the app will fetch <https://sheetjs.com/pres.xlsx> and
|
||||
display the data from the first worksheet in a TABLE. The "Export XLSX" button
|
||||
will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
@ -637,151 +551,12 @@ will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells[^6] well!
|
||||
|
||||
HTML Tables support elements with `rowspan` and `colspan` attributes.
|
||||
|
||||
#### State
|
||||
|
||||
The state will be the serialized HTML string:
|
||||
|
||||
<Tabs groupId="lang">
|
||||
<TabItem name="JS" value="JavaScript">
|
||||
|
||||
```ts
|
||||
import { useState } from 'react';
|
||||
|
||||
/* the component state is a string */
|
||||
const [__html, setHtml] = useState("");
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem name="TS" value="TypeScript" default>
|
||||
|
||||
```ts
|
||||
import { useState } from 'react';
|
||||
|
||||
/* the component state is a string */
|
||||
const [__html, setHtml] = useState<string>("");
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::info Use of the variable name `__html`
|
||||
|
||||
Examples use the name `__html` due to the design of `dangerouslySetInnerHTML`.
|
||||
|
||||
`dangerouslySetInnerHTML` expects objects of the form `{ __html: "html code" }`.
|
||||
|
||||
For example, the following snippet assumes `html` is the variable name:
|
||||
|
||||
```jsx
|
||||
<div ref={tbl} dangerouslySetInnerHTML={{ __html: html }} />
|
||||
```
|
||||
|
||||
By using the name `__html`, the ES6 shorthand syntax simplifies the code:
|
||||
|
||||
```jsx
|
||||
<div ref={tbl} dangerouslySetInnerHTML={{ __html }} />
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
#### Updating State
|
||||
However, this does not handle merge cells well!
|
||||
|
||||
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function
|
||||
generates HTML that is aware of merges and other worksheet features.
|
||||
|
||||
A `useEffect` hook can download and update state when the site is loaded:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
url[(Remote\nFile)]
|
||||
ab[(Data\nArrayBuffer)]
|
||||
wb(SheetJS\nWorkbook)
|
||||
ws(SheetJS\nWorksheet)
|
||||
html(HTML\nTABLE)
|
||||
state((component\nstate))
|
||||
url --> |fetch\n\n| ab
|
||||
ab --> |read\n\n| wb
|
||||
wb --> |wb.Sheets\nselect sheet| ws
|
||||
ws --> |sheet_to_html\n\n| html
|
||||
html --> |setHtml\nfrom `setState`| state
|
||||
linkStyle 1,2,3 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
```js title="In a useEffect hook, update state with HTML generated from a remote workbook"
|
||||
import { useEffect } from 'react';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
/* parse */
|
||||
const wb = read(ab);
|
||||
|
||||
/* generate HTML TABLE from first worksheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
const data = utils.sheet_to_html(ws); // generate objects
|
||||
|
||||
/* update state */
|
||||
setHtml(data); // update state
|
||||
// highlight-end
|
||||
})(); }, []);
|
||||
```
|
||||
|
||||
#### Rendering Data
|
||||
|
||||
ReactJS `dangerouslySetInnerHTML`[^7] prop allows code to set the `innerHTML`
|
||||
attribute, effectively inserting the code into the page.
|
||||
|
||||
It is strongly recommended to set the `innerHTML` of a parent `DIV` container.
|
||||
By attaching a `ref`, callbacks will be able to access the live `TABLE` element.
|
||||
|
||||
```jsx title="Example JSX for displaying HTML TABLE code"
|
||||
<div ref={tbl} dangerouslySetInnerHTML={{ __html }} />
|
||||
```
|
||||
|
||||
#### Exporting Data
|
||||
|
||||
The [`writeFile`](/docs/api/write-options) and [`table_to_book`](/docs/api/utilities/html#html-table-input)
|
||||
functions simplify exporting data. They are best used in the function bodies of
|
||||
`useCallback`[^4] hooks attached to button or other elements.
|
||||
|
||||
A callback can generate a local file when a user clicks a button:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
state((component\nstate))
|
||||
wb(SheetJS\nWorkbook)
|
||||
file[(XLSX\nexport)]
|
||||
state --> |table_to_book\n\n| wb
|
||||
wb --> |writeFile\n\n| file
|
||||
linkStyle 0,1 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
```ts title="Export data from HTML TABLE element to a new XLSX workbook"
|
||||
import { useCallback } from 'react';
|
||||
import { utils, writeFile } from 'xlsx';
|
||||
|
||||
/* get data from live HTML TABLE and export to XLSX */
|
||||
const exportFile = useCallback(() => {
|
||||
/* get live reference to HTML TABLE element */
|
||||
const elt = tbl.current.getElementsByTagName("TABLE")[0];
|
||||
/* generate workbook from element */
|
||||
// highlight-next-line
|
||||
const wb = utils.table_to_book(elt);
|
||||
/* export to XLSX */
|
||||
writeFile(wb, "SheetJSReactAoO.xlsx");
|
||||
}, [pres]);
|
||||
```
|
||||
|
||||
#### Complete Component
|
||||
generates HTML that is aware of merges and other worksheet features. ReactJS
|
||||
`dangerouslySetInnerHTML`[^5] prop allows code to set the `innerHTML` attribute,
|
||||
effectively inserting the code into the page.
|
||||
|
||||
In this example, the component attaches a `ref` to the `DIV` container. During
|
||||
export, the first `TABLE` child element can be parsed with [`table_to_book`](/docs/api/utilities/html#html-table-input) to
|
||||
@ -789,7 +564,6 @@ generate a workbook object.
|
||||
|
||||
```jsx title="src/SheetJSReactHTML.js"
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
export default function SheetJSReactHTML() {
|
||||
@ -800,7 +574,7 @@ export default function SheetJSReactHTML() {
|
||||
|
||||
/* Fetch and update the state once */
|
||||
useEffect(() => { (async() => {
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = read(f); // parse the array buffer
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
// highlight-start
|
||||
@ -826,8 +600,7 @@ export default function SheetJSReactHTML() {
|
||||
}
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
<details open><summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
<Tabs groupId="starter">
|
||||
<TabItem name="vite" value="ViteJS">
|
||||
@ -838,7 +611,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | ViteJS | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `19.1.0` | `6.3.5` | 2025-05-11 |
|
||||
| `18.2.0` | `5.1.6` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -875,7 +648,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -890,14 +663,14 @@ This demo was tested in the following environments:
|
||||
|
||||
| ReactJS | CRA | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `19.1.0` | `5.1.0` | 2025-05-11 |
|
||||
| `18.2.0` | `5.0.1` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npx -y create-react-app@5.1.0 sheetjs-react
|
||||
npx -y create-react-app@5.0.1 --scripts-version=5.0.1 sheetjs-react
|
||||
```
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
@ -905,7 +678,6 @@ npx -y create-react-app@5.1.0 sheetjs-react
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-react
|
||||
npm i
|
||||
npm i react@19.1.0 react-dom@19.1.0 web-vitals --save --save-exact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm start`}
|
||||
</CodeBlock>
|
||||
@ -928,78 +700,7 @@ The generated site will be placed in the `build` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server build
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
and test the page.
|
||||
|
||||
</TabItem>
|
||||
<TabItem name="preact" value="Preact">
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Preact | ViteJS | Date |
|
||||
|:----------|:---------|:-----------|
|
||||
| `10.26.6` | `5.4.19` | 2025-05-11 |
|
||||
|
||||
:::
|
||||
|
||||
```bash
|
||||
npm init preact sheetjs-preact
|
||||
```
|
||||
|
||||
This will initiate the project creation process. **Follow the on-screen prompts and
|
||||
press Enter to accept the default options:**
|
||||
|
||||
- `Project language:` JavaScript
|
||||
- `Use router?` No
|
||||
- `Prerender app (SSG)?` No
|
||||
- `Use ESLint?` No
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd sheetjs-preact
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
|
||||
|
||||
4) Copy `src/SheetJSReactHTML.js` demo code to `src/index.jsx` and update the imports:
|
||||
|
||||
```jsx title="src/index.jsx (replace React import with Preact)"
|
||||
// Remove React import and replace with:
|
||||
import { render } from 'preact';
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||
```
|
||||
|
||||
5) Add the following to the bottom of the file:
|
||||
|
||||
```jsx title="src/index.jsx (append to file)"
|
||||
export function App() { return ( <SheetJSReactHTML/> );}
|
||||
|
||||
render(<App />, document.getElementById('app'));
|
||||
```
|
||||
|
||||
The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSReactHTML.xlsx`.
|
||||
|
||||
6) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
7) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server build
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -1008,7 +709,7 @@ and test the page.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
When the page loads, the app will fetch <https://sheetjs.com/pres.xlsx> and
|
||||
display the data from the first worksheet in a TABLE. The "Export XLSX" button
|
||||
will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
@ -1046,8 +747,8 @@ const columns = Array.from({ length: range.e.c + 1 }, (_, i) => ({
|
||||
|
||||
## Legacy Deployments
|
||||
|
||||
[SheetJS Standalone Scripts](/docs/getting-started/installation/standalone) use
|
||||
simple `SCRIPT` tags and work with legacy deployments that do not use a bundler.
|
||||
[The Standalone Scripts](/docs/getting-started/installation/standalone) play nice
|
||||
with legacy deployments that do not use a bundler.
|
||||
|
||||
[The legacy demo](pathname:///react/index.html) shows a simple ReactJS component
|
||||
transpiled in the browser using Babel standalone library.
|
||||
@ -1056,6 +757,4 @@ transpiled in the browser using Babel standalone library.
|
||||
[^2]: See [`useEffect`](https://react.dev/reference/react/useEffect) in the ReactJS documentation.
|
||||
[^3]: See [`useCallback`](https://react.dev/reference/react/useCallback) in the ReactJS documentation.
|
||||
[^4]: See [`useCallback`](https://react.dev/reference/react/useCallback) in the ReactJS documentation.
|
||||
[^5]: See [Create React App Issue #13717](https://github.com/facebook/create-react-app/issues/13717) for details about React 19+ compatibility issues.
|
||||
[^6]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||||
[^7]: [`dangerouslySetInnerHTML`](https://react.dev/reference/react-dom/components/common#common-props) is a ReactJS prop supported for all built-in components.
|
||||
[^5]: [`dangerouslySetInnerHTML`](https://react.dev/reference/react-dom/components/common#common-props) is a ReactJS prop supported for all built-in components.
|
||||
@ -38,7 +38,7 @@ should be directed to the Angular project.
|
||||
|
||||
:::
|
||||
|
||||
:::danger Telemetry
|
||||
:::warning Telemetry
|
||||
|
||||
Angular CLI enables telemetry by default. When using a recent version, disable
|
||||
analytics globally through the CLI tool before creating a new project:
|
||||
@ -69,7 +69,7 @@ import { read, utils, writeFile } from 'xlsx';
|
||||
The various SheetJS APIs work with various data shapes. The preferred state
|
||||
depends on the application.
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
Angular 17 broke backwards compatibility with projects using Angular 2 - 16.
|
||||
|
||||
@ -87,13 +87,11 @@ loaded into the site. This sheet will have known columns.
|
||||
|
||||
#### State
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
|
||||
with "Name" and "Index" columns. The natural JS representation is an object for
|
||||
each row, using the values in the first rows as keys:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
@ -184,8 +182,8 @@ interface President { Name: string; Index: number };
|
||||
export class AppComponent {
|
||||
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
|
||||
ngOnInit(): void { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
@ -298,7 +296,7 @@ export class AppComponent {
|
||||
#### Complete Component
|
||||
|
||||
This complete component example fetches a test file and displays the contents in
|
||||
an HTML table. When the export button is clicked, a callback will export a file:
|
||||
a HTML table. When the export button is clicked, a callback will export a file:
|
||||
|
||||
<Tabs groupId="ngVer">
|
||||
<TabItem value="2" label="Angular 2-16">
|
||||
@ -321,9 +319,9 @@ interface President { Name: string; Index: number };
|
||||
<td>{{row.Index}}</td>
|
||||
</tr>
|
||||
// highlight-end
|
||||
</tbody><tfoot><tr><td colspan="2">
|
||||
</tbody><tfoot>
|
||||
<button (click)="onSave()">Export XLSX</button>
|
||||
</td></tr></tfoot>
|
||||
</tfoot>
|
||||
</table></div>
|
||||
`
|
||||
})
|
||||
@ -331,8 +329,8 @@ export class AppComponent {
|
||||
// highlight-next-line
|
||||
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
|
||||
ngOnInit(): void { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
/* parse workbook */
|
||||
@ -379,9 +377,9 @@ interface President { Name: string; Index: number };
|
||||
</tr>
|
||||
}
|
||||
// highlight-end
|
||||
</tbody><tfoot><tr><td colspan="2">
|
||||
</tbody><tfoot>
|
||||
<button (click)="onSave()">Export XLSX</button>
|
||||
</td></tr></tfoot>
|
||||
</tfoot>
|
||||
</table></div>
|
||||
`
|
||||
})
|
||||
@ -389,8 +387,8 @@ export class AppComponent {
|
||||
// highlight-next-line
|
||||
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
|
||||
ngOnInit(): void { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
/* parse workbook */
|
||||
@ -416,8 +414,7 @@ export class AppComponent {
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
<details open><summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
@ -425,11 +422,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Angular | Date |
|
||||
|:----------|:-----------|
|
||||
| `20.2.3` | 2025-09-03 |
|
||||
| `19.0.5` | 2025-01-03 |
|
||||
| `18.2.13` | 2025-01-03 |
|
||||
| `17.3.12` | 2025-01-03 |
|
||||
| `16.2.12` | 2025-01-03 |
|
||||
| `17.3.0` | 2024-03-13 |
|
||||
| `16.2.12` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -442,16 +436,16 @@ npx @angular/cli analytics disable -g
|
||||
1) Create a new project:
|
||||
|
||||
```bash
|
||||
npx @angular/cli@20 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
npx @angular/cli@17.3.0 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
The `@angular/cli` version controls the project version of Angular. For example,
|
||||
the following command uses Angular 16:
|
||||
the following command uses Angular 16.2.12:
|
||||
|
||||
```bash
|
||||
npx @angular/cli@16 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
npx @angular/cli@16.2.12 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
```
|
||||
|
||||
:::
|
||||
@ -469,20 +463,8 @@ npm start`}
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:4200`)
|
||||
|
||||
4) In the previous `src/app/app.component.ts` code snippet, select the tab for
|
||||
the appropriate version of Angular ("Angular 2-16" or "Angular 17+") and copy
|
||||
the code. Replace `src/app/app.component.ts` or `src/app/app.ts` in the project.
|
||||
|
||||
For Angular 20+, after replacing `src/app/app.ts`, edit the script and change
|
||||
the exported class name to `App`:
|
||||
|
||||
```ts title="src/app/app.ts (edit highlighted line if file exists)"
|
||||
// ...
|
||||
})
|
||||
// highlight-next-line
|
||||
export class App {
|
||||
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
|
||||
// ...
|
||||
```
|
||||
the appropriate version of Angular ("Angular 2-16" or "Angular 17+"), copy the
|
||||
code contents and replace `src/app/app.component.ts` in the project.
|
||||
|
||||
The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSAngularAoO.xlsx`. Open the file
|
||||
@ -522,12 +504,12 @@ to test the bundled site.
|
||||
|
||||
The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells[^5] well!
|
||||
However, this does not handle merge cells well!
|
||||
|
||||
The `sheet_to_html` function generates HTML that is aware of merges and other
|
||||
worksheet features. The generated HTML does not contain any `<script>` tags,
|
||||
and should therefore be safe to pass to an `innerHTML`-bound variable, but the
|
||||
`DomSanitizer` approach[^6] is strongly recommended:
|
||||
`DomSanitizer` approach[^5] is strongly recommended:
|
||||
|
||||
<Tabs groupId="ngVer">
|
||||
<TabItem value="2" label="Angular 2-16">
|
||||
@ -551,8 +533,8 @@ export class AppComponent {
|
||||
@ViewChild('tableau') tabeller!: ElementRef<HTMLDivElement>;
|
||||
// highlight-end
|
||||
ngOnInit(): void { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
/* parse workbook */
|
||||
@ -598,8 +580,8 @@ export class AppComponent {
|
||||
@ViewChild('tableau') tabeller!: ElementRef<HTMLDivElement>;
|
||||
// highlight-end
|
||||
ngOnInit(): void { (async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
/* parse workbook */
|
||||
@ -625,8 +607,7 @@ export class AppComponent {
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
<details open><summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
@ -634,10 +615,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Angular | Date |
|
||||
|:----------|:-----------|
|
||||
| `19.0.5` | 2025-01-03 |
|
||||
| `18.2.13` | 2025-01-03 |
|
||||
| `17.3.12` | 2025-01-03 |
|
||||
| `16.2.12` | 2025-01-03 |
|
||||
| `17.3.0` | 2024-03-13 |
|
||||
| `16.2.12` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
@ -650,16 +629,16 @@ npx @angular/cli analytics disable -g
|
||||
1) Create a new project:
|
||||
|
||||
```bash
|
||||
npx @angular/cli@19 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
npx @angular/cli@17.3.0 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
The `@angular/cli` version controls the project version of Angular. For example,
|
||||
the following command uses Angular 16:
|
||||
the following command uses Angular 16.2.12:
|
||||
|
||||
```bash
|
||||
npx @angular/cli@16 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
npx @angular/cli@16.2.12 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
```
|
||||
|
||||
:::
|
||||
@ -741,7 +720,7 @@ this.columns = Array.from({ length: range.e.c + 1 }, (_, i) => ({
|
||||
|
||||
## Older Versions
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
This demo is included for legacy deployments. There are incompatibilities with
|
||||
different NodeJS and other ecosystem versions. Issues should be raised with
|
||||
@ -1055,9 +1034,8 @@ will have an `ng-version` attribute.
|
||||
npm run build
|
||||
```
|
||||
|
||||
[^1]: The main website for Angular versions 2-16 is https://angular.io/ . The project moved to a new domain https://angular.dev/ during the Angular 17 launch.
|
||||
[^1]: The main website for Angular versions 2-16 is <https://angular.io/> . The project moved to a new domain <https://angular.dev/> during the Angular 17 launch.
|
||||
[^2]: See `OnInit` in the [Angular 2-16 docs](https://angular.io/api/core/OnInit) or [Angular 17 docs](https://angular.dev/guide/components/lifecycle#ngoninit)
|
||||
[^3]: See [`ngFor`](https://angular.io/api/common/NgFor) in the Angular 2-16 docs.
|
||||
[^4]: See [`@for`](https://angular.dev/api/core/@for) in the Angular 17 docs.
|
||||
[^5]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||||
[^6]: See `DomSanitizer` in the [Angular 2-16 docs](https://angular.io/api/platform-browser/DomSanitizer) or [Angular 17 docs](https://angular.dev/api/platform-browser/DomSanitizer)
|
||||
[^5]: See `DomSanitizer` in the [Angular 2-16 docs](https://angular.io/api/platform-browser/DomSanitizer) or [Angular 17 docs](https://angular.dev/api/platform-browser/DomSanitizer)
|
||||
@ -18,8 +18,8 @@ import CodeBlock from '@theme/CodeBlock';
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses VueJS and SheetJS to process and generate spreadsheets. We'll
|
||||
explore how to load SheetJS in a VueJS single-file component and compare common
|
||||
state models and data flow strategies.
|
||||
explore how to load SheetJS in a VueJS SFC (single-file component) and compare
|
||||
common state models and data flow strategies.
|
||||
|
||||
:::note pass
|
||||
|
||||
@ -37,20 +37,12 @@ This demo focuses on VueJS concepts. Other demos cover general deployments:
|
||||
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||||
installation with Yarn and other package managers.
|
||||
|
||||
In modern sites, the library can be imported directly from JS or JSX code:
|
||||
The library can be imported directly from JS or JSX code with:
|
||||
|
||||
```js
|
||||
import { read, utils, writeFile } from 'xlsx';
|
||||
```
|
||||
|
||||
In projects that use CommonJS, the library can be loaded with `require`:
|
||||
|
||||
```js
|
||||
var XLSX = require("xlsx");
|
||||
```
|
||||
|
||||
["Legacy Deployments"](#legacy-deployments) covers integration strategies for
|
||||
sites that require standalone scripts.
|
||||
|
||||
## Internal State
|
||||
|
||||
@ -64,13 +56,11 @@ loaded into the site. This sheet will have known columns.
|
||||
|
||||
#### State
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
|
||||
with "Name" and "Index" columns. The natural JS representation is an object for
|
||||
each row, using the values in the first rows as keys:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
@ -178,8 +168,8 @@ const pres = ref([]);
|
||||
|
||||
/* Fetch and update the state once */
|
||||
onMounted(async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
@ -215,8 +205,8 @@ const pres = ref<President[]>([]);
|
||||
|
||||
/* Fetch and update the state once */
|
||||
onMounted(async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
@ -242,7 +232,7 @@ onMounted(async() => {
|
||||
A component will typically map over the data with `v-for`[^3]. The following
|
||||
example generates a TABLE with a row for each President:
|
||||
|
||||
```html title="Example single-file component for displaying arrays of objects"
|
||||
```html title="Example SFC for displaying arrays of objects"
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
const rows = ref([]);
|
||||
@ -313,7 +303,7 @@ function exportFile() {
|
||||
#### Complete Component
|
||||
|
||||
This complete component example fetches a test file and displays the contents in
|
||||
an HTML table. When the export button is clicked, a callback will export a file:
|
||||
a HTML table. When the export button is clicked, a callback will export a file:
|
||||
|
||||
```html title="src/SheetJSVueAoO.vue"
|
||||
<script setup>
|
||||
@ -323,8 +313,8 @@ import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
const rows = ref([]);
|
||||
|
||||
onMounted(async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
/* parse workbook */
|
||||
@ -349,29 +339,27 @@ function exportFile() {
|
||||
<td>{{ row.Name }}</td>
|
||||
<td>{{ row.Index }}</td>
|
||||
</tr>
|
||||
</tbody><tfoot><tr><td colSpan={2}>
|
||||
</tbody><tfoot><td colSpan={2}>
|
||||
<button @click="exportFile">Export XLSX</button>
|
||||
</td></tr></tfoot></table>
|
||||
</td></tfoot></table>
|
||||
</template>
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
<details open><summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
<Tabs groupId="starter">
|
||||
<TabItem name="vite" value="ViteJS">
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| VueJS | Deployment | Date |
|
||||
|:---------|:----------------|:-----------|
|
||||
| `3.5.17` | ViteJS `7.0.0` | 2025-06-29 |
|
||||
| `3.5.17` | NuxtJS `3.17.5` | 2025-06-29 |
|
||||
| VueJS | ViteJS | Date |
|
||||
|:--------|:--------|:-----------|
|
||||
| `3.3.9` | `4.5.1` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
<Tabs groupId="starter">
|
||||
<TabItem name="vite" value="ViteJS">
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
@ -387,7 +375,7 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Access the "Local" URL (typically `http://localhost:5173`) in a web browser.
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
|
||||
|
||||
4) Replace `src/App.vue` with the `src/SheetJSVueAoO.vue` example.
|
||||
|
||||
@ -406,7 +394,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -415,10 +403,20 @@ and test the page.
|
||||
</TabItem>
|
||||
<TabItem name="nuxt" value="NuxtJS">
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| VueJS | NuxtJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `3.4.21` | `3.11.1` | 2024-03-21 |
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npx nuxi@latest init sheetjs-nuxt --packageManager npm --no-install --no-gitInit -M ,
|
||||
npx nuxi@latest init sheetjs-nuxt --packageManager npm --no-install --no-gitInit
|
||||
```
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
@ -430,7 +428,7 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Access the "Local" URL (typically `http://localhost:3000`) in a web browser.
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:3000`)
|
||||
|
||||
4) Replace `app.vue` with the `src/SheetJSVueAoO.vue` example.
|
||||
|
||||
@ -448,7 +446,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .output/public/
|
||||
npx http-server .output/public/
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -463,11 +461,11 @@ and test the page.
|
||||
|
||||
The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells[^5] well!
|
||||
However, this does not handle merge cells well!
|
||||
|
||||
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function
|
||||
generates HTML that is aware of merges and other worksheet features. VueJS
|
||||
`v-html`[^6] attribute allows code to set the `innerHTML` attribute, effectively
|
||||
`v-html`[^5] attribute allows code to set the `innerHTML` attribute, effectively
|
||||
inserting the code into the page.
|
||||
|
||||
In this example, the component attaches a `ref` to the `DIV` container. During
|
||||
@ -483,8 +481,8 @@ const html = ref("");
|
||||
const tableau = ref();
|
||||
|
||||
onMounted(async() => {
|
||||
/* Download from https://docs.sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
/* parse workbook */
|
||||
@ -507,23 +505,21 @@ function exportFile() {
|
||||
</template>
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
<details open><summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
<Tabs groupId="starter">
|
||||
<TabItem name="vite" value="ViteJS">
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| VueJS | Deployment | Date |
|
||||
|:---------|:----------------|:-----------|
|
||||
| `3.5.17` | ViteJS `7.0.0` | 2025-06-29 |
|
||||
| `3.5.17` | NuxtJS `3.17.5` | 2025-06-29 |
|
||||
| VueJS | ViteJS | Date |
|
||||
|:---------|:--------|:-----------|
|
||||
| `3.4.21` | `5.2.2` | 2024-03-21 |
|
||||
|
||||
:::
|
||||
|
||||
<Tabs groupId="starter">
|
||||
<TabItem name="vite" value="ViteJS">
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
@ -539,7 +535,7 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Access the "Local" URL (typically `http://localhost:5173`) in a web browser.
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
|
||||
|
||||
4) Replace `src/App.vue` with the `src/SheetJSVueHTML.vue` example.
|
||||
|
||||
@ -558,7 +554,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -567,10 +563,20 @@ and test the page.
|
||||
</TabItem>
|
||||
<TabItem name="nuxt" value="NuxtJS">
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| VueJS | NuxtJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `3.4.21` | `3.11.1` | 2024-03-21 |
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npx nuxi@latest init sheetjs-nuxt --packageManager npm --no-install --no-gitInit -M ,
|
||||
npx nuxi@latest init sheetjs-nuxt --packageManager npm --no-install --no-gitInit
|
||||
```
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
@ -582,7 +588,7 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm run dev`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Access the "Local" URL (typically `http://localhost:3000`) in a web browser.
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:3000`)
|
||||
|
||||
4) Replace `app.vue` with the `src/SheetJSVueHTML.vue` example.
|
||||
|
||||
@ -600,7 +606,7 @@ The generated site will be placed in the `dist` folder.
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .output/public/
|
||||
npx http-server .output/public/
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -662,5 +668,4 @@ modern design patterns.
|
||||
[^2]: See [`onMounted()`](https://vuejs.org/api/composition-api-lifecycle.html#onmounted) in the VueJS documentation.
|
||||
[^3]: See [`v-for`](https://vuejs.org/api/built-in-directives.html#v-for) in the VueJS documentation.
|
||||
[^4]: See [`v-on`](https://vuejs.org/api/built-in-directives.html#v-on) in the VueJS documentation.
|
||||
[^5]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||||
[^6]: See [`v-html`](https://vuejs.org/api/built-in-directives.html#v-html) in the VueJS documentation.
|
||||
[^5]: See [`v-html`](https://vuejs.org/api/built-in-directives.html#v-html) in the VueJS documentation.
|
||||
@ -25,7 +25,7 @@ models and data flow strategies.
|
||||
This demo focuses on Svelte concepts. Other demos cover general deployments:
|
||||
|
||||
- [Static Site Generation powered by SvelteKit](/docs/demos/static/svelte)
|
||||
- [iOS and Android applications powered by CapacitorJS](/docs/demos/mobile/capacitor)
|
||||
- [iOS applications powered by CapacitorJS](/docs/demos/mobile/capacitor)
|
||||
- [Desktop application powered by Wails](/docs/demos/desktop/wails)
|
||||
|
||||
:::
|
||||
@ -50,29 +50,11 @@ depends on the application.
|
||||
### Array of Objects
|
||||
|
||||
Typically, some users will create a spreadsheet with source data that should be
|
||||
loaded into the site. This sheet will have known columns. For example, "Name"
|
||||
and "Index" are used in [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx):
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
loaded into the site. This sheet will have known columns. For example, our
|
||||
[presidents sheet](https://sheetjs.com/pres.xlsx) has "Name" / "Index" columns:
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
```js
|
||||
[
|
||||
{ Name: "Bill Clinton", Index: 42 },
|
||||
{ Name: "GeorgeW Bush", Index: 43 },
|
||||
{ Name: "Barack Obama", Index: 44 },
|
||||
{ Name: "Donald Trump", Index: 45 },
|
||||
{ Name: "Joseph Biden", Index: 46 }
|
||||
]
|
||||
```
|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
This naturally maps to an array of typed objects, as in the TS example below:
|
||||
|
||||
```ts
|
||||
@ -83,12 +65,24 @@ interface President {
|
||||
Index: number;
|
||||
}
|
||||
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = read(f);
|
||||
const data = utils.sheet_to_json<President>(wb.Sheets[wb.SheetNames[0]]);
|
||||
console.log(data);
|
||||
```
|
||||
|
||||
`data` will be an array of objects:
|
||||
|
||||
```js
|
||||
[
|
||||
{ Name: "Bill Clinton", Index: 42 },
|
||||
{ Name: "GeorgeW Bush", Index: 43 },
|
||||
{ Name: "Barack Obama", Index: 44 },
|
||||
{ Name: "Donald Trump", Index: 45 },
|
||||
{ Name: "Joseph Biden", Index: 46 }
|
||||
]
|
||||
```
|
||||
|
||||
A component will typically map over the data. The following example generates
|
||||
a TABLE with a row for each President:
|
||||
|
||||
@ -102,7 +96,7 @@ let pres = [];
|
||||
|
||||
/* Fetch and update the state once */
|
||||
onMount(async() => {
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = read(f); // parse the array buffer
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
// highlight-start
|
||||
@ -128,22 +122,21 @@ function exportFile() {
|
||||
<td>{p.Index}</td>
|
||||
</tr>{/each}
|
||||
<!-- highlight-end -->
|
||||
</tbody><tfoot><tr><td colSpan={2}>
|
||||
</tbody><tfoot><td colSpan={2}>
|
||||
<button on:click={exportFile}>Export XLSX</button>
|
||||
</td></tr></tfoot></table>
|
||||
</td></tfoot></table>
|
||||
</main>
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
<details open><summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| SvelteJS | ViteJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `5.38.6` | `7.1.1` | 2025-09-03 |
|
||||
| Svelte | ViteJS | Date |
|
||||
|:--------|:--------|:-----------|
|
||||
| `4.2.8` | `5.0.5` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -170,9 +163,7 @@ The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSSvelteAoA.xlsx`. There may be a
|
||||
delay since Vite will try to optimize the SheetJS library on the fly.
|
||||
|
||||
5) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
6) Build the site:
|
||||
5) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -180,10 +171,10 @@ npm run build
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
7) Start a local web server:
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
@ -195,7 +186,7 @@ and test the page.
|
||||
|
||||
The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells[^1] well!
|
||||
However, this does not handle merge cells well!
|
||||
|
||||
The `sheet_to_html` function generates HTML that is aware of merges and other
|
||||
worksheet features. Svelte `@html` tag allows raw HTML strings:
|
||||
@ -210,7 +201,7 @@ let tbl;
|
||||
|
||||
/* Fetch and update the state once */
|
||||
onMount(async() => {
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = read(f); // parse the array buffer
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
// highlight-start
|
||||
@ -236,16 +227,15 @@ function exportFile() {
|
||||
</main>
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
<details open><summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| SvelteJS | ViteJS | Date |
|
||||
|:---------|:---------|:-----------|
|
||||
| `5.25.3` | `6.2.3` | 2025-03-30 |
|
||||
| Svelte | ViteJS | Date |
|
||||
|:--------|:--------|:-----------|
|
||||
| `4.2.8` | `5.0.5` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -272,9 +262,7 @@ The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSSvelteHTML.xlsx`. There may be a
|
||||
delay since Vite will try to optimize the SheetJS library on the fly.
|
||||
|
||||
5) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
6) Build the site:
|
||||
5) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -282,15 +270,13 @@ npm run build
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
7) Start a local web server:
|
||||
6) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
and test the page.
|
||||
|
||||
</details>
|
||||
|
||||
[^1]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||||
|
||||
@ -10,7 +10,7 @@ sidebar_position: 7
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
This demo is for the legacy AngularJS framework (version 1).
|
||||
|
||||
@ -34,11 +34,10 @@ models and data flow strategies.
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Browser | Version | Date |
|
||||
|:-------------|:------------------|:-----------|
|
||||
| Chromium 133 | `1.8.2` (latest) | 2025-03-30 |
|
||||
| Konqueror 22 | `1.8.2` (latest) | 2025-04-23 |
|
||||
| Chromium 133 | `1.2.32` (legacy) | 2025-03-30 |
|
||||
| Version | Date |
|
||||
|:------------------|:-----------|
|
||||
| `1.8.2` (latest) | 2023-12-04 |
|
||||
| `1.2.32` (legacy) | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -74,22 +73,22 @@ property to `"arraybuffer"` ensures the result is an `ArrayBuffer` object.
|
||||
The SheetJS [`read`](/docs/api/parse-options) method can parse the `ArrayBuffer`
|
||||
and return a SheetJS workbook object[^2].
|
||||
|
||||
This controller fetches [a sample file](https://docs.sheetjs.com/pres.xlsx),
|
||||
The following controller fetches [a sample file](https://sheetjs.com/pres.xlsx),
|
||||
parses the result into a workbook, extracts the first worksheet, and uses the
|
||||
SheetJS [`sheet_to_html`](/docs/api/utilities/html#html-table-output) method to
|
||||
generate an HTML table:
|
||||
generate a HTML table:
|
||||
|
||||
```js title="Sample Controller"
|
||||
```js
|
||||
/* The controller function must accept a `$http` argument */
|
||||
app.controller('sheetjs', function($scope, $http) {
|
||||
/* fetch https://docs.sheetjs.com/pres.xlsx */
|
||||
/* fetch https://sheetjs.com/pres.xlsx */
|
||||
$http({
|
||||
method:'GET', url:'https://docs.sheetjs.com/pres.xlsx',
|
||||
method:'GET', url:'https://sheetjs.com/pres.xlsx',
|
||||
/* ensure the result is an ArrayBuffer object */
|
||||
responseType:'arraybuffer'
|
||||
}).then(function(response) {
|
||||
}).then(function(data) {
|
||||
// highlight-next-line
|
||||
var wb = XLSX.read(response.data);
|
||||
var wb = XLSX.read(data.data);
|
||||
/* generate HTML from first worksheet*/
|
||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||
var html = XLSX.utils.sheet_to_html(ws);
|
||||
@ -168,13 +167,11 @@ depends on the application.
|
||||
|
||||
### Array of Objects
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
|
||||
with "Name" and "Index" columns. The natural JS representation is an object for
|
||||
each row, using the values in the first rows as keys:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
@ -199,10 +196,10 @@ file, generates row objects, and stores the array in the state:
|
||||
```js
|
||||
app.controller('sheetjs', function($scope, $http) {
|
||||
$http({
|
||||
url:'https://docs.sheetjs.com/pres.xlsx',
|
||||
url:'https://sheetjs.com/pres.xlsx',
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(response) {
|
||||
var wb = XLSX.read(response.data);
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data);
|
||||
// highlight-next-line
|
||||
$scope.data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
}, function(err) { console.log(err); });
|
||||
@ -238,8 +235,7 @@ $scope.exportSheetJS = function() {
|
||||
};
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary open><b>How to run the example</b> (click to hide)</summary>
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
1) Save the following to `index.html`:
|
||||
|
||||
@ -276,10 +272,10 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
XLSX.writeFile(wb, "SheetJSAngularJSAoO.xlsx");
|
||||
};
|
||||
$http({
|
||||
url:'https://docs.sheetjs.com/pres.xlsx',
|
||||
url:'https://sheetjs.com/pres.xlsx',
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(response) {
|
||||
var wb = XLSX.read(response.data);
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data);
|
||||
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
$scope.data = data;
|
||||
}, function(err) { console.log(err); });
|
||||
@ -289,17 +285,8 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
</html>`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
```
|
||||
|
||||
Access the displayed URL with a web browser (typically `http://localhost:8080`)
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
store an array of objects in state. When the "Export Table" button is clicked,
|
||||
a worksheet is created and exported to XLSX.
|
||||
2) Start a local web server with `npx http-server .` and access the displayed
|
||||
URL with a web browser (typically `http://localhost:8080`)
|
||||
|
||||
</details>
|
||||
|
||||
@ -307,12 +294,12 @@ a worksheet is created and exported to XLSX.
|
||||
|
||||
The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells[^4] well!
|
||||
However, this does not handle merge cells well!
|
||||
|
||||
The `sheet_to_html` function generates HTML that is aware of merges and other
|
||||
worksheet features. The generated HTML does not contain any `<script>` tags,
|
||||
and should therefore be safe to pass to an `ng-bind-html` binding. This approach
|
||||
requires the `ngSanitize` plugin[^5].
|
||||
requires the `ngSanitize` plugin[^4].
|
||||
|
||||
```html
|
||||
<div ng-controller="sheetjs">
|
||||
@ -325,10 +312,10 @@ requires the `ngSanitize` plugin[^5].
|
||||
var app = angular.module('s5s', ['ngSanitize']);
|
||||
app.controller('sheetjs', function($scope, $http) {
|
||||
$http({
|
||||
url:'https://docs.sheetjs.com/pres.xlsx',
|
||||
url:'https://sheetjs.com/pres.xlsx',
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(response) {
|
||||
var wb = XLSX.read(response.data);
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data);
|
||||
// highlight-next-line
|
||||
$scope.data = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
}, function(err) { console.log(err); });
|
||||
@ -349,8 +336,7 @@ The HTML table can be directly exported with [`table_to_book`](/docs/api/utiliti
|
||||
};
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary open><b>How to run the example</b> (click to hide)</summary>
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
1) Save the following to `index.html`:
|
||||
|
||||
@ -381,10 +367,10 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
XLSX.writeFile(wb, "SheetJSAngularJSHTML.xlsx");
|
||||
};
|
||||
$http({
|
||||
url:'https://docs.sheetjs.com/pres.xlsx',
|
||||
url:'https://sheetjs.com/pres.xlsx',
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(response) {
|
||||
var wb = XLSX.read(response.data);
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data);
|
||||
$scope.data = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
}, function(err) { console.log(err); });
|
||||
});
|
||||
@ -393,22 +379,12 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
</html>`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
```
|
||||
|
||||
Access the displayed URL with a web browser (typically `http://localhost:8080`)
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
store the HTML string in state. When the "Export Table" button is clicked, a
|
||||
worksheet is created and exported to XLSX.
|
||||
2) Start a local web server with `npx http-server .` and access the displayed
|
||||
URL with a web browser (typically `http://localhost:8080`)
|
||||
|
||||
</details>
|
||||
|
||||
[^1]: See [`$http`](https://docs.angularjs.org/api/ng/service/$http) in the AngularJS documentation.
|
||||
[^2]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^3]: See ["Creating Directives"](https://docs.angularjs.org/guide/directive#creating-a-directive-that-manipulates-the-dom) in the AngularJS documentation.
|
||||
[^4]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||||
[^5]: See [`ngSanitize`](https://docs.angularjs.org/api/ngSanitize) in the AngularJS documentation.
|
||||
[^4]: See [`ngSanitize`](https://docs.angularjs.org/api/ngSanitize) in the AngularJS documentation.
|
||||
@ -57,13 +57,13 @@ require(
|
||||
</script>`}
|
||||
</CodeBlock>
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
The official Google CDN does not have the newest releases of Dojo Toolkit
|
||||
|
||||
**This is a known Google CDN bug.**
|
||||
|
||||
The script https://docs.sheetjs.com/dojo/dojo.js was fetched from the official
|
||||
The script <https://docs.sheetjs.com/dojo/dojo.js> was fetched from the official
|
||||
`1.17.3` uncompressed release artifact[^1].
|
||||
|
||||
:::
|
||||
@ -72,12 +72,7 @@ The script https://docs.sheetjs.com/dojo/dojo.js was fetched from the official
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Platform | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 133 | 2025-03-30 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
The demos were last tested on 2023-12-04.
|
||||
|
||||
Demos exclusively using Dojo Core were tested using Dojo Toolkit `1.17.3`.
|
||||
|
||||
@ -98,13 +93,13 @@ was the latest version available on the Google CDN.
|
||||
When fetching spreadsheets with XHR, `handleAs: "arraybuffer"` yields an
|
||||
`ArrayBuffer` which can be passed to the SheetJS `read` method.
|
||||
|
||||
The following example generates an HTML table from the first worksheet:
|
||||
The following example generates a HTML table from the first worksheet:
|
||||
|
||||
```html
|
||||
<div id="tbl"></div>
|
||||
<script>
|
||||
require(["dojo/request/xhr", "xlsx"], function(xhr, _XLSX) {
|
||||
xhr("https://docs.sheetjs.com/pres.numbers", {
|
||||
xhr("https://sheetjs.com/pres.numbers", {
|
||||
headers: { "X-Requested-With": null },
|
||||
// highlight-next-line
|
||||
handleAs: "arraybuffer"
|
||||
@ -160,7 +155,7 @@ require([
|
||||
], function(ready, xhr, Memory, registry, _XLSX) {
|
||||
ready(function() {
|
||||
/* fetch test file */
|
||||
xhr("https://docs.sheetjs.com/pres.xlsx", {
|
||||
xhr("https://sheetjs.com/pres.xlsx", {
|
||||
headers: { "X-Requested-With": null },
|
||||
handleAs: "arraybuffer"
|
||||
}).then(function(ab) {
|
||||
@ -206,5 +201,5 @@ function export_all_data_from_store(store) {
|
||||
}
|
||||
```
|
||||
|
||||
[^1]: All Dojo Toolkit releases are available at https://download.dojotoolkit.org/. The mirrored `dojo.js` corresponds to the `1.17.3` uncompressed script http://download.dojotoolkit.org/release-1.17.3/dojo.js.uncompressed.js.
|
||||
[^1]: All Dojo Toolkit releases are available at <https://download.dojotoolkit.org/>. The mirrored `dojo.js` corresponds to the `1.17.3` uncompressed script <http://download.dojotoolkit.org/release-1.17.3/dojo.js.uncompressed.js>.
|
||||
[^2]: See [`dojo/store`](https://dojotoolkit.org/reference-guide/dojo/store.html) in the Dojo Toolkit documentation.
|
||||
@ -1,506 +0,0 @@
|
||||
---
|
||||
title: Sheets in Blazor Sites
|
||||
sidebar_label: Blazor
|
||||
pagination_prev: demos/index
|
||||
pagination_next: demos/grid/index
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[Blazor](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor) is a
|
||||
framework for building user interfaces using C#, HTML, JS and CSS.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses Blazor and SheetJS to process and generate spreadsheets. We'll
|
||||
explore how to load SheetJS in Razor components and compare common state models
|
||||
and data flow strategies.
|
||||
|
||||
:::caution Blazor support is considered experimental.
|
||||
|
||||
Great open source software grows with user tests and reports. Any issues should
|
||||
be reported to the Blazor project for further diagnosis.
|
||||
|
||||
:::
|
||||
|
||||
:::danger Telemetry
|
||||
|
||||
**The `dotnet` command embeds telemetry.**
|
||||
|
||||
The `DOTNET_CLI_TELEMETRY_OPTOUT` environment variable should be set to `1`.
|
||||
|
||||
["Platform Configuration"](#platform-configuration) includes instructions for
|
||||
setting the environment variable on supported platforms.
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
### Installation
|
||||
|
||||
The SheetJS library can be loaded when the page is loaded or imported whenever
|
||||
the library functionality is used.
|
||||
|
||||
#### Standalone Script
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be loaded in the root HTML page (typically `wwwroot/index.html`):
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<!-- use version ${current} -->
|
||||
<script type="text/javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
#### ECMAScript Module
|
||||
|
||||
The SheetJS ECMAScript module script can be dynamically imported from functions.
|
||||
This ensures the library is only loaded when necessary. The following JS example
|
||||
loads the library and returns a Promise that resolves to the version string:
|
||||
|
||||
<CodeBlock language="js">{`\
|
||||
async function sheetjs_version(id) {
|
||||
/* dynamically import the script in the event listener */
|
||||
// highlight-next-line
|
||||
const XLSX = await import("https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs");
|
||||
\n\
|
||||
/* use the library */
|
||||
return XLSX.version;
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
### Calling JS from C#
|
||||
|
||||
Callbacks for events in Razor elements invoke C# methods. The C# methods can use
|
||||
Blazor APIs to invoke JS methods that are visible in the browser global scope.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor U as User
|
||||
participant P as Browser
|
||||
participant A as Blazor
|
||||
U-->>P: click button
|
||||
P-->>A: click event
|
||||
Note over A: C#35; callback<br/><br/>InvokeVoidAsync
|
||||
A->>P: call JS function
|
||||
Note over P: global method<br/><br/>SheetJS logic
|
||||
P->>U: download workbook
|
||||
```
|
||||
|
||||
#### Setup
|
||||
|
||||
The primary mechanism for invoking JS functions from Blazor is `IJSRuntime`[^1].
|
||||
It should be injected at the top of relevant Razor component scripts:
|
||||
|
||||
```csharp title="Injecting IJSRuntime"
|
||||
@inject IJSRuntime JS
|
||||
```
|
||||
|
||||
#### Fire and Forget
|
||||
|
||||
When exporting a file with the SheetJS `writeFile` method[^2], browser APIs do
|
||||
not provide success or error feedback. As a result, this demo invokes functions
|
||||
using the `InvokeVoidAsync` static method[^3].
|
||||
|
||||
The following C# method will invoke the `export_method` method in the browser:
|
||||
|
||||
```csharp title="Invoking JS functions from C#"
|
||||
private async Task ExportDataset() {
|
||||
await JS.InvokeVoidAsync("export_method", data);
|
||||
}
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
**The JS methods must be defined in the global scope!**
|
||||
|
||||
In this demo, the script is added to the `HEAD` block of the root HTML file:
|
||||
|
||||
```html title="wwwroot/index.html"
|
||||
<head>
|
||||
<!-- ... meta / title / base / link tags -->
|
||||
<link href="SheetJSBlazorWasm.styles.css" rel="stylesheet" />
|
||||
|
||||
<!-- highlight-start -->
|
||||
<!-- script with `export_method` is in the HEAD block -->
|
||||
<script>
|
||||
/* In a normal script tag, Blazor JS can call this method */
|
||||
async function export_method(...rows) {
|
||||
/* display the array of objects */
|
||||
console.log(rows);
|
||||
}
|
||||
</script>
|
||||
<!-- highlight-end -->
|
||||
</head>
|
||||
```
|
||||
|
||||
When using `<script type="module">`, top-level function definitions are not
|
||||
visible to Blazor by default. They must be attached to `globalThis`:
|
||||
|
||||
```html title="Attaching methods to globalThis"
|
||||
<script type="module">
|
||||
/* Using `type="module"`, Blazor JS cannot see this function definition */
|
||||
async function export_method(...rows) {
|
||||
/* display the array of objects */
|
||||
console.log(rows);
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
/* Once attached to `globalThis`, Blazor JS can call this method */
|
||||
globalThis.export_method = export_method;
|
||||
// highlight-end
|
||||
</script>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
#### Blazor Callbacks
|
||||
|
||||
Methods are commonly bound to buttons in the Razor template using `@onclick`.
|
||||
When the following button is clicked, Blazor will invoke `ExportDataset`:
|
||||
|
||||
```html title="Binding callback to a HTML button"
|
||||
<button @onclick="ExportDataset">Export Dataset</button>
|
||||
```
|
||||
|
||||
### State in Blazor
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns.
|
||||
|
||||

|
||||
|
||||
#### C# Representation
|
||||
|
||||
The natural C# representation of a single row is a class object:
|
||||
|
||||
```csharp title="President class"
|
||||
public class President {
|
||||
public string Name { get; set; }
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
var PrezClinton = new President() { Name = "Bill Clinton", Index = 42 };
|
||||
```
|
||||
|
||||
The entire dataset is typically stored in an array of class objects:
|
||||
|
||||
```csharp title="President dataset"
|
||||
private President[] data;
|
||||
```
|
||||
|
||||
#### Data Interchange
|
||||
|
||||
`InvokeVoidAsync` can pass data from the C# state to a JS function:
|
||||
|
||||
```csharp
|
||||
await JS.InvokeVoidAsync("export_method", data);
|
||||
```
|
||||
|
||||
Each row in the dataset will be passed as a separate argument to the JavaScript
|
||||
method, so the JavaScript code should collect the arguments:
|
||||
|
||||
```js title="Collecting rows in a JS callback"
|
||||
/* NOTE: blazor spreads the C# array, so the ... spread syntax is required */
|
||||
async function export_method(...rows) {
|
||||
/* display the array of objects */
|
||||
console.log(rows);
|
||||
}
|
||||
```
|
||||
|
||||
Each row is a simple JavaScript object.
|
||||
|
||||
:::caution pass
|
||||
|
||||
Blazor automatically spreads arrays. Each row is passed as a separate argument
|
||||
to the JavaScript method.
|
||||
|
||||
The example method uses the JavaScript spread syntax to collect the arguments.
|
||||
|
||||
:::
|
||||
|
||||
#### Exporting Data
|
||||
|
||||
With the collected array of objects, the SheetJS `json_to_sheet` method[^4] will
|
||||
generate a SheetJS worksheet[^5] from the dataset. After creating a workbook[^6]
|
||||
object with the `book_new` method[^7], the file is written with `writeFile`[^2]:
|
||||
|
||||
<CodeBlock title="JS Callback for exporting datasets" language="javascript">{`\
|
||||
/* NOTE: blazor spreads the C# array, so the spread is required */
|
||||
async function export_method(...rows) {
|
||||
const XLSX = await import("https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs");
|
||||
const ws = XLSX.utils.json_to_sheet(rows);
|
||||
const wb = XLSX.utils.book_new(ws, "Data");
|
||||
XLSX.writeFile(wb, "SheetJSBlazor.xlsx");
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
|
||||
### HTML Tables
|
||||
|
||||
When displaying datasets, Razor components typically generate HTML tables:
|
||||
|
||||
```html title="Razor template from official starter"
|
||||
<table class="table" id="weather-table">
|
||||
<thead>
|
||||
<tr><th>Date</th><th>Temp. (C)</th><th>Temp. (F)</th><th>Summary</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var forecast in forecasts)
|
||||
{
|
||||
<tr>
|
||||
<td>@forecast.Date.ToShortDateString()</td>
|
||||
<td>@forecast.TemperatureC</td>
|
||||
<td>@forecast.TemperatureF</td>
|
||||
<td>@forecast.Summary</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
```
|
||||
|
||||
If it has an `id`, JS code on the frontend can find the table element using the
|
||||
`document.getElementById` DOM method. A SheetJS workbook object can be generated
|
||||
using the `table_to_book` method[^8] and exported with `writeFile`[^2]:
|
||||
|
||||
<CodeBlock title="JS Callback for exporting HTML TABLE elements" language="javascript">{`\
|
||||
async function export_method() {
|
||||
const XLSX = await import("https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs");
|
||||
const wb = XLSX.utils.table_to_book(document.getElementById("weather-table"));
|
||||
XLSX.writeFile(wb, "SheetJSBlazor.xlsx");
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
This approach uses data that already exists in the document, so no additional
|
||||
data is passed from C# to JavaScript.
|
||||
|
||||
## Complete Demo
|
||||
|
||||
The Blazor + WASM starter app includes a "Weather" component that displays data
|
||||
from a C#-managed dataset. This demo uses SheetJS to export data in two ways:
|
||||
|
||||
- "Export Dataset" will send row objects from the underlying C# data store to
|
||||
the frontend. The SheetJS `json_to_sheet` method[^4] builds the worksheet.
|
||||
|
||||
- "Export HTML Table" will scrape the table using the SheetJS `table_to_book`
|
||||
method[^8]. No extra data will be sent to the frontend.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | Date |
|
||||
|:-------------|:-----------|
|
||||
| `darwin-x64` | 2025-04-17 |
|
||||
| `darwin-arm` | 2025-04-24 |
|
||||
| `win11-x64` | 2025-04-17 |
|
||||
| `win11-arm` | 2025-04-24 |
|
||||
|
||||
:::
|
||||
|
||||
### Platform Configuration
|
||||
|
||||
0) Set the `DOTNET_CLI_TELEMETRY_OPTOUT` environment variable to `1`.
|
||||
|
||||
<details open>
|
||||
<summary><b>How to disable telemetry</b> (click to hide)</summary>
|
||||
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="unix" label="Linux/MacOS">
|
||||
|
||||
Add the following line to `.profile`, `.bashrc` and `.zshrc`:
|
||||
|
||||
```bash title="(add to .profile , .bashrc , and .zshrc)"
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
```
|
||||
|
||||
Close and restart the Terminal to load the changes.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
Type `env` in the search bar and select "Edit the system environment variables".
|
||||
|
||||
In the new window, click the "Environment Variables..." button.
|
||||
|
||||
In the new window, look for the "System variables" section and click "New..."
|
||||
|
||||
Set the "Variable name" to `DOTNET_CLI_TELEMETRY_OPTOUT` and the value to `1`.
|
||||
|
||||
Click "OK" in each window (3 windows) and restart your computer.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
</details>
|
||||
|
||||
1) Install .NET
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
For macOS x64 and ARM64, install the `dotnet-sdk` Cask with Homebrew:
|
||||
|
||||
```bash
|
||||
brew install --cask dotnet-sdk
|
||||
```
|
||||
|
||||
For Steam Deck Holo and other Arch Linux x64 distributions, the `dotnet-sdk` and
|
||||
`dotnet-runtime` packages should be installed using `pacman`:
|
||||
|
||||
```bash
|
||||
sudo pacman -Syu dotnet-sdk dotnet-runtime
|
||||
```
|
||||
|
||||
https://dotnet.microsoft.com/en-us/download/dotnet/6.0 is the official source
|
||||
for Windows and ARM64 Linux versions.
|
||||
|
||||
</details>
|
||||
|
||||
2) Open a new Terminal window in macOS or PowerShell window in Windows.
|
||||
|
||||
### App Creation
|
||||
|
||||
3) Create a new `blazorwasm` app:
|
||||
|
||||
```bash
|
||||
dotnet new blazorwasm -o SheetJSBlazorWasm
|
||||
cd SheetJSBlazorWasm
|
||||
dotnet run
|
||||
```
|
||||
|
||||
When the Blazor service runs, the terminal will display a URL:
|
||||
|
||||
```text
|
||||
info: Microsoft.Hosting.Lifetime[14]
|
||||
// highlight-next-line
|
||||
Now listening on: http://localhost:6969
|
||||
```
|
||||
|
||||
4) In a new browser window, open the displayed URL from Step 3.
|
||||
|
||||
5) Click the "Weather" link and confirm the page includes a data table.
|
||||
|
||||
6) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
### SheetJS Integration
|
||||
|
||||
7) Add the following script tag to `wwwroot/index.html` in the `HEAD` block:
|
||||
|
||||
<CodeBlock title="wwwroot/index.html (add within the HEAD block)" language="html">{`\
|
||||
<script>
|
||||
/* NOTE: blazor spreads the C# array, so the spread is required */
|
||||
async function export_dataset(...rows) {
|
||||
const XLSX = await import("https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs");
|
||||
const ws = XLSX.utils.json_to_sheet(rows);
|
||||
const wb = XLSX.utils.book_new(ws, "Data");
|
||||
XLSX.writeFile(wb, "SheetJSBlazorDataset.xlsx");
|
||||
}
|
||||
\n\
|
||||
async function export_html(id) {
|
||||
const XLSX = await import("https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs");
|
||||
const wb = XLSX.utils.table_to_book(document.getElementById(id));
|
||||
XLSX.writeFile(wb, "SheetJSBlazorHTML.xlsx");
|
||||
}
|
||||
</script>`}
|
||||
</CodeBlock>
|
||||
|
||||
8) Inject the `IJSRuntime` dependency near the top of `Pages/Weather.razor`:
|
||||
|
||||
```csharp title="Pages/Weather.razor (add highlighted lines)"
|
||||
@page "/weather"
|
||||
@inject HttpClient Http
|
||||
// highlight-next-line
|
||||
@inject IJSRuntime JS
|
||||
```
|
||||
|
||||
9) Add an ID to the `TABLE` element in `Pages/Weather.razor`:
|
||||
|
||||
```html title="Pages/Weather.razor (add id to TABLE element)"
|
||||
{
|
||||
<!-- highlight-next-line -->
|
||||
<table class="table" id="weather-table">
|
||||
<thead>
|
||||
<tr>
|
||||
```
|
||||
|
||||
10) Add callbacks to the `@code` section in `Pages/Weather.razor`:
|
||||
|
||||
```csharp title="Pages/Weather.razor (add within the @code section)"
|
||||
private async Task ExportDataset()
|
||||
{
|
||||
await JS.InvokeVoidAsync("export_dataset", forecasts);
|
||||
}
|
||||
|
||||
private async Task ExportHTML()
|
||||
{
|
||||
await JS.InvokeVoidAsync("export_html", "weather-table");
|
||||
}
|
||||
```
|
||||
|
||||
11) Add Export buttons to the template in `Pages/Weather.razor`:
|
||||
|
||||
```csharp title="Pages/Weather.razor (add highlighted lines)"
|
||||
<p>This component demonstrates fetching data from the server.</p>
|
||||
|
||||
<!-- highlight-start -->
|
||||
<button @onclick="ExportDataset">Export Dataset</button>
|
||||
<button @onclick="ExportHTML">Export HTML TABLE</button>
|
||||
<!-- highlight-end -->
|
||||
```
|
||||
|
||||
|
||||
### Testing
|
||||
|
||||
12) Launch the `dotnet` process again:
|
||||
|
||||
```bash
|
||||
dotnet run
|
||||
```
|
||||
|
||||
When the Blazor service runs, the terminal will display a URL:
|
||||
|
||||
```text
|
||||
info: Microsoft.Hosting.Lifetime[14]
|
||||
Now listening on: http://localhost:6969
|
||||
```
|
||||
|
||||
13) In a new browser window, open the displayed URL from Step 12.
|
||||
|
||||
14) Click the "Weather" link. The page should match the following screenshot:
|
||||
|
||||

|
||||
|
||||
15) Click the "Export Dataset" button and save the generated file to
|
||||
`SheetJSBlazorDataset.xlsx`. Open the file in a spreadsheet editor and confirm
|
||||
the data matches the table. The column labels will differ since the underlying
|
||||
dataset uses different labels.
|
||||
|
||||

|
||||
|
||||
16) Click the "Export HTML TABLE" button and save the generated file to
|
||||
`SheetJSBlazorHTML.xlsx`. Open the file in a spreadsheet editor and confirm the
|
||||
data matches the table. The column labels will match the HTML table.
|
||||
|
||||

|
||||
|
||||
:::note pass
|
||||
|
||||
It is somewhat curious that the official `dotnet` Blazor sample dataset marks
|
||||
`1 C` and `-13 C` as "freezing" but marks `-2 C` as "chilly". It stands to
|
||||
reason that `-2 C` should also be freezing.
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See ["Microsoft.JSInterop.IJSRuntime"](https://learn.microsoft.com/en-us/dotnet/api/microsoft.jsinterop.ijsruntime) in the `dotnet` documentation.
|
||||
[^2]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
|
||||
[^3]: See ["Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync"](https://learn.microsoft.com/en-us/dotnet/api/microsoft.jsinterop.jsruntimeextensions.invokevoidasync) in the `dotnet` documentation.
|
||||
[^4]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
|
||||
[^5]: See ["Sheet Objects"](/docs/csf/sheet)
|
||||
[^6]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^7]: See [`book_new` in "Utilities"](/docs/api/utilities/wb)
|
||||
[^8]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
|
||||
@ -2,7 +2,7 @@
|
||||
title: Legacy Frameworks
|
||||
pagination_prev: demos/index
|
||||
pagination_next: demos/grid/index
|
||||
sidebar_position: 18
|
||||
sidebar_position: 9
|
||||
sidebar_custom_props:
|
||||
skip: 1
|
||||
---
|
||||
@ -25,9 +25,9 @@ the shim script must be loaded first:
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<!-- SheetJS version ${current} \`shim.min.js\` -->
|
||||
<script type="text/javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js"></script>
|
||||
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js"></script>
|
||||
<!-- SheetJS version ${current} \`xlsx.full.min.js\` -->
|
||||
<script type="text/javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>
|
||||
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>
|
||||
<script>
|
||||
/* display SheetJS version */
|
||||
if(typeof console == "object" && console.log) console.log(XLSX.version);
|
||||
@ -38,7 +38,7 @@ else document.write(XLSX.version);
|
||||
|
||||
## Internet Explorer
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
Internet Explorer is unmaintained and users should consider modern browsers.
|
||||
The SheetJS testing grid still includes IE and should work.
|
||||
@ -48,8 +48,7 @@ The SheetJS testing grid still includes IE and should work.
|
||||
The modern upload and download strategies are not available in older versions of
|
||||
IE, but there are approaches using ActiveX or Flash.
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Example</b> (click to show)</summary>
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
This demo includes all of the support files for the Flash and ActiveX methods.
|
||||
|
||||
@ -77,8 +76,7 @@ npx -y http-server .
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Other Live Demos</b> (click to show)</summary>
|
||||
<details><summary><b>Other Live Demos</b> (click to show)</summary>
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -87,10 +85,10 @@ demo pages should be downloaded and hosted using a simple HTTP server.
|
||||
|
||||
:::
|
||||
|
||||
https://oss.sheetjs.com/sheetjs/ajax.html uses XMLHttpRequest to download test
|
||||
<https://oss.sheetjs.com/sheetjs/ajax.html> uses XMLHttpRequest to download test
|
||||
files and convert to CSV
|
||||
|
||||
https://oss.sheetjs.com/sheetjs/ demonstrates reading files with `FileReader`.
|
||||
<https://oss.sheetjs.com/sheetjs/> demonstrates reading files with `FileReader`.
|
||||
|
||||
Older versions of IE do not support HTML5 File API but do support Base64.
|
||||
|
||||
@ -200,24 +198,18 @@ included in the page and the relevant features are enabled on the target system.
|
||||
|
||||
### KnockoutJS
|
||||
|
||||
[KnockoutJS](https://knockoutjs.com/) was a popular MVVM framework.
|
||||
KnockoutJS was a popular MVVM framework.
|
||||
|
||||
The [Live demo](pathname:///knockout/knockout3.html) shows a view model that is
|
||||
The [Live demo](pathname:///knockout/knockout.html) shows a view model that is
|
||||
updated with file data and exported to spreadsheets.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| KnockoutJS | Date | Live Demo |
|
||||
|:-----------|:-----------|:-----------------------------------------------|
|
||||
| `3.5.0` | 2025-01-08 | [**KO3**](pathname:///knockout/knockout3.html) |
|
||||
| `2.3.0` | 2025-01-08 | [**KO2**](pathname:///knockout/knockout2.html) |
|
||||
This demo was last run on 2023 December 04 using KnockoutJS `3.4.2`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Full Exposition</b> (click to show)</summary>
|
||||
<details><summary><b>Full Exposition</b> (click to show)</summary>
|
||||
|
||||
**State**
|
||||
|
||||
@ -1,635 +0,0 @@
|
||||
---
|
||||
title: Sheets in UI5 Sites
|
||||
sidebar_label: OpenUI5 / SAPUI5
|
||||
description: Build enterprise-grade applications with OpenUI5. Seamlessly integrate spreadsheets into your app using SheetJS. Bring Excel-powered workflows and data to the modern web.
|
||||
pagination_prev: demos/index
|
||||
pagination_next: demos/grid/index
|
||||
sidebar_position: 10
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[OpenUI5](https://openui5.org/) is a JavaScript framework for building
|
||||
enterprise-ready web applications. It is compatible with the SAPUI5 framework.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo shows how to handle spreadsheet data in OpenUI5 apps using SheetJS.
|
||||
You'll learn how to load spreadsheet files, process their data, and generate
|
||||
new spreadsheet exports.
|
||||
|
||||
:::info pass
|
||||
|
||||
[Docs Issue #20](https://git.sheetjs.com/sheetjs/docs.sheetjs.com/issues/20)
|
||||
includes a complete example starting from the OpenUI5 "Worklist App Tutorial".
|
||||
|
||||
:::
|
||||
|
||||
## Installation
|
||||
|
||||
SheetJS libraries conform to the UI5 ECMAScript requirements[^1]. SheetJS
|
||||
libraries can be loaded in a UI5 site at different points in the app lifecycle.
|
||||
|
||||
#### UI5 Module {#installation-define}
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
comply with AMD `define` semantics. They support `sap.ui.define` out of the box.
|
||||
|
||||
If the SheetJS Standalone script is saved to `webapp/xlsx.full.min.js`, the base
|
||||
script `webapp/index.js` can load the `./xlsx.full.min` dependency:
|
||||
|
||||
```js title="webapp/index.js (loading the SheetJS dependency)"
|
||||
sap.ui.define([
|
||||
// highlight-next-line
|
||||
"./xlsx.full.min", // relative path to script, without the file extension
|
||||
/* ... other libraries ... */
|
||||
], function (
|
||||
// highlight-next-line
|
||||
_XLSX // !! NOTE: this is not XLSX! A different variable name must be used
|
||||
/* ... variables for the other libraries ... */,
|
||||
) {
|
||||
// highlight-next-line
|
||||
alert(XLSX.version); // use XLSX in the callback
|
||||
});
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
In some deployments, the function argument was `undefined`.
|
||||
|
||||
The standalone scripts add `window.XLSX`, so it is recommended to use `_XLSX`
|
||||
in the function arguments and access the library with `XLSX` in the callback.
|
||||
|
||||
:::
|
||||
|
||||
#### HTML {#installation-html}
|
||||
|
||||
UI5 is typically loaded in a `SCRIPT` tag in `webapp/index.html`. Similarly,
|
||||
[SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be loaded with a `SCRIPT` tag in the same HTML page:
|
||||
|
||||
<CodeBlock language="html" value="html" title="index.html (add in the HEAD block before UI5 scripts)">{`\
|
||||
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>
|
||||
`}
|
||||
</CodeBlock>
|
||||
|
||||
This will expose the `XLSX` global object, which includes the functions listed
|
||||
in the ["API Reference"](/docs/api/) section of the documentation.
|
||||
|
||||
:::caution pass
|
||||
|
||||
**The SheetJS Standalone script must be loaded before the UI5 bootstrap script**:
|
||||
|
||||
<CodeBlock language="html" value="html" title="webapp/index.html (loading the SheetJS standalone script)">{`\
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>UI5 Walkthrough</title>
|
||||
|
||||
<!-- The SheetJS Standalone script must be loaded before the UI5 bootstrap -->
|
||||
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>
|
||||
|
||||
<!-- UI5 bootstrap script -->
|
||||
<script
|
||||
id="sap-ui-bootstrap"
|
||||
src="resources/sap-ui-core.js"
|
||||
...(other attributes)...
|
||||
></script>
|
||||
</head>
|
||||
`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
## Internal State
|
||||
|
||||
The various SheetJS APIs work with various data shapes. The preferred state
|
||||
depends on the application.
|
||||
|
||||
### JSON Model
|
||||
|
||||
The UI5 `JSONModel`[^2] is a client-side model implementation for JavaScript
|
||||
object data. Think of it like a container that holds your spreadsheet data.
|
||||
|
||||
`JSONModel` provides powerful two-way data binding capabilities. UI5 will
|
||||
automatically updates your webpage whenever the data changes. It will also
|
||||
respond to changes when users interact with components in the webpage.
|
||||
|
||||
|
||||
#### State {#json-state}
|
||||
|
||||
The example [presidents sheet](https://docs.sheetjs.com/pres.xlsx) has one
|
||||
header row with "Name" and "Index" columns. The natural JS representation is an
|
||||
object for each row, where the keys are specified in the first row:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
```js
|
||||
[
|
||||
{ Name: "Bill Clinton", Index: 42 },
|
||||
{ Name: "GeorgeW Bush", Index: 43 },
|
||||
{ Name: "Barack Obama", Index: 44 },
|
||||
{ Name: "Donald Trump", Index: 45 },
|
||||
{ Name: "Joseph Biden", Index: 46 }
|
||||
]
|
||||
```
|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
Here is a basic example of initializing a model. A more complete implementation
|
||||
will be shown later.
|
||||
|
||||
```js title="UI5 JSONModel for rows of data"
|
||||
sap.ui.define(["sap/ui/model/json/JSONModel"], function (JSONModel) {
|
||||
// highlight-next-line
|
||||
const oModel = new JSONModel({ presidents: [] });
|
||||
});
|
||||
```
|
||||
|
||||
#### Updating State {#json-update}
|
||||
|
||||
Starting from a spreadsheet file, the SheetJS [`read`](/docs/api/parse-options)
|
||||
method parses the data into a SheetJS workbook object[^3]. After selecting a
|
||||
worksheet, the [`sheet_to_json`](/docs/api/utilities/array#array-output) method
|
||||
generates row objects that can be assigned to the model.
|
||||
|
||||
Here is a sample flow diagram and method for downloading a workbook, generating
|
||||
rows from the first worksheet, and updating a UI5 `JSONModel`:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
url[(Remote\nFile)]
|
||||
ab[(Data\nArrayBuffer)]
|
||||
wb(SheetJS\nWorkbook)
|
||||
ws(SheetJS\nWorksheet)
|
||||
aoo(array of\nobjects)
|
||||
model((JSON\nModel))
|
||||
url --> |fetch\n\n| ab
|
||||
ab --> |read\n\n| wb
|
||||
wb --> |wb.Sheets\nselect sheet| ws
|
||||
ws --> |sheet_to_json\n\n| aoo
|
||||
aoo --> |setProperty\nfrom model| model
|
||||
linkStyle 1,2,3 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
```js title="Download workbook, extract data from first worksheet, and update JSONModel"
|
||||
_loadExcelFile: async function () {
|
||||
/* Download from https://docs.sheetjs.com/pres.xlsx */
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
/* parse */
|
||||
const wb = XLSX.read(f); // parse the array buffer
|
||||
/* generate array of objects from first worksheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
const data = XLSX.utils.sheet_to_json(ws); // generate objects
|
||||
|
||||
/* update JSONModel */
|
||||
this.getView().getModel().setProperty("/presidents", data);
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
#### Rendering Data {#json-render}
|
||||
|
||||
In UI5, the "Model-View-Controller"[^4] pattern is used to organize code and
|
||||
separate concerns. The view defines the UI structure, the controller handles the
|
||||
logic, and the model manages the data.
|
||||
|
||||
The following example uses the `Table` component[^5] to display data.
|
||||
|
||||
```xml title="Example View XML for displaying an array of objects"
|
||||
<mvc:View>
|
||||
<Page>
|
||||
<!-- The Table component binds to the presidents array -->
|
||||
<!-- highlight-next-line -->
|
||||
<Table width="300px" items="{/presidents}">
|
||||
<!-- Column definitions specify the table structure -->
|
||||
<columns>
|
||||
<Column><header><Text text="Name" /></header></Column>
|
||||
<Column><header><Text text="Value" /></header></Column>
|
||||
</columns>
|
||||
<!-- ColumnListItem template defines how each row should be rendered -->
|
||||
<!-- highlight-start -->
|
||||
<items>
|
||||
<ColumnListItem>
|
||||
<cells>
|
||||
<Text text="{Name}" />
|
||||
<Text text="{Index}" />
|
||||
</cells>
|
||||
</ColumnListItem>
|
||||
</items>
|
||||
<!-- highlight-end -->
|
||||
</Table>
|
||||
</Page>
|
||||
</mvc:View>
|
||||
```
|
||||
|
||||
#### Exporting Data {#json-export}
|
||||
|
||||
The `getProperty` method[^6] of the `JSONModel` pulls data from the UI5 model.
|
||||
If an array of objects was pushed with `setProperty`, the `getProperty` method
|
||||
will return an array of objects.
|
||||
|
||||
The SheetJS [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
|
||||
function will create a SheetJS worksheet object[^7] from the data in the array.
|
||||
The [`book_new`](/docs/api/utilities/wb) method will create a SheetJS workbook
|
||||
object that includes the new worksheet. [`writeFile`](/docs/api/write-options)
|
||||
will attempt to generate a file and initiate a download.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
state((State\nJSONModel))
|
||||
aoo[(Array of\nObjects)]
|
||||
ws(SheetJS\nWorksheet)
|
||||
wb(((SheetJS\nWorkbook)))
|
||||
file[(XLSX\nexport)]
|
||||
state --> |getProperty\n\n| aoo
|
||||
aoo --> |json_to_sheet\n\n| ws
|
||||
ws --> |book_new\n\n| wb
|
||||
wb --> |writeFile\n\n| file
|
||||
linkStyle 1,2,3 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
Here is a sample method for exporting data from the UI5 `JSONModel` to XLSX:
|
||||
|
||||
```js title="Fetch data from JSONModel and export to XLSX"
|
||||
/* get model data and export to XLSX */
|
||||
onExport: function () {
|
||||
const data = this.getView().getModel().getProperty("/presidents");
|
||||
/* generate worksheet from model data */
|
||||
// highlight-next-line
|
||||
const ws = XLSX.utils.json_to_sheet(data);
|
||||
/* create workbook and append worksheet */
|
||||
const wb = XLSX.utils.book_new(ws, "Data");
|
||||
/* export to XLSX */
|
||||
XLSX.writeFileXLSX(wb, "SheetJSOpenUI5AoO.xlsx");
|
||||
}
|
||||
```
|
||||
|
||||
This method can be bound to the `press` event of a `sap.m.Button` control:
|
||||
|
||||
```xml title="Example View XML for exporting an array of objects to a workbook"
|
||||
<mvc:View>
|
||||
<Page>
|
||||
<!-- The `onExport` method is bound to the `press` event -->
|
||||
<Button text="Export Data" press=".onExport" />
|
||||
</Page>
|
||||
</mvc:View>
|
||||
```
|
||||
|
||||
#### Complete Component
|
||||
|
||||
This complete component example fetches a test file and displays the contents in a table.
|
||||
When the export button is clicked, an event handler will export a file:
|
||||
|
||||
##### View Implementation {#view-implementation}
|
||||
|
||||
```xml title="webapp/view/Main.view.xml"
|
||||
<mvc:View controllerName="sheetjs.openui5.controller.Main" displayBlock="true" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:core="sap.ui.core" core:require="{formatter: 'sheetjs/openui5/model/formatter'}">
|
||||
<Page>
|
||||
<VBox width="auto" alignItems="Start">
|
||||
<Table width="300px" items="{/presidents}">
|
||||
<columns>
|
||||
<Column><header><Text text="Name" /></header></Column>
|
||||
<Column><header><Text text="Index" /></header></Column>
|
||||
</columns>
|
||||
<items><ColumnListItem><cells>
|
||||
<Text text="{Name}" />
|
||||
<Text text="{Index}" />
|
||||
</cells></ColumnListItem></items>
|
||||
</Table>
|
||||
<Button text="Export XLSX" press=".onExport" />
|
||||
</VBox>
|
||||
</Page>
|
||||
</mvc:View>
|
||||
```
|
||||
|
||||
##### Controller Implementation {#controller-implementation}
|
||||
|
||||
```js title="webapp/controller/Main.controller.js"
|
||||
sap.ui.define(
|
||||
["./BaseController", "sap/ui/model/json/JSONModel"],
|
||||
function (BaseController, JSONModel) {
|
||||
"use strict";
|
||||
|
||||
return BaseController.extend("com.demo.xlsx.controller.Main", {
|
||||
onInit: function () {
|
||||
/* initialize model */
|
||||
const oModel = new JSONModel({ presidents: [] });
|
||||
this.getView().setModel(oModel);
|
||||
/* load data */
|
||||
this._loadExcelFile();
|
||||
},
|
||||
|
||||
_loadExcelFile: async function () {
|
||||
/* fetch and parse file */
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = XLSX.read(f);
|
||||
/* extract data from first worksheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||
const data = XLSX.utils.sheet_to_json(ws);
|
||||
/* update state model */
|
||||
this.getView().getModel().setProperty("/presidents", data);
|
||||
},
|
||||
|
||||
onExport: function () {
|
||||
/* fetch data from model */
|
||||
const data = this.getView().getModel().getProperty("/presidents");
|
||||
/* generate workbook */
|
||||
const ws = XLSX.utils.json_to_sheet(data);
|
||||
const wb = XLSX.utils.book_new(ws, "Data");
|
||||
/* export to XLSX */
|
||||
XLSX.writeFileXLSX(wb, "SheetJSOpenUI5AoO.xlsx");
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OpenUI5 | Date |
|
||||
|:----------|------------|
|
||||
| `1.132.1` | 2025-01-24 |
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npm i --global generator-easy-ui5
|
||||
npx yo easy-ui5 app
|
||||
```
|
||||
|
||||
When prompted, enter the following options:
|
||||
|
||||
- `Enter your application id (namespace)?`: Type `sheetjs.openui5` and press <kbd>Enter</kbd>
|
||||
- `Which framework do you want to use?`: Press <kbd>Enter</kbd> (`OpenUI5` should be the default)
|
||||
- `Which framework version do you want to use?`: Type `1.132.1` and press <kbd>Enter</kbd>
|
||||
- `Who is the author of the application?`: Press <kbd>Enter</kbd> (use the default author)
|
||||
- `Would you like to create a new directory for the application?`: Type `Y` and press <kbd>Enter</kbd>
|
||||
- `Would you like to initialize a local git repository for the application?`: Type `N` and press <kbd>Enter</kbd>
|
||||
|
||||

|
||||
|
||||
2) Install the dependencies and start server:
|
||||
|
||||
```bash
|
||||
cd sheetjs.openui5
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:8080`).
|
||||
|
||||
In the file listing, click `index.html` to launch the app.
|
||||
|
||||
4) Add the SheetJS Standalone script to `webapp/index.html` after the `title` tag:
|
||||
|
||||
<CodeBlock language="html" value="html" title="webapp/index.html (add highlighted lines)">{`\
|
||||
<title>UI5 Application: sheetjs.openui5</title>
|
||||
<!-- highlight-next-line -->
|
||||
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
5) Replace `webapp/view/Main.view.xml` with the `Main.view.xml` snippet in the
|
||||
["View Implementation" section](#view-implementation).
|
||||
|
||||
6) Replace `webapp/controller/Main.controller.js` with the `Main.controller.js`
|
||||
example in the ["Controller Implementation" section](#controller-implementation).
|
||||
|
||||
7) Switch back to the browser window.
|
||||
|
||||
The page will refresh and show a table with an Export button.
|
||||
|
||||
Click the button and the page will attempt to download `SheetJSOpenUI5AoO.xlsx`.
|
||||
This file can be inspected with a spreadsheet editor.
|
||||
|
||||
8) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build:opt
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
:::caution pass
|
||||
|
||||
SAP recommends `npm run build`. This does not generate a proper standalone site!
|
||||
Sites built with `npm run build` must be served with `npm run start:dist`.
|
||||
|
||||
This demo uses the `build:opt` target to ensure that a proper static site is
|
||||
generated. The `dist` folder in this demo can be deployed on a static host.
|
||||
|
||||
:::
|
||||
|
||||
9) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
and test the page.
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
display the data from the first worksheet in a TABLE. The "Export XLSX" button
|
||||
will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
</details>
|
||||
|
||||
### HTML
|
||||
|
||||
The main disadvantage of the Array of Objects approach is the specific nature
|
||||
of the columns. For more general use, passing around an Array of Arrays works.
|
||||
However, this does not handle merge cells[^8] well!
|
||||
|
||||
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function
|
||||
generates HTML that is aware of merges and other worksheet features.
|
||||
|
||||
To render the HTML string from the model, the property from the model should be
|
||||
bound to the `content` property of a UI5 `core:HTML`[^9] control.
|
||||
|
||||
On export, the [`table_to_book`](/docs/api/utilities/html#html-table-input)
|
||||
method creates a SheetJS workbook object from the rendered HTML table.
|
||||
|
||||
##### View Implementation {#view-implementation-html}
|
||||
|
||||
```xml title="webapp/view/Main.view.xml"
|
||||
<mvc:View controllerName="sheetjs.openui5.controller.Main" displayBlock="true" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:core="sap.ui.core" xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
<Page>
|
||||
<content>
|
||||
<core:HTML id="tbl" content="{/tableHTML}" />
|
||||
<Button text="Export XLSX" press=".onExport"/>
|
||||
</content>
|
||||
</Page>
|
||||
</mvc:View>
|
||||
```
|
||||
|
||||
##### Controller Implementation {#controller-implementation-html}
|
||||
|
||||
```js title="webapp/controller/Main.controller.js"
|
||||
sap.ui.define([
|
||||
"sap/ui/core/mvc/Controller",
|
||||
"sap/ui/model/json/JSONModel"
|
||||
], function (Controller, JSONModel) {
|
||||
"use strict";
|
||||
|
||||
return Controller.extend("sheetjs.openui5.controller.Main", {
|
||||
onInit: function () {
|
||||
/* the component state is an HTML string */
|
||||
const oModel = new JSONModel({ tableHTML: "", });
|
||||
this.getView().setModel(oModel);
|
||||
/* load data */
|
||||
this._loadExcelFile();
|
||||
},
|
||||
|
||||
_loadExcelFile: async function () {
|
||||
/* fetch and parse file */
|
||||
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = XLSX.read(f);
|
||||
/* generate HTML table from first worksheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||
const opts = { header: `<table>`, footer: `</table>` };
|
||||
const tableHTML = XLSX.utils.sheet_to_html(ws, opts);
|
||||
/* update state model */
|
||||
this.getView().getModel().setProperty("/tableHTML", tableHTML);
|
||||
},
|
||||
|
||||
onExport: function () {
|
||||
/* Get reference to the `TABLE` element in the model */
|
||||
const table = this.getView().byId("tbl").getDomRef();
|
||||
|
||||
/* Generate workbook */
|
||||
const wb = XLSX.utils.table_to_book(table);
|
||||
|
||||
/* Export to XLSX */
|
||||
XLSX.writeFileXLSX(wb, "SheetJSOpenUI5HTML.xlsx");
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
<details open>
|
||||
<summary><b>How to run the example</b> (click to hide)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OpenUI5 | Date |
|
||||
|:----------|------------|
|
||||
| `1.132.1` | 2025-01-24 |
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
npm i --global generator-easy-ui5
|
||||
npx yo easy-ui5 app
|
||||
```
|
||||
|
||||
When prompted, enter the following options:
|
||||
|
||||
- `Enter your application id (namespace)?`: Type `sheetjs.openui5` and press <kbd>Enter</kbd>
|
||||
- `Which framework do you want to use?`: Press <kbd>Enter</kbd> (`OpenUI5` should be the default)
|
||||
- `Which framework version do you want to use?`: Type `1.132.1` and press <kbd>Enter</kbd>
|
||||
- `Who is the author of the application?`: Press <kbd>Enter</kbd> (use the default author)
|
||||
- `Would you like to create a new directory for the application?`: Type `Y` and press <kbd>Enter</kbd>
|
||||
- `Would you like to initialize a local git repository for the application?`: Type `N` and press <kbd>Enter</kbd>
|
||||
|
||||

|
||||
|
||||
2) Install the dependencies and start server:
|
||||
|
||||
```bash
|
||||
cd sheetjs.openui5
|
||||
npm install
|
||||
npm start
|
||||
````
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:8080`)
|
||||
|
||||
In the file listing, click `index.html` to launch the app.
|
||||
|
||||
4) Add the SheetJS Standalone script to `webapp/index.html` after the `title` tag:
|
||||
|
||||
<CodeBlock language="html" value="html" title="webapp/index.html (add highlighted lines)">{`\
|
||||
<title>UI5 Application: sheetjs.openui5</title>
|
||||
<!-- highlight-next-line -->
|
||||
<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
5) Replace `webapp/view/Main.view.xml` with the `Main.view.xml` snippet in the
|
||||
["View Implementation" section](#view-implementation-html).
|
||||
|
||||
6) Replace `webapp/controller/Main.controller.js` with the `Main.controller.js`
|
||||
example in ["Controller Implementation"](#controller-implementation-html).
|
||||
|
||||
7) Switch back to the browser window.
|
||||
|
||||
The page will refresh and show a table with an Export button.
|
||||
|
||||
Click the button and the page will attempt to download `SheetJSOpenUI5HTML.xlsx`.
|
||||
This file can be inspected with a spreadsheet editor.
|
||||
|
||||
8) Build the site:
|
||||
|
||||
```bash
|
||||
npm run build:opt
|
||||
```
|
||||
|
||||
The generated site will be placed in the `dist` folder.
|
||||
|
||||
:::caution pass
|
||||
|
||||
SAP recommends `npm run build`. This does not generate a proper standalone site!
|
||||
Sites built with `npm run build` must be served with `npm run start:dist`.
|
||||
|
||||
This demo uses the `build:opt` target to ensure that a proper static site is
|
||||
generated. The `dist` folder in this demo can be deployed on a static host.
|
||||
|
||||
:::
|
||||
|
||||
9) Start a local web server:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser
|
||||
and test the page.
|
||||
|
||||
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||||
display the data from the first worksheet in a TABLE. The "Export XLSX" button
|
||||
will generate a workbook that can be opened in a spreadsheet editor.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
[^1]: See ["ECMAScript Support"](https://sdk.openui5.org/topic/0cb44d7a147640a0890cefa5fd7c7f8e.html#loio0cb44d7a147640a0890cefa5fd7c7f8e/section_UI5Mod) for more details about OpenUI5 compatibility.
|
||||
[^2]: See [`JSONModel`](https://sdk.openui5.org/1.38.62/docs/api/symbols/sap.ui.model.json.JSONModel.html) in the OpenUI5 documentation.
|
||||
[^3]: See ["SheetJS Data Model"](/docs/csf/)
|
||||
[^4]: See [OpenUI5's MVC Documentation](https://sdk.openui5.org/topic/91f233476f4d1014b6dd926db0e91070) for detailed explanation of the pattern implementation.
|
||||
[^5]: See ["List, List Item, and Table"](https://sdk.openui5.org/topic/295e44b2d0144318bcb7bdd56bfa5189) in the OpenUI5 documentation.
|
||||
[^6]: See [`getProperty` of class `sap.ui.model.json.JSONModel`](https://sdk.openui5.org/api/sap.ui.model.json.JSONModel#methods/getProperty) in the OpenUI5 documentation.
|
||||
[^7]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/sheet) for more details.
|
||||
[^8]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||||
[^9]: See [`core:HTML`](https://sdk.openui5.org/1.38.62/docs/api/symbols/sap.ui.core.HTML.html) in the OpenUI5 documentation.
|
||||
@ -41,10 +41,9 @@ This demo was tested in the following environments:
|
||||
|
||||
| ViteJS | Date |
|
||||
|:---------|:-----------|
|
||||
| `6.2.3` | 2025-03-30 |
|
||||
| `5.4.15` | 2025-03-30 |
|
||||
| `4.5.10` | 2025-03-30 |
|
||||
| `3.2.11` | 2025-03-30 |
|
||||
| `5.0.5` | 2023-12-04 |
|
||||
| `4.5.0` | 2023-12-04 |
|
||||
| `3.2.7` | 2023-12-05 |
|
||||
|
||||
:::
|
||||
|
||||
@ -99,7 +98,7 @@ interface President {
|
||||
|
||||
async function xport() {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data: President[] = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -144,24 +143,6 @@ writeFileXLSX(workbook, "Presidents.xlsx");
|
||||
npm run dev
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this was last tested in ViteJS version 4, the process crashed with the error
|
||||
|
||||
```
|
||||
Search string not found: "for (const existingRoot of buildInfoVersionMap.roots) {"
|
||||
```
|
||||
|
||||
**This is a known issue with ViteJS 4 and its dependency tree!**[^1]
|
||||
|
||||
The recommended workaround is to forcefully upgrade `vue-tsc`:
|
||||
|
||||
```bash
|
||||
npm i "vue-tsc@2"
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
5) Open a web browser to `http://localhost:5173/` and click the export button.
|
||||
|
||||
6) Build the production site:
|
||||
@ -173,10 +154,8 @@ npx vite build
|
||||
7) Verify the new site by running a local web server in the `dist` folder:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
8) Access the displayed URL (typically `http://localhost:8080`) in a web browser
|
||||
and click the export button.
|
||||
|
||||
[^1]: See [issue 4484 in the `vuejs/language-tools` repository on GitHub](https://github.com/vuejs/language-tools/issues/4484) for the issue triage and workaround. This issue does not affect other ViteJS major versions.
|
||||
@ -47,23 +47,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| ESBuild | Date |
|
||||
|:----------|:-----------|
|
||||
| `0.25.5` | 2025-06-02 |
|
||||
| `0.24.2` | 2025-06-02 |
|
||||
| `0.23.1` | 2025-06-02 |
|
||||
| `0.22.0` | 2025-06-02 |
|
||||
| `0.21.5` | 2025-06-02 |
|
||||
| `0.20.2` | 2025-06-02 |
|
||||
| `0.19.12` | 2025-06-02 |
|
||||
| `0.18.20` | 2025-06-02 |
|
||||
| `0.17.19` | 2025-06-02 |
|
||||
| `0.16.17` | 2025-06-02 |
|
||||
| `0.15.18` | 2025-06-02 |
|
||||
| `0.14.54` | 2025-06-02 |
|
||||
| `0.13.15` | 2025-06-02 |
|
||||
| `0.12.29` | 2025-06-02 |
|
||||
| `0.11.23` | 2025-06-02 |
|
||||
| `0.10.2` | 2025-06-02 |
|
||||
| `0.9.7` | 2025-06-02 |
|
||||
| `0.14.14` | 2023-12-04 |
|
||||
| `0.19.8` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -91,7 +76,7 @@ Assuming the primary source file is `in.js`, the following command will bundle
|
||||
the script and generate `out.js`:
|
||||
|
||||
```bash
|
||||
npx -y esbuild@0.25.5 in.js --bundle --outfile=out.js
|
||||
npx -y esbuild@0.19.8 in.js --bundle --outfile=out.js
|
||||
```
|
||||
|
||||
### Browser Demo
|
||||
@ -140,13 +125,13 @@ curl -LO https://docs.sheetjs.com/esbuild/esbrowser.js
|
||||
4) Create bundle:
|
||||
|
||||
```bash
|
||||
npx -y esbuild@0.25.5 esbrowser.js --bundle --outfile=esb.browser.js
|
||||
npx -y esbuild@0.19.8 esbrowser.js --bundle --outfile=esb.browser.js
|
||||
```
|
||||
|
||||
5) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) with a web browser.
|
||||
@ -180,7 +165,7 @@ Assuming the primary source file is `in.js`, the following command will bundle
|
||||
the script for NodeJS and generate `out.js`:
|
||||
|
||||
```bash
|
||||
npx -y esbuild@0.25.5 in.js --bundle --platform=node --outfile=out.js
|
||||
npx -y esbuild@0.19.8 in.js --bundle --platform=node --outfile=out.js
|
||||
```
|
||||
|
||||
### NodeJS Demo
|
||||
@ -229,7 +214,7 @@ curl -LO https://docs.sheetjs.com/esbuild/esbnode.js
|
||||
3) Create bundle:
|
||||
|
||||
```bash
|
||||
npx -y esbuild@0.25.5 esbnode.js --bundle --platform=node --outfile=esb.node.js
|
||||
npx -y esbuild@0.19.8 esbnode.js --bundle --platform=node --outfile=esb.node.js
|
||||
```
|
||||
|
||||
4) Run the bundle:
|
||||
|
||||
@ -41,10 +41,10 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date | Required Workarounds |
|
||||
|:---------|:-----------|:------------------------------------|
|
||||
| `5.97.1` | 2025-01-03 | |
|
||||
| `4.47.0` | 2025-01-03 | |
|
||||
| `3.12.0` | 2025-01-03 | Import `xlsx/dist/xlsx.full.min.js` |
|
||||
| `2.7.0` | 2025-01-03 | Import `xlsx/dist/xlsx.full.min.js` |
|
||||
| `2.7.0` | 2024-03-16 | Import `xlsx/dist/xlsx.full.min.js` |
|
||||
| `3.12.0` | 2024-03-16 | Import `xlsx/dist/xlsx.full.min.js` |
|
||||
| `4.47.0` | 2024-03-16 | Downgrade NodeJS (tested v16.20.2) |
|
||||
| `5.90.3` | 2024-03-16 | |
|
||||
|
||||
:::
|
||||
|
||||
@ -167,7 +167,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
|
||||
|
||||
document.getElementById("xport").addEventListener("click", function() {
|
||||
/* fetch JSON data and parse */
|
||||
var url = "https://docs.sheetjs.com/executive.json";
|
||||
var url = "https://sheetjs.com/data/executive.json";
|
||||
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -177,7 +177,7 @@ document.getElementById("xport").addEventListener("click", function() {
|
||||
prez.forEach(function(row) {
|
||||
row.start = row.terms.find(function(term) {
|
||||
return term.type === "prez";
|
||||
}).start;
|
||||
}).start
|
||||
});
|
||||
prez.sort(function(l,r) { return l.start.localeCompare(r.start); });
|
||||
|
||||
@ -260,7 +260,7 @@ npx webpack@3.x -p
|
||||
</TabItem>
|
||||
<TabItem value="4+" label="4.x, 5.x and beyond" default>
|
||||
|
||||
:::danger Pinning specific versions of webpack
|
||||
:::warning Pinning specific versions of webpack
|
||||
|
||||
The webpack tooling is not designed for switching between versions. A specific
|
||||
version above 4.0 can be pinned by locally installing webpack and the CLI tool.
|
||||
@ -269,16 +269,15 @@ version above 4.0 can be pinned by locally installing webpack and the CLI tool.
|
||||
|
||||
**Webpack 4.x**
|
||||
|
||||
:::note pass
|
||||
:::info pass
|
||||
|
||||
Some Webpack 4 versions are incompatible with Node 18+. They will elicit the
|
||||
following error:
|
||||
Webpack 4 is incompatible with Node 18+. It will elicit the following error:
|
||||
|
||||
```
|
||||
Error: error:0308010C:digital envelope routines::unsupported
|
||||
```
|
||||
|
||||
In some demo tests, NodeJS was locally downgraded to 16.20.2
|
||||
When this demo was last tested, NodeJS was locally downgraded to 16.20.2
|
||||
|
||||
:::
|
||||
|
||||
@ -322,7 +321,7 @@ npx webpack --mode=production
|
||||
6) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
7) Load the displayed URL (typically `http://localhost:8080/`) in a web browser.
|
||||
|
||||
@ -34,21 +34,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Browserify | Date |
|
||||
|:-----------|:-----------|
|
||||
| `17.0.1` | 2025-06-18 |
|
||||
| `16.5.2` | 2025-06-18 |
|
||||
| `15.2.0` | 2025-06-18 |
|
||||
| `14.5.0` | 2025-06-18 |
|
||||
| `13.3.0` | 2025-06-18 |
|
||||
| `12.0.2` | 2025-06-18 |
|
||||
| `11.2.0` | 2025-06-18 |
|
||||
| `10.2.6` | 2025-06-18 |
|
||||
| `9.0.8` | 2025-06-18 |
|
||||
| `8.1.3` | 2025-06-18 |
|
||||
| `7.1.0` | 2025-06-18 |
|
||||
| `6.3.4` | 2025-06-18 |
|
||||
| `5.13.1` | 2025-06-18 |
|
||||
| `4.2.3` | 2025-06-18 |
|
||||
| `3.46.1` | 2025-06-18 |
|
||||
| `17.0.0` | 2023-12-04 |
|
||||
| `3.46.1` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -105,7 +92,7 @@ const { utils, version, writeFileXLSX } = require('xlsx');
|
||||
|
||||
document.getElementById("xport").addEventListener("click", function() {
|
||||
/* fetch JSON data and parse */
|
||||
var url = "https://docs.sheetjs.com/executive.json";
|
||||
var url = "https://sheetjs.com/data/executive.json";
|
||||
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -115,7 +102,7 @@ document.getElementById("xport").addEventListener("click", function() {
|
||||
prez.forEach(function(row) {
|
||||
row.start = row.terms.find(function(term) {
|
||||
return term.type === "prez";
|
||||
}).start;
|
||||
}).start
|
||||
});
|
||||
prez.sort(function(l,r) { return l.start.localeCompare(r.start); });
|
||||
|
||||
@ -177,7 +164,7 @@ npm install --save browserify@3.46.1
|
||||
5) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
6) Load the displayed URL (typically `http://localhost:8080/`) in a web browser.
|
||||
|
||||
@ -40,8 +40,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| RequireJS | Date |
|
||||
|:----------|:-----------|
|
||||
| `2.3.7` | 2025-01-07 |
|
||||
| `2.1.22` | 2025-01-07 |
|
||||
| `2.3.6` | 2024-03-01 |
|
||||
| `2.1.22` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -51,7 +51,7 @@ The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
comply with AMD `define` semantics. They support RequireJS and the `r.js`
|
||||
optimizer out of the box.
|
||||
|
||||
### Configuration
|
||||
### Config
|
||||
|
||||
The RequireJS config should set the `xlsx` alias in the `paths` property.
|
||||
|
||||
@ -162,7 +162,7 @@ example, the following script corresponds to RequireJS `2.1.22`:
|
||||
require(["xlsx"], function(XLSX) {
|
||||
document.getElementById("xport").addEventListener("click", function() {
|
||||
/* fetch JSON data and parse */
|
||||
var url = "https://docs.sheetjs.com/executive.json";
|
||||
var url = "https://sheetjs.com/data/executive.json";
|
||||
fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -209,7 +209,7 @@ uses normal functions and traditional Promise chains.
|
||||
3) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
4) Load the displayed URL (typically `http://localhost:8080/`) in a web browser.
|
||||
|
||||
@ -46,11 +46,11 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Platform | Date |
|
||||
|:----------|:---------|:-----------|
|
||||
| `0.19.47` | NodeJS | 2025-01-03 |
|
||||
| `0.20.16` | Browser | 2025-01-03 |
|
||||
| `0.20.19` | NodeJS | 2025-03-03 |
|
||||
| `0.21.6` | NodeJS | 2025-03-03 |
|
||||
| `6.15.1` | NodeJS | 2025-01-03 |
|
||||
| `0.19.47` | NodeJS | 2024-03-31 |
|
||||
| `0.20.16` | Browser | 2024-03-31 |
|
||||
| `0.20.19` | NodeJS | 2024-03-31 |
|
||||
| `0.21.6` | NodeJS | 2024-03-31 |
|
||||
| `6.14.3` | NodeJS | 2024-03-31 |
|
||||
|
||||
:::
|
||||
|
||||
@ -203,7 +203,7 @@ npm init -y
|
||||
1) Install the dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz systemjs@6.15.1`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz systemjs@6.14.3`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Download [`SheetJSystem.js`](pathname:///systemjs/SheetJSystem.js) and move
|
||||
|
||||
@ -34,10 +34,10 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `4.29.1` | 2025-01-03 |
|
||||
| `3.29.5` | 2025-01-03 |
|
||||
| `2.79.1` | 2025-01-03 |
|
||||
| `1.32.1` | 2025-01-03 |
|
||||
| `4.13.0` | 2024-03-25 |
|
||||
| `3.29.4` | 2024-03-25 |
|
||||
| `2.79.1` | 2024-03-25 |
|
||||
| `1.32.1` | 2024-03-25 |
|
||||
|
||||
:::
|
||||
|
||||
@ -111,7 +111,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
|
||||
|
||||
document.getElementById("xport").addEventListener("click", async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -169,7 +169,7 @@ This step will create `bundle.js`
|
||||
5) Start a local HTTP server:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080/`) in a web browser.
|
||||
|
||||
@ -34,8 +34,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `2.14.4` | 2025-05-07 |
|
||||
| `1.12.3` | 2025-05-07 |
|
||||
| `2.10.3` | 2023-12-04 |
|
||||
| `1.12.3` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -51,7 +51,7 @@ can load relevant parts of the library:
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
```
|
||||
|
||||
:::danger Parcel Bug
|
||||
:::warning Parcel Bug
|
||||
|
||||
Errors of the form `Could not statically evaluate fs call` stem from a Parcel
|
||||
bug[^1]. Upgrade to Parcel version 1.5.0 or later.
|
||||
@ -86,7 +86,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
|
||||
document.getElementById("vers").innerText = version;
|
||||
document.getElementById("xport").onclick = async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -128,14 +128,13 @@ document.getElementById("xport").onclick = async() => {
|
||||
For ParcelJS version 1, the entire script should be copied to `index.js` and the
|
||||
main `index.html` page should load the `index.js` script:
|
||||
|
||||
<details>
|
||||
<summary><b>ParcelJS v1 example</b> (click to show)</summary>
|
||||
<details><summary><b>ParcelJS v1 example</b> (click to show)</summary>
|
||||
|
||||
```html title="index.html"
|
||||
<body>
|
||||
<h3>SheetJS <span id="vers"></span> export demo</h3>
|
||||
<button id="xport">Click to Export!</button>
|
||||
<script src="index.js" type="module"></script>
|
||||
<script src="index.js"></script>
|
||||
<body>
|
||||
```
|
||||
|
||||
@ -146,7 +145,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
|
||||
document.getElementById("vers").innerText = version;
|
||||
document.getElementById("xport").onclick = async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -208,7 +207,7 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
3) Run the ParcelJS development server:
|
||||
|
||||
```bash
|
||||
npx -y parcel index.html
|
||||
npx -y parcel@2.10.3 index.html
|
||||
```
|
||||
|
||||
The process will print a URL:
|
||||
@ -231,7 +230,7 @@ a web browser and click the "Click to Export!" button to generate a file.
|
||||
6) Build the production site:
|
||||
|
||||
```bash
|
||||
npx -y parcel build index.html
|
||||
npx -y parcel@2.10.0 build index.html
|
||||
```
|
||||
|
||||
The production site will be stored in the `dist` folder
|
||||
@ -239,7 +238,7 @@ The production site will be stored in the `dist` folder
|
||||
7) Start a local web server and serve the `dist` folder:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080/`) in a web browser.
|
||||
|
||||
@ -35,7 +35,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:----------|:-----------|
|
||||
| `1.21.1` | 2025-06-18 |
|
||||
| `1.2.246` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -63,9 +63,9 @@ part of the `utils` object, the required import is:
|
||||
import { utils, writeFile } from 'xlsx';
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
:::warning pass
|
||||
|
||||
When this demo was tested against recent versions of `@swc/core`, `spack` crashed:
|
||||
When this demo was tested against the `@swc/core@1.3.100`, `spack` crashed:
|
||||
|
||||
```
|
||||
thread '<unnamed>' panicked at 'cannot access a scoped thread local variable without calling `set` first',
|
||||
@ -73,10 +73,7 @@ thread '<unnamed>' panicked at 'cannot access a scoped thread local variable wit
|
||||
|
||||
**This is a bug in SWC**
|
||||
|
||||
This bug is known to affect versions `1.3.100`, `1.4.17`, and `1.10.6`.
|
||||
|
||||
This bug was fixed in version `1.21.1`. It is strongly recommended to upgrade
|
||||
existing projects to use `1.21.1` or to downgrade to `1.2.246`.
|
||||
Until the bug is fixed, it is strongly recommended to use `@swc/core@1.2.246`.
|
||||
|
||||
:::
|
||||
|
||||
@ -95,17 +92,17 @@ npm init -y
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.21.1`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.2.246`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.21.1`}
|
||||
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.2.246`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.21.1`}
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz regenerator-runtime @swc/cli @swc/core@1.2.246`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
@ -124,7 +121,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
|
||||
|
||||
document.getElementById("xport").addEventListener("click", async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -196,7 +193,7 @@ This command will create the script `lib/web.js`
|
||||
6) Start a local HTTP server, then go to `http://localhost:8080/`
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
Click on "Click here to export" to generate a file.
|
||||
|
||||
@ -43,18 +43,9 @@ Complete Examples are included [in the "Dojo" demo](/docs/demos/frontend/dojo)
|
||||
|
||||
## Snowpack
|
||||
|
||||
Snowpack was a development tool built by the AstroJS team.
|
||||
|
||||
:::caution pass
|
||||
|
||||
Snowpack is no longer maintained. The developers recommend [ViteJS](/docs/demos/frontend/bundler/vitejs)
|
||||
|
||||
:::
|
||||
|
||||
Snowpack works with no caveats.
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Example</b> (click to show)</summary>
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
@ -62,7 +53,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:--------|:-----------|
|
||||
| `3.8.8` | 2025-01-07 |
|
||||
| `3.8.8` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -102,7 +93,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
|
||||
|
||||
document.getElementById("xport").addEventListener("click", async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -161,32 +152,21 @@ Unlike other bundlers, Snowpack requires a full page including `HEAD` element.
|
||||
npx snowpack@3.8.8 build
|
||||
```
|
||||
|
||||
5) Start a local HTTP server:
|
||||
5) Start a local HTTP server, then go to `http://localhost:8080/`
|
||||
|
||||
```bash
|
||||
npx -y http-server build/
|
||||
npx http-server build/
|
||||
```
|
||||
|
||||
6) Open a web browser to the displayed URL (typically `http://localhost:8080/`).
|
||||
|
||||
Click on "Click here to export" to generate a file.
|
||||
|
||||
</details>
|
||||
|
||||
## WMR
|
||||
|
||||
WMR was a development tool built by the PreactJS team.
|
||||
|
||||
:::caution pass
|
||||
|
||||
WMR is no longer maintained. The developers recommend [ViteJS](/docs/demos/frontend/bundler/vitejs)
|
||||
|
||||
:::
|
||||
|
||||
WMR works with no caveats.
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Example</b> (click to show)</summary>
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
@ -194,7 +174,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:--------|:-----------|
|
||||
| `3.8.0` | 2025-01-07 |
|
||||
| `3.8.0` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -234,7 +214,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
|
||||
|
||||
document.getElementById("xport").addEventListener("click", async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const url = "https://sheetjs.com/data/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
@ -287,14 +267,12 @@ writeFileXLSX(workbook, "Presidents.xlsx");
|
||||
npx wmr@3.8.0 build
|
||||
```
|
||||
|
||||
5) Start a local HTTP server:
|
||||
5) Start a local HTTP server in `dist` folder and go to `http://localhost:8080/`
|
||||
|
||||
```bash
|
||||
npx -y http-server dist/
|
||||
npx http-server dist/
|
||||
```
|
||||
|
||||
6) Open a web browser to the displayed URL (typically `http://localhost:8080/`).
|
||||
|
||||
Click on "Click here to export" to generate a file.
|
||||
|
||||
</details>
|
||||
|
||||
@ -65,7 +65,7 @@ The following demos are in separate pages:
|
||||
<a href={item.href}>{item.label}</a>{item.customProps?.summary && (" - " + item.customProps.summary)}
|
||||
</li>);
|
||||
})}
|
||||
<li><a href="/docs/demos/frontend/bundler/#dojo">Dojo Toolkit</a></li>
|
||||
<li><a href="/docs/demos/frontend/bundler/#snowpack">Snowpack</a></li>
|
||||
<li><a href="/docs/demos/frontend/bundler/#wmr">WMR</a></li>
|
||||
<li><a href="/docs/demos/frontend/bundler#dojo">Dojo Toolkit</a></li>
|
||||
<li><a href="/docs/demos/frontend/bundler#snowpack">Snowpack</a></li>
|
||||
<li><a href="/docs/demos/frontend/bundler#wmr">WMR</a></li>
|
||||
</ul>
|
||||
|
||||
@ -11,19 +11,10 @@ pagination_next: demos/net/upload/index
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
A number of JavaScript APIs, including `XMLHttpRequest` and `fetch`, allow
|
||||
scripts to download spreadsheets for further processing.
|
||||
|
||||
This demo uses various APIs and wrapper libraries to download workbooks and pass
|
||||
raw binary data to SheetJS libraries.
|
||||
|
||||
- ["Browser Demos"](#browser-demos) run entirely within the web browser. A test
|
||||
workbook will be downloaded and parsed in the web browser.
|
||||
|
||||
- ["NodeJS Demos"](#nodejs-demos) run in NodeJS and other server-side platforms.
|
||||
`XMLHttpRequest` and `fetch` browser APIs enable binary data transfer between
|
||||
web browser clients and web servers. Since this library works in web browsers,
|
||||
server conversion work can be offloaded to the client! This demo shows a few
|
||||
common scenarios involving browser APIs and popular wrapper libraries.
|
||||
|
||||
:::info pass
|
||||
|
||||
@ -45,7 +36,7 @@ functions functions can send files to clients.
|
||||
|
||||
:::
|
||||
|
||||
## Binary Data
|
||||
## Downloading Binary Data
|
||||
|
||||
Most interesting spreadsheet files are binary data that contain byte sequences
|
||||
that represent invalid UTF-8 characters.
|
||||
@ -71,7 +62,7 @@ flowchart LR
|
||||
|
||||
```js
|
||||
/* download data into an ArrayBuffer object */
|
||||
const res = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
const res = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await res.arrayBuffer(); // recover data as ArrayBuffer
|
||||
|
||||
/* parse file */
|
||||
@ -80,19 +71,19 @@ const wb = XLSX.read(ab);
|
||||
|
||||
## Browser Demos
|
||||
|
||||
When the page is accessed, https://docs.sheetjs.com/pres.numbers will be fetched
|
||||
and parsed. The old table will be replaced with a table whose contents match the
|
||||
first worksheet. The SheetJS `sheet_to_html` method[^2] creates the HTML table.
|
||||
When the page is accessed, the browser will attempt to download <https://sheetjs.com/pres.numbers>
|
||||
and read the workbook. The old table will be replaced with a table whose
|
||||
contents match the first worksheet. The table is generated using the SheetJS
|
||||
`sheet_to_html` method[^2]
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
Each browser demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 133 | 2025-03-30 |
|
||||
| Safari 18.3 | 2025-03-30 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
| Browser | Date |
|
||||
|:------------|:-----------|
|
||||
| Chrome 120 | 2024-01-30 |
|
||||
| Safari 17.2 | 2024-01-15 |
|
||||
|
||||
:::
|
||||
|
||||
@ -117,10 +108,9 @@ req.onload = function(e) {
|
||||
req.send();
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live Download demo</b> (click to show)</summary>
|
||||
<details><summary><b>Live Download demo</b> (click to show)</summary>
|
||||
|
||||
This demo uses `XMLHttpRequest` to fetch https://docs.sheetjs.com/pres.numbers
|
||||
This demo uses `XMLHttpRequest` to download <https://sheetjs.com/pres.numbers>
|
||||
and show the data in an HTML table.
|
||||
|
||||
```jsx live
|
||||
@ -131,7 +121,7 @@ function SheetJSXHRDL() {
|
||||
React.useEffect(() => { (async() => {
|
||||
/* Fetch file */
|
||||
const req = new XMLHttpRequest();
|
||||
req.open("GET", "https://docs.sheetjs.com/pres.numbers", true);
|
||||
req.open("GET", "https://sheetjs.com/pres.numbers", true);
|
||||
req.responseType = "arraybuffer";
|
||||
req.onload = e => {
|
||||
/* Parse file */
|
||||
@ -170,11 +160,10 @@ fetch(url).then(function(res) {
|
||||
});
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live Download demo</b> (click to show)</summary>
|
||||
<details><summary><b>Live Download demo</b> (click to show)</summary>
|
||||
|
||||
This demo uses `fetch` to download https://docs.sheetjs.com/pres.numbers and
|
||||
show the data in an HTML table.
|
||||
This demo uses `fetch` to download <https://sheetjs.com/pres.numbers> and show
|
||||
the data in an HTML table.
|
||||
|
||||
```jsx live
|
||||
function SheetJSFetchDL() {
|
||||
@ -183,7 +172,7 @@ function SheetJSFetchDL() {
|
||||
/* Fetch and update HTML */
|
||||
React.useEffect(() => { (async() => {
|
||||
/* Fetch file */
|
||||
const res = await fetch("https://docs.sheetjs.com/pres.numbers");
|
||||
const res = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await res.arrayBuffer();
|
||||
|
||||
/* Parse file */
|
||||
@ -218,7 +207,7 @@ In a GET request, the default behavior is to return a `Blob` object. Passing
|
||||
|
||||
```js
|
||||
$.ajax({
|
||||
type: "GET", url: "https://docs.sheetjs.com/pres.numbers",
|
||||
type: "GET", url: "https://sheetjs.com/pres.numbers",
|
||||
|
||||
/* suppress jQuery post-processing */
|
||||
// highlight-next-line
|
||||
@ -265,11 +254,10 @@ async function workbook_dl_axios(url) {
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live Download demo</b> (click to show)</summary>
|
||||
<details><summary><b>Live Download demo</b> (click to show)</summary>
|
||||
|
||||
This demo uses `axios` to download https://docs.sheetjs.com/pres.numbers and
|
||||
show the data in an HTML table.
|
||||
This demo uses `axios` to download <https://sheetjs.com/pres.numbers> and show
|
||||
the data in an HTML table.
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -291,7 +279,7 @@ function SheetJSAxiosDL() {
|
||||
React.useEffect(() => { (async() => {
|
||||
if(typeof axios != "function") return setHTML("ReferenceError: axios is not defined");
|
||||
/* Fetch file */
|
||||
const res = await axios("https://docs.sheetjs.com/pres.numbers", {responseType: "arraybuffer"});
|
||||
const res = await axios("https://sheetjs.com/pres.numbers", {responseType: "arraybuffer"});
|
||||
|
||||
/* Parse file */
|
||||
const wb = XLSX.read(res.data);
|
||||
@ -327,11 +315,10 @@ superagent
|
||||
});
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Live Download demo</b> (click to show)</summary>
|
||||
<details><summary><b>Live Download demo</b> (click to show)</summary>
|
||||
|
||||
This demo uses `superagent` to download https://docs.sheetjs.com/pres.numbers
|
||||
and show the data in an HTML table.
|
||||
This demo uses `superagent` to download <https://sheetjs.com/pres.numbers> and
|
||||
show the data in an HTML table.
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -355,7 +342,7 @@ function SheetJSSuperAgentDL() {
|
||||
return setHTML("ReferenceError: superagent is not defined");
|
||||
/* Fetch file */
|
||||
superagent
|
||||
.get("https://docs.sheetjs.com/pres.numbers")
|
||||
.get("https://sheetjs.com/pres.numbers")
|
||||
.responseType("arraybuffer")
|
||||
.end((err, res) => {
|
||||
/* Parse file */
|
||||
@ -384,7 +371,7 @@ The `https` module provides a low-level `get` method for HTTPS GET requests:
|
||||
```js title="SheetJSHTTPSGet.js"
|
||||
var https = require("https"), XLSX = require("xlsx");
|
||||
|
||||
https.get('https://docs.sheetjs.com/pres.xlsx', function(res) {
|
||||
https.get('https://sheetjs.com/pres.numbers', function(res) {
|
||||
var bufs = [];
|
||||
res.on('data', function(chunk) { bufs.push(chunk); });
|
||||
res.on('end', function() {
|
||||
@ -397,36 +384,14 @@ https.get('https://docs.sheetjs.com/pres.xlsx', function(res) {
|
||||
});
|
||||
```
|
||||
|
||||
:::note Tested Deployments
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
This demo was tested in the following environments:
|
||||
:::note Tested Environments
|
||||
|
||||
| NodeJS | Date | Workarounds |
|
||||
|:-----------|:-----------|:-------------------------------|
|
||||
| `0.10.48` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `0.12.18` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `4.9.1` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `6.17.1` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `8.17.0` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `10.24.1` | 2025-03-31 | |
|
||||
| `12.22.12` | 2025-03-31 | |
|
||||
| `14.21.3` | 2025-03-31 | |
|
||||
| `16.20.2` | 2025-03-31 | |
|
||||
| `18.20.8` | 2025-03-31 | |
|
||||
| `20.19.0` | 2025-03-31 | |
|
||||
| `22.14.0` | 2025-03-31 | |
|
||||
|
||||
The `NODE_TLS_REJECT_UNAUTHORIZED` workaround sets the value to `'0'`:
|
||||
|
||||
```js title="Legacy NodeJS Certificate has Expired Bypass (prepend to script)"
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
```
|
||||
This demo was last tested on 2024 January 15 against NodeJS `20.11.0`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
1) Install the [NodeJS module](/docs/getting-started/installation/nodejs)
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
@ -443,24 +408,11 @@ node SheetJSHTTPSGet.js
|
||||
|
||||
If successful, the script will print CSV contents of the test file.
|
||||
|
||||
:::caution pass
|
||||
|
||||
For older versions of NodeJS, the script may fail due to a certificate error.
|
||||
The error can be suppressed by prepending the following line to the script:
|
||||
|
||||
```js title="SheetJSHTTPSGet.js (add to top)"
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
```
|
||||
|
||||
**It is strongly encouraged to upgrade to a newer NodeJS version!**
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
### fetch
|
||||
|
||||
:::info pass
|
||||
:::caution pass
|
||||
|
||||
Experimental support for `fetch` was introduced in NodeJS `16.15.0`. It will be
|
||||
considered stable in NodeJS LTS version `22`.
|
||||
@ -479,21 +431,14 @@ async function parse_from_url(url) {
|
||||
}
|
||||
```
|
||||
|
||||
:::note Tested Deployments
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
This demo was tested in the following environments:
|
||||
:::note Tested Environments
|
||||
|
||||
| NodeJS | Date |
|
||||
|:-----------|:-----------|
|
||||
| `18.20.8` | 2025-03-30 |
|
||||
| `20.18.0` | 2025-03-30 |
|
||||
| `22.14.0` | 2025-03-30 |
|
||||
This demo was last tested on 2024 January 15 against NodeJS `20.11.0`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
1) Install the [NodeJS module](/docs/getting-started/installation/nodejs)
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
@ -514,7 +459,7 @@ async function parse_from_url(url) {
|
||||
}
|
||||
|
||||
(async() => {
|
||||
const wb = await parse_from_url('https://docs.sheetjs.com/pres.numbers');
|
||||
const wb = await parse_from_url('https://sheetjs.com/pres.numbers');
|
||||
/* print the first worksheet to console */
|
||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||
console.log(XLSX.utils.sheet_to_csv(ws));
|
||||
@ -538,7 +483,7 @@ was added to the platform, third party modules wrapped the native APIs.
|
||||
|
||||
#### request
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
`request` has been deprecated and should only be used in legacy deployments.
|
||||
|
||||
@ -548,7 +493,7 @@ Setting the option `encoding: null` passes raw buffers:
|
||||
|
||||
```js title="SheetJSRequest.js"
|
||||
var XLSX = require('xlsx'), request = require('request');
|
||||
var url = 'https://docs.sheetjs.com/pres.xlsx';
|
||||
var url = 'https://sheetjs.com/pres.numbers';
|
||||
|
||||
/* call `request` with the option `encoding: null` */
|
||||
// highlight-next-line
|
||||
@ -565,36 +510,14 @@ request(url, {encoding: null}, function(err, res, data) {
|
||||
});
|
||||
```
|
||||
|
||||
:::note Tested Deployments
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
This demo was tested in the following environments:
|
||||
:::note Tested Environments
|
||||
|
||||
| NodeJS | `request` | Date | Workarounds |
|
||||
|:-----------|:----------|:-----------|:-------------------------------|
|
||||
| `0.10.48` | `2.22.0` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `0.12.18` | `2.22.0` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `4.9.1` | `2.22.0` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `6.17.1` | `2.88.2` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `8.17.0` | `2.88.2` | 2025-03-31 | `NODE_TLS_REJECT_UNAUTHORIZED` |
|
||||
| `10.24.1` | `2.88.2` | 2025-03-31 | |
|
||||
| `12.22.12` | `2.88.2` | 2025-03-31 | |
|
||||
| `14.21.3` | `2.88.2` | 2025-03-31 | |
|
||||
| `16.20.2` | `2.88.2` | 2025-03-31 | |
|
||||
| `18.20.8` | `2.88.2` | 2025-03-31 | |
|
||||
| `20.19.0` | `2.88.2` | 2025-03-31 | |
|
||||
| `22.14.0` | `2.88.2` | 2025-03-31 | |
|
||||
|
||||
The `NODE_TLS_REJECT_UNAUTHORIZED` workaround sets the value to `'0'`:
|
||||
|
||||
```js title="Legacy NodeJS Certificate has Expired Bypass (prepend to script)"
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
```
|
||||
This demo was last tested on 2024 January 15 against request `2.88.2`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
1) Install the [NodeJS module](/docs/getting-started/installation/nodejs)
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
@ -611,19 +534,6 @@ node SheetJSRequest.js
|
||||
|
||||
If successful, the script will print CSV contents of the test file.
|
||||
|
||||
:::caution pass
|
||||
|
||||
For older versions of NodeJS, the script may fail due to a certificate error.
|
||||
The error can be suppressed by prepending the following line to the script:
|
||||
|
||||
```js title="SheetJSRequest.js (add to top)"
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
```
|
||||
|
||||
**It is strongly encouraged to upgrade to a newer NodeJS version!**
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
#### axios
|
||||
@ -642,32 +552,18 @@ async function workbook_dl_axios(url) {
|
||||
}
|
||||
```
|
||||
|
||||
:::note Tested Deployments
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
This demo was tested in the following environments:
|
||||
:::note Tested Environments
|
||||
|
||||
| NodeJS | Axios | Date |
|
||||
|:-----------|:-------|:-----------|
|
||||
| `4.9.1` | 0.22.0 | 2025-03-31 |
|
||||
| `6.17.1` | 0.22.0 | 2025-03-31 |
|
||||
| `8.17.0` | 0.28.1 | 2025-03-31 |
|
||||
| `10.24.1` | 0.28.1 | 2025-03-31 |
|
||||
| `12.22.12` | 1.8.4 | 2025-03-31 |
|
||||
| `14.21.3` | 1.8.4 | 2025-03-31 |
|
||||
| `16.20.2` | 1.8.4 | 2025-03-31 |
|
||||
| `18.20.8` | 1.8.4 | 2025-03-31 |
|
||||
| `20.19.0` | 1.8.4 | 2025-03-31 |
|
||||
| `22.14.0` | 1.8.4 | 2025-03-31 |
|
||||
This demo was last tested on 2024 January 15 against Axios `1.6.5`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
1) Install the [NodeJS module](/docs/getting-started/installation/nodejs)
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz axios@1.8.4`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz axios@1.6.5`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Save the following to `SheetJSAxios.js`:
|
||||
@ -683,7 +579,7 @@ async function workbook_dl_axios(url) {
|
||||
}
|
||||
|
||||
(async() => {
|
||||
const wb = await workbook_dl_axios('https://docs.sheetjs.com/pres.numbers');
|
||||
const wb = await workbook_dl_axios('https://sheetjs.com/pres.numbers');
|
||||
/* print the first worksheet to console */
|
||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||
console.log(XLSX.utils.sheet_to_csv(ws));
|
||||
@ -698,27 +594,6 @@ node SheetJSAxios.js
|
||||
|
||||
If successful, the script will print CSV contents of the test file.
|
||||
|
||||
:::caution pass
|
||||
|
||||
Legacy NodeJS versions do not support `async` functions. The `async` function
|
||||
must be manually translated to a `then` chain:
|
||||
|
||||
```js title="SheetJSAxios.js (ES5)"
|
||||
const XLSX = require("xlsx"), axios = require("axios");
|
||||
|
||||
var url = 'https://docs.sheetjs.com/pres.numbers';
|
||||
|
||||
axios(url, {responseType:'arraybuffer'}).then(function(res) {
|
||||
/* at this point, res.data is a Buffer */
|
||||
var wb = XLSX.read(res.data, {type: "buffer"});
|
||||
/* print the first worksheet to console */
|
||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||
console.log(XLSX.utils.sheet_to_csv(ws));
|
||||
});
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
## Other Platforms
|
||||
@ -17,7 +17,7 @@ cloud storage solutions. Spreadsheets can be written using SheetJS and uploaded.
|
||||
|
||||
This demo explores file uploads using a number of browser APIs and wrapper
|
||||
libraries. The upload process will generate a sample XLSX workbook, upload the
|
||||
file to [a test server](https://s2c.sheetjs.com), and display the response.
|
||||
file to [a test server](#test-server), and display the response.
|
||||
|
||||
:::info pass
|
||||
|
||||
@ -60,120 +60,14 @@ flowchart LR
|
||||
form --> |POST\nrequest| server
|
||||
```
|
||||
|
||||
### Generating Files
|
||||
|
||||
In a typical scenario, a process generates arrays of simple objects.
|
||||
|
||||
The SheetJS `json_to_sheet` method[^2] generates a SheetJS worksheet object[^3].
|
||||
The `book_new` method[^4] creates a workbook object that includes the worksheet.
|
||||
|
||||
The `write` method[^5] generates the file in memory.
|
||||
|
||||
The following snippet creates a sample dataset and generates an `ArrayBuffer`
|
||||
object representing the workbook bytes:
|
||||
|
||||
```js title="Generating an XLSX file in memory"
|
||||
```js
|
||||
/* create sample SheetJS workbook object */
|
||||
var aoa = [
|
||||
["S", "h", "e", "e", "t", "J", "S"],
|
||||
[ 5, 4, 3, 3, 7, 9, 5]
|
||||
];
|
||||
var ws = XLSX.utils.aoa_to_sheet(aoa);
|
||||
var wb = XLSX.utils.book_new(ws, "Sheet1");
|
||||
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
|
||||
|
||||
/* export SheetJS workbook object to XLSX file bytes */
|
||||
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'array'});
|
||||
```
|
||||
|
||||
### Creating Form Data
|
||||
|
||||
`File` objects represent files. The `File` constructor accepts an array of data
|
||||
fragments and a filename.
|
||||
|
||||
Browser APIs typically represent form body data using `FormData` objects. The
|
||||
`append` method adds fields to the `FormData` object. Adding `File` objects
|
||||
effectively "attaches" a file in the upload.
|
||||
|
||||
The following snippet constructs a new `FormData` object. The `file` field in
|
||||
the form will be set to the data from the previous snippet:
|
||||
|
||||
```js title="Creating Form Data and attaching the generated file"
|
||||
/* create File */
|
||||
var file = new File([data], 'sheetjs.xlsx')
|
||||
// generated XLSX ^^^^ ^^^^^^^^^^^^ file name
|
||||
|
||||
/* build FormData with the generated file */
|
||||
var fdata = new FormData();
|
||||
fdata.append('file', file);
|
||||
// ^^^^ field name in the form body
|
||||
```
|
||||
|
||||
### POST Request
|
||||
|
||||
This demo explores a number of APIs and libraries for making POST requests. Each
|
||||
approach will upload data stored in `FormData` objects.
|
||||
|
||||
This snippet uses `XMLHttpRequest` to upload data to https://s2c.sheetjs.com:
|
||||
|
||||
```js title="Uploading Form Data with XMLHttpRequest"
|
||||
/* send data using XMLHttpRequest */
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("POST", "https://s2c.sheetjs.com", true);
|
||||
req.send(fdata);
|
||||
```
|
||||
|
||||
## Browser Demos
|
||||
|
||||
When the upload button is clicked, the browser will build up a new workbook,
|
||||
generate a XLSX file, upload it to https://s2c.sheetjs.com and show the
|
||||
response. If the process was successful, a HTML table will be displayed
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
Each browser demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 133 | 2025-03-30 |
|
||||
| Safari 18.3 | 2025-03-30 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
|
||||
:::
|
||||
|
||||
#### Test Server
|
||||
|
||||
The https://s2c.sheetjs.com service is currently hosted on Deno Deploy. The
|
||||
["Deno Deploy" demo](/docs/demos/cloud/deno#demo) covers the exact steps for
|
||||
deploying the service.
|
||||
|
||||
The CORS-enabled service handles POST requests by looking for uploaded files in
|
||||
the `"file"` key. If a file is found, the file will be parsed using the SheetJS
|
||||
`read` method[^6] and the first worksheet will be converted to HTML using the
|
||||
`sheet_to_html` method[^7].
|
||||
|
||||
### XMLHttpRequest
|
||||
|
||||
Using the `XMLHttpRequest` API, the `send` method can accept `FormData` objects:
|
||||
|
||||
```js title="Uploading Form Data with XMLHttpRequest"
|
||||
/* send data using XMLHttpRequest */
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("POST", "https://s2c.sheetjs.com", true);
|
||||
req.send(fdata);
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Code Snippet</b> (click to show)</summary>
|
||||
|
||||
```js title="SheetJS + XMLHttpRequest example"
|
||||
/* create sample SheetJS workbook object */
|
||||
var aoa = [
|
||||
["S", "h", "e", "e", "t", "J", "S"],
|
||||
[ 5, 4, 3, 3, 7, 9, 5]
|
||||
];
|
||||
const ws = XLSX.utils.aoa_to_sheet(aoa);
|
||||
const wb = XLSX.utils.book_new();
|
||||
var wb = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
|
||||
|
||||
/* export SheetJS workbook object to XLSX file bytes */
|
||||
@ -190,13 +84,42 @@ req.open("POST", "https://s2c.sheetjs.com", true);
|
||||
req.send(fdata);
|
||||
```
|
||||
|
||||
</details>
|
||||
## Test Server
|
||||
|
||||
<details>
|
||||
<summary><b>Live demo</b> (click to show)</summary>
|
||||
The <https://s2c.sheetjs.com> service is currently hosted on Deno Deploy. The
|
||||
["Deno Deploy" demo](/docs/demos/cloud/deno#demo) covers the exact steps for
|
||||
deploying the service.
|
||||
|
||||
The CORS-enabled service handles POST requests by looking for uploaded files in
|
||||
the `"file"` key. If a file is found, the file will be parsed using the SheetJS
|
||||
`read` method[^2] and the first worksheet will be converted to HTML using the
|
||||
`sheet_to_html` method[^3].
|
||||
|
||||
## Browser Demos
|
||||
|
||||
When the upload button is clicked, the browser will build up a new workbook,
|
||||
generate a XLSX file, upload it to <https://s2c.sheetjs.com> and show the
|
||||
response. If the process was successful, a HTML table will be displayed
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
Each browser demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:------------|:-----------|
|
||||
| Chrome 120 | 2024-01-15 |
|
||||
| Safari 17.3 | 2024-02-21 |
|
||||
|
||||
:::
|
||||
|
||||
### XMLHttpRequest
|
||||
|
||||
This demo uses [the code snippet from the intro](#uploading-binary-data).
|
||||
|
||||
<details><summary><b>Live demo</b> (click to show)</summary>
|
||||
|
||||
This demo starts from an array of arrays of data. When the button is clicked, a
|
||||
workbook file will be generated and uploaded to https://s2c.sheetjs.com. The
|
||||
workbook file will be generated and uploaded to <https://s2c.sheetjs.com>. The
|
||||
service will return a HTML table.
|
||||
|
||||
```jsx live
|
||||
@ -261,15 +184,7 @@ function SheetJSXHRUL() {
|
||||
|
||||
`fetch` takes a second parameter which allows for setting POST request body:
|
||||
|
||||
```js title="Uploading Form Data with fetch"
|
||||
/* send data using fetch */
|
||||
fetch("https://s2c.sheetjs.com", { method: "POST", body: fdata });
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Code Snippet</b> (click to show)</summary>
|
||||
|
||||
```js title="SheetJS + fetch example"
|
||||
```js
|
||||
/* create sample SheetJS workbook object */
|
||||
var aoa = [
|
||||
["S", "h", "e", "e", "t", "J", "S"],
|
||||
@ -291,12 +206,9 @@ fdata.append('file', new File([data], 'sheetjs.xlsx'));
|
||||
fetch("https://s2c.sheetjs.com", { method: "POST", body: fdata });
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary><b>Live demo</b> (click to show)</summary>
|
||||
|
||||
<details>
|
||||
<summary><b>Live demo</b> (click to show)</summary>
|
||||
|
||||
This demo uses `fetch` to upload data to https://s2c.sheetjs.com. It will parse
|
||||
This demo uses `fetch` to upload data to <https://s2c.sheetjs.com>. It will parse
|
||||
the workbook and return an HTML table.
|
||||
|
||||
```jsx live
|
||||
@ -364,15 +276,7 @@ are still relevant.
|
||||
|
||||
Uploading form data is nearly identical to the `fetch` example:
|
||||
|
||||
```js title="Uploading Form Data with axios"
|
||||
/* send data using axios */
|
||||
axios("https://s2c.sheetjs.com", { method: "POST", body: fdata });
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Code Snippet</b> (click to show)</summary>
|
||||
|
||||
```js title="SheetJS + axios example"
|
||||
```js
|
||||
/* create sample SheetJS workbook object */
|
||||
var aoa = [
|
||||
["S", "h", "e", "e", "t", "J", "S"],
|
||||
@ -394,12 +298,9 @@ fdata.append('file', new File([data], 'sheetjs.xlsx'));
|
||||
axios("https://s2c.sheetjs.com", { method: "POST", data: fdata });
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary><b>Live demo</b> (click to show)</summary>
|
||||
|
||||
<details>
|
||||
<summary><b>Live demo</b> (click to show)</summary>
|
||||
|
||||
This demo uses `axios` to upload data to https://s2c.sheetjs.com. It will parse
|
||||
This demo uses `axios` to upload data to <https://s2c.sheetjs.com>. It will parse
|
||||
the workbook and return an HTML table.
|
||||
|
||||
:::caution pass
|
||||
@ -474,15 +375,7 @@ with a "Fluent Interface".
|
||||
|
||||
The `send` method accepts a `FormData` object as the first argument:
|
||||
|
||||
```js title="Uploading Form Data with superagent"
|
||||
/* send data using superagent */
|
||||
superagent.post("https://s2c.sheetjs.com").send(fd);
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Code Snippet</b> (click to show)</summary>
|
||||
|
||||
```js title="SheetJS + superagent example"
|
||||
```js
|
||||
/* create sample SheetJS workbook object */
|
||||
var aoa = [
|
||||
["S", "h", "e", "e", "t", "J", "S"],
|
||||
@ -504,12 +397,9 @@ fdata.append('file', new File([data], 'sheetjs.xlsx'));
|
||||
superagent.post("https://s2c.sheetjs.com").send(fd);
|
||||
```
|
||||
|
||||
</details>
|
||||
<details><summary><b>Live demo</b> (click to show)</summary>
|
||||
|
||||
<details>
|
||||
<summary><b>Live demo</b> (click to show)</summary>
|
||||
|
||||
This demo uses `superagent` to upload data to https://s2c.sheetjs.com. It will
|
||||
This demo uses `superagent` to upload data to <https://s2c.sheetjs.com>. It will
|
||||
parse the workbook and return an HTML table.
|
||||
|
||||
:::caution pass
|
||||
@ -579,27 +469,21 @@ function SheetJSSuperAgentUL() {
|
||||
|
||||
## NodeJS Demos
|
||||
|
||||
These examples show how to upload data in NodeJS scripts.
|
||||
These examples show how to upload data in NodeJS.
|
||||
|
||||
### fetch
|
||||
|
||||
NodeJS `fetch`, available in version 20, mirrors the [browser `fetch`](#fetch).
|
||||
The `fetch` implementation mirrors the [browser `fetch`](#fetch).
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| NodeJS | Date |
|
||||
|:-----------|:-----------|
|
||||
| `22.13.0` | 2025-01-08 |
|
||||
| `20.18.1` | 2025-01-08 |
|
||||
This demo was last tested on 2023 November 19 against NodeJS `20.9.0`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Example</b> (click to show)</summary>
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
This demo uses `fetch` to upload data to https://s2c.sheetjs.com. It will parse
|
||||
This demo uses `fetch` to upload data to <https://s2c.sheetjs.com>. It will parse
|
||||
the workbook and return data in CSV rows.
|
||||
|
||||
1) Install the [SheetJS NodeJS module](/docs/getting-started/installation/nodejs):
|
||||
@ -649,164 +533,6 @@ It will print CSV contents of the test file.
|
||||
|
||||
</details>
|
||||
|
||||
### request
|
||||
|
||||
The deprecated [`request`](https://github.com/request/request) library is useful
|
||||
in legacy NodeJS deployments where `fetch` may not be available.
|
||||
|
||||
The SheetJS `write` method will generate NodeJS Buffer objects when the `type`
|
||||
option is set to `"buffer"`:
|
||||
|
||||
```js
|
||||
/* export SheetJS workbook object to XLSX file bytes */
|
||||
const data = XLSX.write(wb, {bookType: 'xlsx', type: 'buffer'});
|
||||
```
|
||||
|
||||
A `request` file object can be built using the Buffer. The file object must
|
||||
include an `options` object that specifies the file name and content type:
|
||||
|
||||
```js
|
||||
/* create a file object for the `request` form data */
|
||||
const request_file = {
|
||||
/* `value` can be a Buffer object */
|
||||
value: data,
|
||||
options: {
|
||||
/* `options.filename` is the filename that the server will see */
|
||||
filename: "sheetjs.xlsx",
|
||||
/* `options.contentType` must be set */
|
||||
contentType: "application/octet-stream"
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
The `request` and `request.post` methods accept an options argument. The
|
||||
`formData` property specifies the body to be uploaded. Property names correspond
|
||||
to the uploaded form names and values describe the uploaded content.
|
||||
|
||||
The `request` file object should be added to the `formData` object:
|
||||
|
||||
```js
|
||||
request({
|
||||
// ... other options ...
|
||||
formData: {
|
||||
// ... other form fields ...
|
||||
|
||||
/* the server will see the uploaded file in the `file` body property */
|
||||
/* highlight-next-line */
|
||||
file: request_file
|
||||
}
|
||||
}, function(err, res) { /* handle response ... */ });
|
||||
```
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| NodeJS | `request` | Date |
|
||||
|:-----------|:----------|:-----------|
|
||||
| `22.13.0` | `2.88.2` | 2025-01-08 |
|
||||
| `20.18.1` | `2.88.2` | 2025-01-08 |
|
||||
| `18.20.5` | `2.88.2` | 2025-01-08 |
|
||||
| `16.20.2` | `2.88.2` | 2025-01-08 |
|
||||
| `14.21.3` | `2.88.2` | 2025-01-08 |
|
||||
| `12.22.12` | `2.88.2` | 2025-01-08 |
|
||||
| `10.24.1` | `2.88.2` | 2025-01-08 |
|
||||
| `8.17.0` | `2.88.2` | 2025-01-08 |
|
||||
| `6.17.1` | `2.88.2` | 2025-01-08 |
|
||||
| `4.9.1` | `2.81.0` | 2025-01-08 |
|
||||
| `0.12.18` | `2.81.0` | 2025-01-08 |
|
||||
| `0.10.48` | `2.81.0` | 2025-01-08 |
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
This demo uses `request` to upload data to https://s2c.sheetjs.com. It will
|
||||
parse the workbook and return data in CSV rows.
|
||||
|
||||
1) Install the [SheetJS NodeJS module](/docs/getting-started/installation/nodejs)
|
||||
and `request` module:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz request`}
|
||||
</CodeBlock>
|
||||
|
||||
:::note pass
|
||||
|
||||
The current version of `request` requires NodeJS 6 or later. For older versions
|
||||
of NodeJS, `request` version `2.81.0` should be installed.
|
||||
|
||||
:::
|
||||
|
||||
2) Save the following to `SheetJSRequest.js`:
|
||||
|
||||
```js title="SheetJSRequest.js"
|
||||
const XLSX = require("xlsx");
|
||||
const request = require("request");
|
||||
|
||||
/* create sample SheetJS workbook object */
|
||||
var aoa = [
|
||||
["S", "h", "e", "e", "t", "J", "S"],
|
||||
[ 5, 4, 3, 3, 7, 9, 5]
|
||||
];
|
||||
const ws = XLSX.utils.aoa_to_sheet(aoa);
|
||||
const wb = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
|
||||
|
||||
/* export SheetJS workbook object to XLSX file bytes */
|
||||
var data = XLSX.write(wb, {bookType: 'xlsx', type: 'buffer'});
|
||||
|
||||
request({
|
||||
method: "POST",
|
||||
url: "https://s2c.sheetjs.com",
|
||||
headers: {
|
||||
Accept: "text/html"
|
||||
},
|
||||
formData: {
|
||||
type: "csv",
|
||||
file: {
|
||||
value: data,
|
||||
options: {
|
||||
filename: "sheetjs.xlsx",
|
||||
contentType: "application/octet-stream"
|
||||
}
|
||||
}
|
||||
}
|
||||
}, function(err, res, body) {
|
||||
if(err) return console.error(err);
|
||||
console.log(body);
|
||||
});
|
||||
```
|
||||
|
||||
3) Run the script:
|
||||
|
||||
```bash
|
||||
node SheetJSRequest.js
|
||||
```
|
||||
|
||||
It will print CSV contents of the test file.
|
||||
|
||||
:::caution pass
|
||||
|
||||
For legacy versions of NodeJS, the process may fail with a certificate error:
|
||||
|
||||
```
|
||||
{ [Error: certificate not trusted] code: 'CERT_UNTRUSTED' }
|
||||
```
|
||||
|
||||
The environment variable `NODE_TLS_REJECT_UNAUTHORIZED` can be set to `0`:
|
||||
|
||||
```bash
|
||||
env NODE_TLS_REJECT_UNAUTHORIZED="0" node SheetJSRequest.js
|
||||
```
|
||||
|
||||
**It is strongly recommended to upgrade to a newer version of NodeJS!**
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@ -845,9 +571,5 @@ const res = await fetch(url, {method:"POST", body: fdata});
|
||||
If the generated file is valid, then the issue is in the server infrastructure.
|
||||
|
||||
[^1]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
[^2]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
|
||||
[^3]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/sheet) for more details.
|
||||
[^4]: See [`book_new` in "Utilities"](/docs/api/utilities/wb)
|
||||
[^5]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
[^6]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^7]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
|
||||
@ -22,12 +22,8 @@ The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `4.21.2` | 2025-01-03 |
|
||||
| `5.0.1` | 2025-01-03 |
|
||||
This demo was tested on 2024 March 11 using `express-formidable@1.2.0` and
|
||||
ExpressJS `4.18.3`
|
||||
|
||||
:::
|
||||
|
||||
@ -140,28 +136,26 @@ app.get('/download', function(req, res) {
|
||||
res.status(200).end(buf);
|
||||
// highlight-end
|
||||
});
|
||||
app.listen(+process.env.PORT||3000, function() { console.log("Ready to go"); });
|
||||
app.listen(+process.env.PORT||3000);
|
||||
```
|
||||
|
||||
2) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz express@4.21.2 express-formidable@1.2.0`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz express@4.18.3 express-formidable@1.2.0`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Start server
|
||||
3) Start server (note: it will not print anything to console when running)
|
||||
|
||||
```bash
|
||||
node SheetJSExpressCSV.js
|
||||
```
|
||||
|
||||
Once the server starts, the process will print `Ready to go`.
|
||||
|
||||
4) Test POST requests using https://docs.sheetjs.com/pres.numbers . The commands
|
||||
should be run in a new terminal window:
|
||||
4) Test POST requests using <https://sheetjs.com/pres.numbers> . The following
|
||||
commands should be run in a new terminal window:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://sheetjs.com/pres.numbers
|
||||
curl -X POST -F upload=@pres.numbers http://localhost:3000/upload
|
||||
```
|
||||
|
||||
|
||||
@ -22,12 +22,7 @@ The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Drash | Deno | Date |
|
||||
|:--------|:-------|:-----------|
|
||||
| `2.8.1` | 1.46.0 | 2025-05-21 |
|
||||
| `2.8.1` | 2.3.3 | 2025-05-21 |
|
||||
This demo was last tested on 2024 March 11 against Drash 2.8.1 and Deno 1.41.2.
|
||||
|
||||
:::
|
||||
|
||||
@ -128,24 +123,13 @@ curl -LO https://docs.sheetjs.com/drash/SheetJSDrash.ts
|
||||
deno run --allow-net SheetJSDrash.ts
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
Deno 2 requires the `--allow-import` entitlement:
|
||||
|
||||
```bash
|
||||
deno run --allow-net --allow-write --allow-import SheetJSDrash.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
3) Download the test file https://docs.sheetjs.com/pres.numbers
|
||||
3) Download the test file <https://sheetjs.com/pres.numbers>
|
||||
|
||||
4) Open `http://localhost:7262/` in your browser.
|
||||
|
||||
Click "Choose File" and select `pres.numbers` from the Downloads folder.
|
||||
Click "Choose File" and select `pres.numbers`. Then click "Submit"
|
||||
|
||||
Click "Submit" to make a request to the Drash server. The response should show
|
||||
the contents of the file as an HTML table.
|
||||
The page should show the contents of the file as an HTML table.
|
||||
|
||||
5) Open `http://localhost:7262/export` in your browser.
|
||||
|
||||
|
||||
@ -1,250 +0,0 @@
|
||||
---
|
||||
title: Sheets on Fire with HonoJS
|
||||
sidebar_label: HonoJS
|
||||
pagination_prev: demos/net/network/index
|
||||
pagination_next: demos/net/email/index
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[HonoJS](https://hono.dev/) is a lightweight server-side framework.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses HonoJS and SheetJS to read and write data. We'll explore how to
|
||||
parse uploaded files in a POST request handler and respond to GET requests with
|
||||
downloadable spreadsheets.
|
||||
|
||||
The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Platform | HonoJS | Date |
|
||||
|:---------------|:----------|:-----------|
|
||||
| BunJS `1.2.13` | `2.7.8` | 2025-05-21 |
|
||||
| BunJS `1.2.13` | `3.12.12` | 2025-05-21 |
|
||||
| BunJS `1.2.13` | `4.7.10` | 2025-05-21 |
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS BunJS module](/docs/getting-started/installation/bun) can be
|
||||
imported from HonoJS server scripts.
|
||||
|
||||
### Reading Data
|
||||
|
||||
The HonoJS body parser[^1] processes files in POST requests. The body parser
|
||||
returns an object that can be indexed by field name:
|
||||
|
||||
```js
|
||||
/* /import route */
|
||||
app.post('/import', async(c) => {
|
||||
/* parse body */
|
||||
const body = await c.req.parseBody();
|
||||
/* get a file uploaded in the `upload` field */
|
||||
// highlight-next-line
|
||||
const file = body["upload"];
|
||||
|
||||
/* `file` is a `File` object */
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
By default, the HonoJS body parser will use the last value when the form body
|
||||
specifies multiple values for a given field. To force the body parser to process
|
||||
all files, the field name must end with `[]`:
|
||||
|
||||
```js
|
||||
/* parse body */
|
||||
const body = await c.req.parseBody();
|
||||
/* get all files uploaded in the `upload` field */
|
||||
// highlight-next-line
|
||||
const files = body["upload[]"];
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
HonoJS exposes each file as a `Blob` object. The `Blob#arrayBuffer` method
|
||||
returns a Promise that resolves to an `ArrayBuffer`. That `ArrayBuffer` can be
|
||||
parsed with the SheetJS `read` method[^2].
|
||||
|
||||
This example server responds to POST requests. The server will look for a file
|
||||
in the request body under the `"upload"` key. If a file is present, the server
|
||||
will parse the file and, generate CSV rows using the `sheet_to_csv` method[^3],
|
||||
and respond with text:
|
||||
|
||||
```js
|
||||
import { Hono } from 'hono';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
const app = new Hono();
|
||||
app.post('/import', async(c) => {
|
||||
/* get file data */
|
||||
const body = await c.req.parseBody();
|
||||
const file = body["upload"];
|
||||
const ab = await file.arrayBuffer();
|
||||
/* parse */
|
||||
const wb = read(ab);
|
||||
/* generate CSV */
|
||||
const csv = utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
|
||||
return c.text(csv);
|
||||
});
|
||||
export default app;
|
||||
```
|
||||
|
||||
### Writing Data
|
||||
|
||||
Given a SheetJS workbook object, the `write` method using `type: "buffer"`[^4]
|
||||
generates data objects which can be passed to the response `body` method.
|
||||
|
||||
This example server responds to GET requests. The server will generate a SheetJS
|
||||
worksheet object from an array of arrays[^5], build up a new workbook using the
|
||||
`book_new`[^6] utility method, generate a XLSX file using `write`, and send the
|
||||
file with appropriate headers to download `SheetJSHonoJS.xlsx`:
|
||||
|
||||
```js
|
||||
import { Hono } from 'hono';
|
||||
import { utils, write } from "xlsx";
|
||||
|
||||
const app = new Hono();
|
||||
app.get("/export", (c) => {
|
||||
/* generate SheetJS workbook object */
|
||||
var ws = utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
|
||||
var wb = utils.book_new(ws, "Data");
|
||||
/* generate buffer */
|
||||
var buf = write(wb, {type: "buffer", bookType: "xlsx"});
|
||||
/* set headers */
|
||||
c.header('Content-Disposition', 'attachment; filename="SheetJSHonoJS.xlsx"');
|
||||
c.header('Content-Type', 'application/vnd.ms-excel');
|
||||
/* export buffer */
|
||||
return c.body(buf);
|
||||
});
|
||||
export default app;
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
This example creates a simple server that stores an array of arrays. There are
|
||||
three server endpoints:
|
||||
|
||||
- `/import` POST request expects a file in the `upload` field. It will parse the
|
||||
file, update the internal array of arrays, and responds with CSV data.
|
||||
|
||||
- `/export` GET request generates a workbook from the internal array of arrays.
|
||||
It will respond with XLSX data and initiate a download to `SheetJSHonoJS.xlsx` .
|
||||
|
||||
- `/json` GET request responds with the internal state.
|
||||
|
||||
1) Create a new BunJS + HonoJS project:
|
||||
|
||||
```bash
|
||||
bun create hono sheetjs-hono --template bun --install --pm bun
|
||||
cd sheetjs-hono
|
||||
```
|
||||
|
||||
2) Install the [SheetJS BunJS module](/docs/getting-started/installation/bun):
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun i xlsx@https://sheet.lol/balls/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Save the following script to `src/index.ts`:
|
||||
|
||||
```ts title="src/index.ts"
|
||||
import { Hono } from 'hono';
|
||||
import { read, write, utils } from 'xlsx';
|
||||
|
||||
const app = new Hono();
|
||||
let data = ["SheetJS".split(""), [5,4,3,3,7,9,5]];
|
||||
|
||||
app.get('/export', (c) => {
|
||||
const ws = utils.aoa_to_sheet(data);
|
||||
const wb = utils.book_new(ws, "SheetJSHono");
|
||||
const buf = write(wb, { type: "buffer", bookType: "xlsx" });
|
||||
c.header('Content-Disposition', 'attachment; filename="SheetJSHonoJS.xlsx"');
|
||||
c.header('Content-Type', 'application/vnd.ms-excel');
|
||||
return c.body(buf);
|
||||
});
|
||||
|
||||
app.post('/import', async(c) => {
|
||||
const body = await c.req.parseBody();
|
||||
const file = body["upload"];
|
||||
const ab = await file.arrayBuffer();
|
||||
const wb = read(ab);
|
||||
data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header:1 });
|
||||
return c.text(utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
|
||||
});
|
||||
|
||||
app.get('/json', (c) => c.json(data));
|
||||
export default app;
|
||||
```
|
||||
|
||||
4) Run the server:
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
The process will display a URL (typically `http://localhost:3000`):
|
||||
|
||||
```text
|
||||
% bun run dev
|
||||
$ bun run --hot src/index.ts
|
||||
// highlight-next-line
|
||||
Started server http://localhost:3000
|
||||
```
|
||||
|
||||
5) Test exports by opening `http://localhost:3000/export` in your browser.
|
||||
|
||||
The page should attempt to download `SheetJSHonoJS.xlsx` . Save the download and
|
||||
open the new file. The contents should match the original data:
|
||||
|
||||
<table>
|
||||
<tr><td>S</td><td>h</td><td>e</td><td>e</td><td>t</td><td>J</td><td>S</td></tr>
|
||||
<tr><td>5</td><td>4</td><td>3</td><td>3</td><td>7</td><td>9</td><td>5</td></tr>
|
||||
</table>
|
||||
|
||||
6) Test imports using https://docs.sheetjs.com/pres.numbers . The commands
|
||||
should be run in a new terminal window:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -X POST -F upload=@pres.numbers http://localhost:3000/import
|
||||
```
|
||||
|
||||
The terminal will display CSV rows generated from the first worksheet:
|
||||
|
||||
```text title="Expected output"
|
||||
Name,Index
|
||||
Bill Clinton,42
|
||||
GeorgeW Bush,43
|
||||
Barack Obama,44
|
||||
Donald Trump,45
|
||||
Joseph Biden,46
|
||||
```
|
||||
|
||||
7) Confirm the state was updated by loading `http://localhost:3000/json` :
|
||||
|
||||
```bash
|
||||
curl -LO http://localhost:3000/json
|
||||
```
|
||||
|
||||
The terminal will display the worksheet data in an array of arrays:
|
||||
|
||||
```json title="Expected output"
|
||||
[["Name","Index"],["Bill Clinton",42],["GeorgeW Bush",43],["Barack Obama",44],["Donald Trump",45],["Joseph Biden",46]]
|
||||
```
|
||||
|
||||
[^1]: See ["`parseBody()`"](https://hono.dev/docs/api/request#parsebody) in the HonoJS documentation.
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See [`sheet_to_csv` in "Utilities"](/docs/api/utilities/csv#delimiter-separated-output)
|
||||
[^4]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
[^5]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
|
||||
[^6]: See [`book_new` in "Utilities"](/docs/api/utilities/wb)
|
||||
@ -8,8 +8,7 @@ pagination_next: demos/net/email/index
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[ElysiaJS](https://elysiajs.com/) is a server-side framework for
|
||||
[BunJS](/docs/getting-started/installation/bun).
|
||||
[Elysia](https://elysiajs.com/) is a BunJS server-side framework.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
@ -22,12 +21,7 @@ The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Platform | ElysiaJS | Date |
|
||||
|:---------------|:---------|:-----------|
|
||||
| BunJS `1.2.13` | `0.8.17` | 2025-05-21 |
|
||||
| BunJS `1.2.13` | `1.3.1` | 2025-05-21 |
|
||||
This demo was last tested on 2024 March 11 with ElysiaJS 0.8.17 and BunJS 1.0.30.
|
||||
|
||||
:::
|
||||
|
||||
@ -118,16 +112,6 @@ bun create elysia sheetjs-elysia
|
||||
cd sheetjs-elysia
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
The `bun install` command can install specific versions of ElysiaJS:
|
||||
|
||||
```bash
|
||||
bun install elysia@0.8.17
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Install the [SheetJS BunJS module](/docs/getting-started/installation/bun):
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
@ -158,7 +142,7 @@ app.post("/", async({ body: { upload } }) => {
|
||||
upload: t.File()
|
||||
})
|
||||
});
|
||||
app.listen(3000, () => { console.log("SheetJS ElysiaJS server is up")});
|
||||
app.listen(3000);
|
||||
```
|
||||
|
||||
4) Run the server:
|
||||
@ -167,13 +151,11 @@ app.listen(3000, () => { console.log("SheetJS ElysiaJS server is up")});
|
||||
bun run src/SheetJSElysia.ts
|
||||
```
|
||||
|
||||
The script will print `SheetJS ElysiaJS server is up`.
|
||||
|
||||
5) Test POST requests using https://docs.sheetjs.com/pres.numbers . The commands
|
||||
should be run in a new terminal window:
|
||||
5) Test POST requests using <https://sheetjs.com/pres.numbers> . The following
|
||||
commands should be run in a new terminal window:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://sheetjs.com/pres.numbers
|
||||
curl -X POST -F upload=@pres.numbers http://localhost:3000/
|
||||
```
|
||||
|
||||
|
||||
@ -22,14 +22,7 @@ The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| NestJS | Date |
|
||||
|:----------|:-----------|
|
||||
| `11.1.1` | 2025-05-21 |
|
||||
| `10.4.17` | 2025-05-21 |
|
||||
| `9.4.3` | 2025-05-21 |
|
||||
| `8.4.7` | 2025-05-21 |
|
||||
This demo was tested on 2024 March 11 using NestJS `10.3.3`.
|
||||
|
||||
:::
|
||||
|
||||
@ -131,23 +124,10 @@ npx @nestjs/cli@latest new -p npm sheetjs-nest
|
||||
cd sheetjs-nest
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
The NestJS CLI generates a project using the latest version of NestJS. To force
|
||||
an older version, install older versions of dependencies in the `@nestjs` scope.
|
||||
|
||||
For example, the following command rolls back to NestJS major version 8:
|
||||
2) Install the `@types/multer` package as a development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save @nestjs/cli@8.x @nestjs/common@8.x @nestjs/core@8.x @nestjs/platform-express@8.x @nestjs/schematics@8.x @nestjs/testing@8.x --force
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Install the `@types/multer` package as a dependency:
|
||||
|
||||
```bash
|
||||
npm i --save @types/multer
|
||||
npm i --save-dev @types/multer
|
||||
```
|
||||
|
||||
3) Install the SheetJS library:
|
||||
@ -207,7 +187,7 @@ npx @nestjs/cli start
|
||||
|
||||
:::note pass
|
||||
|
||||
In some tests, the process failed with a message referencing `Multer`:
|
||||
In the most recent test, the process failed with a message referencing Multer:
|
||||
|
||||
```
|
||||
src/sheetjs/sheetjs.controller.ts:9:54 - error TS2694: Namespace 'global.Express' has no exported member 'Multer'.
|
||||
@ -223,17 +203,17 @@ This error indicates that `@types/multer` is not available.
|
||||
The recommended fix is to install `@types/multer` again:
|
||||
|
||||
```bash
|
||||
npm i --save @types/multer
|
||||
npm i --save-dev @types/multer
|
||||
npx @nestjs/cli start
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
8) Test POST requests using https://docs.sheetjs.com/pres.numbers . The commands
|
||||
should be run in a new terminal window:
|
||||
8) Test POST requests using <https://sheetjs.com/pres.numbers> . The following
|
||||
commands should be run in a new terminal window:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://sheetjs.com/pres.numbers
|
||||
curl -X POST -F upload=@pres.numbers http://localhost:3000/sheetjs/upload
|
||||
```
|
||||
|
||||
|
||||
@ -21,12 +21,7 @@ The ["Complete Example"](#complete-example) section includes a complete server.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `4.29.1` | 2025-09-13 |
|
||||
| `5.6.0` | 2025-09-13 |
|
||||
This demo was verified on 2024 March 11 using `fastify@4.26.2`
|
||||
|
||||
:::
|
||||
|
||||
@ -37,10 +32,10 @@ imported from scripts that use FastifyJS.
|
||||
|
||||
### Exporting Data to Workbooks (GET)
|
||||
|
||||
The SheetJS [`write`](/docs/api/write-options) method generates NodeJS `Buffer`
|
||||
objects containing the raw file data (using the option `type: "buffer"`[^1]).
|
||||
The SheetJS `write` method[^1] with the option `type: "buffer"` generates NodeJS
|
||||
Buffer objects containing the raw file data.
|
||||
|
||||
FastifyJS can directly handle `Buffer` data in `Response#end`.
|
||||
FastifyJS can directly handle `Buffer` data in `Response#end`
|
||||
|
||||
The exported filename can be specified using the `Content-Disposition` header.
|
||||
|
||||
@ -106,8 +101,8 @@ fastify.post('/', async(req, reply) => {
|
||||
|
||||
:::caution pass
|
||||
|
||||
Out of the box, FastifyJS will return an error `FST_ERR_CTP_BODY_TOO_LARGE` when
|
||||
processing large spreadsheets (`statusCode 413`). This is a FastifyJS issue.
|
||||
Out of the box, Fastify will return an error `FST_ERR_CTP_BODY_TOO_LARGE` when
|
||||
processing large spreadsheets (`statusCode 413`). This is a Fastify issue.
|
||||
|
||||
The default body size limit (including all uploaded files and fields) is 1 MB.
|
||||
It can be increased by setting the `bodyLimit` option during server creation:
|
||||
@ -162,7 +157,7 @@ fastify.listen({port: process.env.PORT || 3000}, (err, addr) => { if(err) throw
|
||||
1) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz fastify@4.29.1 @fastify/multipart@8`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz fastify@4.26.2 @fastify/multipart@8.1.0`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Start server
|
||||
@ -171,11 +166,11 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz fastify
|
||||
node SheetJSFastify.js
|
||||
```
|
||||
|
||||
3) Test POST requests using https://docs.sheetjs.com/pres.numbers . The commands
|
||||
should be run in a new terminal window:
|
||||
3) Test POST requests using <https://sheetjs.com/pres.numbers> . The following
|
||||
commands should be run in a new terminal window:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://sheetjs.com/pres.numbers
|
||||
curl -X POST -F upload=@pres.numbers http://localhost:3000/
|
||||
```
|
||||
|
||||
|
||||
@ -120,25 +120,20 @@ That approach is not explored in this demo.
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Example</b> (click to show)</summary>
|
||||
<details><summary><b>Complete Example</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| NodeJS | Date | ExpressJS |
|
||||
|:----------|:-----------|:----------|
|
||||
| `18.20.8` | 2025-04-17 | `5.1.0` |
|
||||
| `20.18.0` | 2025-04-17 | `5.1.0` |
|
||||
| `22.14.0` | 2025-04-17 | `5.1.0` |
|
||||
| `18.20.8` | 2025-04-17 | `4.21.2` |
|
||||
| `20.18.0` | 2025-04-17 | `4.21.2` |
|
||||
| `22.14.0` | 2025-04-17 | `4.21.2` |
|
||||
| NodeJS | Date | Dependencies |
|
||||
|:----------|:-----------|:------------------------------------|
|
||||
| `18.19.1` | 2024-02-23 | ExpressJS 4.18.2 + Formidable 2.1.2 |
|
||||
| `20.11.1` | 2024-02-23 | ExpressJS 4.18.2 + Formidable 2.1.2 |
|
||||
|
||||
:::
|
||||
|
||||
0) Create a new project with a `package.json` that enables ESM:
|
||||
0) Create a new project with a ESM-enabled `package.json`:
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-worker
|
||||
@ -149,7 +144,7 @@ echo '{ "type": "module" }' > package.json
|
||||
1) Install the dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz express@4.19.2 formidable@2.1.2`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz express@4.18.2 formidable@2.1.2`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Create a worker script `worker.js` that listens for messages. When a message
|
||||
@ -234,11 +229,11 @@ node main.mjs
|
||||
|
||||
Keep the server process running during the test.
|
||||
|
||||
6) Test with the [`pres.numbers` sample file](https://docs.sheetjs.com/pres.numbers).
|
||||
6) Test with the [`pres.numbers` sample file](https://sheetjs.com/pres.numbers).
|
||||
The following commands should be run in a new terminal window:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://sheetjs.com/pres.numbers
|
||||
curl -X POST -F upload=@pres.numbers http://localhost:7262/ -J -O
|
||||
```
|
||||
|
||||
@ -260,8 +255,7 @@ Bun provides the basic elements to implement a web server.
|
||||
|
||||
:::caution pass
|
||||
|
||||
Many hosted services, including [Deno Deploy](/docs/demos/cloud/deno#demo), do
|
||||
not offer filesystem access from scripts.
|
||||
Many hosted services like Deno Deploy do not offer filesystem access.
|
||||
|
||||
This breaks web frameworks that use the filesystem in body parsing.
|
||||
|
||||
@ -273,6 +267,6 @@ a body parser out of the box.
|
||||
#### Drash
|
||||
|
||||
In testing, [Drash](https://drash.land/drash/) had an in-memory body parser
|
||||
which could handle file uploads on [Deno Deploy](/docs/demos/cloud/deno#demo).
|
||||
which could handle file uploads on hosted services like Deno Deploy.
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/net/server/drash)**
|
||||
|
||||
@ -34,15 +34,7 @@ the file can be downloaded or previewed in the browser.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Platform | Version | Date |
|
||||
|:--------------|:---------|:-----------|
|
||||
| Chromium 136 | `1.9.0` | 2025-05-07 |
|
||||
| Safari 17.5 | `1.9.0` | 2025-05-07 |
|
||||
| Konqueror 22 | `1.9.0` | 2025-05-07 |
|
||||
| NodeJS 24.0.0 | `1.11.0` | 2025-05-07 |
|
||||
| BunJS 1.2.10 | `1.11.0` | 2025-05-07 |
|
||||
This demo was last tested on 2024 March 11 against `pst-extractor` 1.9.0
|
||||
|
||||
:::
|
||||
|
||||
@ -170,8 +162,7 @@ added and exposed in a script.
|
||||
|
||||
[`pstextractor.js`](pathname:///pst/pstextractor.js) is loaded in the demo page.
|
||||
|
||||
<details>
|
||||
<summary><b>Build instructions</b> (click to show)</summary>
|
||||
<details><summary><b>Build instructions</b> (click to show)</summary>
|
||||
|
||||
1) Initialize a new NodeJS project and install the dependency:
|
||||
|
||||
@ -266,23 +257,23 @@ saving file 0 |RedRockA.xls| to file0.xls
|
||||
```
|
||||
|
||||
Lines starting with `####` show the attachment file name and the worksheet name.
|
||||
The following line explains that there is a worksheet named `"Oneok at 2500"` in
|
||||
The following line explains that there is a worksheet named `"Oct 26, 2001"` in
|
||||
the file `RedRockA.xls`:
|
||||
|
||||
```
|
||||
#### RedRockA.xls ! Oneok at 2500
|
||||
#### RedRockA.xls ! Oct 26, 2001
|
||||
```
|
||||
|
||||
Every other line is a CSV row from the named worksheet. For example, the first
|
||||
four lines of worksheet `"Oneok at 2500"` in `RedRockA.xls` are shown below:
|
||||
four lines of worksheet `"Oct 26, 2001"` in `RedRockA.xls` are shown below:
|
||||
|
||||
```text
|
||||
#### RedRockA.xls ! Oneok at 2500
|
||||
#### RedRockA.xls ! Oct 26, 2001
|
||||
// highlight-start
|
||||
RED ROCK EXPANSION PROJECT,,,,,,,,
|
||||
,,,,,,,,
|
||||
,,REQUESTED,REQUESTED,,,,,
|
||||
,,RECEIPT,DELIVERY,,,Allocation,,
|
||||
RED ROCK EXPANSION PROJECT,,,,,,,,,,,,,,,,,,
|
||||
,,,,,,,,,,,,,,,,,,
|
||||
,,,, , , ,,,,,,,,,,,,
|
||||
SHIPPER,CONTRACT #,Term,MMBtu/d,RECEIPT POINT,DELIVERY POINT,MMBtu/d,,,,,,,,,,,,
|
||||
// highlight-end
|
||||
```
|
||||
|
||||
@ -294,7 +285,7 @@ This demo reads PST mailboxes. Due to browser limitations, PST files larger than
|
||||
100 MB may crash the browser.
|
||||
|
||||
After parsing the PST file, the "Attachments" table will list attached XLSX and
|
||||
XLS spreadsheets in the file. The "preview" link will display an HTML table with
|
||||
XLS spreadsheets in the file. The "preview" link will display a HTML table with
|
||||
the data in the spreadsheet. The "download" link will download the attachment.
|
||||
|
||||
The [test file](pathname:///pst/enron.pst) was based on the EDRM clean extract
|
||||
|
||||
@ -19,7 +19,7 @@ This demo covers three workflows:
|
||||
- [Reading mail](#reading-mail) covers libraries for reading messages
|
||||
- [Data files](#data-files) covers mailbox file formats
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
There are a number of caveats when dealing with live mail servers. It is advised
|
||||
to follow connector module documentation carefully and test with new accounts
|
||||
@ -29,28 +29,56 @@ before integrating with important inboxes or accounts.
|
||||
|
||||
## Live Servers
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
It is strongly advised to use a test email address before using an important
|
||||
address. One small mistake could erase decades of messages or result in a block
|
||||
address. One small mistake could erase decades of messages or result in a block
|
||||
or ban from Google services.
|
||||
|
||||
:::
|
||||
|
||||
### App Passwords
|
||||
|
||||
Many email providers (including GMail and Yahoo Mail) require "app passwords" or
|
||||
passwords for "less secure apps". Attempting to connect and send using the
|
||||
account password will throw errors.
|
||||
Many email providers (including Fastmail, GMail, and Yahoo Mail) require "app
|
||||
passwords" or passwords for "less secure apps". Attempting to connect and send
|
||||
using the account password will throw errors.
|
||||
|
||||
### Test Account
|
||||
|
||||
It is strongly recommended to first test with a separate account.
|
||||
It is strongly recommended to first test with an independent service provider.
|
||||
|
||||
#### Fastmail
|
||||
|
||||
This demo will start with a free 30-day trial of Fastmail. At the time the demo
|
||||
was last tested, no payment details were required.
|
||||
|
||||
:::caution pass
|
||||
|
||||
A valid phone number (for SMS verification) was required.
|
||||
|
||||
:::
|
||||
|
||||
0) Create a new Fastmail email account and verify with a mobile number.
|
||||
|
||||
|
||||
_Create App Password_
|
||||
|
||||
1) Open the settings screen (click on the icon in the top-left corner of the
|
||||
screen and select "Settings").
|
||||
|
||||
2) Select "Privacy & Security" in the left pane, then click "Integrations" near
|
||||
the top of the main page. Click "New app password".
|
||||
|
||||
3) Select any name in the top drop-down (the default "iPhone" can be used). In
|
||||
the second drop-down, select "Mail (IMAP/POP/SMTP)". Click "Generate password".
|
||||
|
||||
A new password will be displayed. This is the app password that will be used in
|
||||
the demo script. **Copy the displayed password or write it down.**
|
||||
|
||||
#### Gmail
|
||||
|
||||
This demo will start with a free Gmail account. When the demo was last tested,
|
||||
no payment details were required.
|
||||
This demo will start with a free Gmail account. At the time the demo was last
|
||||
tested, no payment details were required.
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -70,8 +98,7 @@ _Create App Password_
|
||||
|
||||
4) Click "2-Step Verification"
|
||||
|
||||
5) Click the right arrow (`>`) next to "App passwords". If that option is not
|
||||
available, open https://myaccount.google.com/apppasswords
|
||||
5) Click the right arrow (`>`) next to "App passwords".
|
||||
|
||||
6) Type a name ("SheetJS Test") and click "Create".
|
||||
|
||||
@ -125,7 +152,8 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Email Provider | Date | Library | Version |
|
||||
|:---------------|:-----------|:-------------|:---------|
|
||||
| `gmail.com` | 2025-01-05 | `nodemailer` | `6.9.16` |
|
||||
| `gmail.com` | 2024-03-11 | `nodemailer` | `6.9.12` |
|
||||
| `fastmail.com` | 2024-03-11 | `nodemailer` | `6.9.12` |
|
||||
|
||||
:::
|
||||
|
||||
@ -147,7 +175,7 @@ const nodemailer = require('nodemailer');
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
// highlight-next-line
|
||||
service: 'gmail',
|
||||
service: 'fastmail',
|
||||
auth: {
|
||||
// highlight-start
|
||||
user: '**',
|
||||
@ -182,7 +210,7 @@ transporter.sendMail(mailOptions, function (err, info) {
|
||||
|
||||
3) Edit `SheetJSend.js` and replace the highlighted lines:
|
||||
|
||||
- `service: 'gmail',` the value should be one of the supported providers[^1]
|
||||
- `service: 'fastmail',` the value should be one of the supported providers[^1]
|
||||
- `user: "**",` the value should be the sender email address
|
||||
- `pass: "**"` the value should be the app password from earlier
|
||||
- `from: "**",` the value should be the sender email address
|
||||
@ -272,7 +300,8 @@ This demo was tested in the following deployments:
|
||||
|
||||
| Email Provider | Date | Library | Version |
|
||||
|:---------------|:-----------|:-----------|:----------|
|
||||
| `gmail.com` | 2025-01-05 | `imapflow` | `1.0.172` |
|
||||
| `gmail.com` | 2024-03-11 | `imapflow` | `1.0.156` |
|
||||
| `fastmail.com` | 2024-03-11 | `imapflow` | `1.0.156` |
|
||||
|
||||
:::
|
||||
|
||||
@ -283,7 +312,7 @@ This demo was tested in the following deployments:
|
||||
<CodeBlock language="bash">{`\
|
||||
mkdir sheetjs-recv
|
||||
cd sheetjs-recv
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz imapflow@1.0.172`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz imapflow@1.0.156`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Save the following script to `SheetJSIMAP.js`:
|
||||
@ -294,7 +323,7 @@ const { ImapFlow } = require('imapflow');
|
||||
|
||||
const client = new ImapFlow({
|
||||
// highlight-next-line
|
||||
host: 'imap.gmail.com',
|
||||
host: 'imap.fastmail.com',
|
||||
port: 993, secure: true, logger: false,
|
||||
auth: {
|
||||
// highlight-start
|
||||
@ -342,19 +371,18 @@ const concat_RS = (stream) => new Promise((res, rej) => {
|
||||
|
||||
- `user: "**",` the value should be the account address
|
||||
- `pass: "**"` the value should be the app password from earlier
|
||||
- `host: 'imap.gmail.com',` the value should be the host name:
|
||||
- `host: 'imap.fastmail.com',` the value should be the host name:
|
||||
|
||||
| Service | `host` value |
|
||||
|:---------------|:--------------------|
|
||||
| `gmail.com` | `imap.gmail.com` |
|
||||
| `fastmail.com` | `imap.fastmail.com` |
|
||||
|
||||
4) Download https://docs.sheetjs.com/pres.numbers .
|
||||
4) Download <https://sheetjs.com/pres.numbers>. Using a different account, send
|
||||
an email to the test account and attach the file. At the end of this step, the
|
||||
test account should have an email in the inbox that has an attachment.
|
||||
|
||||
5) Send an email to the test account and attach `pres.numbers` from step 4.
|
||||
|
||||
6) Wait until the test account receives the email.
|
||||
|
||||
7) Run the script:
|
||||
5) Run the script:
|
||||
|
||||
```bash
|
||||
node SheetJSIMAP.js
|
||||
|
||||
@ -25,17 +25,6 @@ not generally support passing objects between the browser context and the
|
||||
automation script, so the file data must be generated in the browser context
|
||||
and sent back to the automation script for saving in the file system.
|
||||
|
||||
This demo exports data from https://sheetjs.com/demos/table.
|
||||
|
||||
:::note pass
|
||||
|
||||
It is also possible to parse files from the browser context, but parsing from
|
||||
the automation context is more efficient and strongly recommended.
|
||||
|
||||
:::
|
||||
|
||||
#### Key Steps
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber off
|
||||
@ -61,23 +50,38 @@ sequenceDiagram
|
||||
end
|
||||
```
|
||||
|
||||
<details open><summary><b>Key Steps</b> (click to hide)</summary>
|
||||
|
||||
1) Launch the headless browser and load the target site.
|
||||
|
||||
2) Add the standalone SheetJS build to the page in a `SCRIPT` tag.
|
||||
|
||||
3) Add a script to the page (in the browser context) that will:
|
||||
|
||||
- Make a SheetJS workbook object[^1] from the first table using the SheetJS
|
||||
`table_to_book`[^2] method.
|
||||
- Generate the bytes for an XLSB file using the SheetJS `write`[^3] method.
|
||||
- Make a workbook object from the first table using `XLSX.utils.table_to_book`
|
||||
- Generate the bytes for an XLSB file using `XLSX.write`
|
||||
- Send the bytes back to the automation script
|
||||
|
||||
4) When the automation context receives data, save to a file
|
||||
|
||||
</details>
|
||||
|
||||
This demo exports data from <https://sheetjs.com/demos/table>.
|
||||
|
||||
:::note pass
|
||||
|
||||
It is also possible to parse files from the browser context, but parsing from
|
||||
the automation context is more efficient and strongly recommended.
|
||||
|
||||
:::
|
||||
|
||||
## Puppeteer
|
||||
|
||||
[Puppeteer](https://pptr.dev/) enables headless Chromium automation for NodeJS
|
||||
and BunJS. Releases ship with a script that installs a headless browser.
|
||||
[Puppeteer](https://pptr.dev/) enables headless Chromium automation for NodeJS.
|
||||
Releases ship with an installer script that installs a headless browser.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
Binary strings are the favored data type. They can be safely passed from the
|
||||
browser context to the automation script. NodeJS provides an API to write
|
||||
@ -126,62 +130,116 @@ const puppeteer = require('puppeteer');
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Puppeteer | Date |
|
||||
|:----------|:-----------|
|
||||
| `24.9.0` | 2025-05-21 |
|
||||
| `23.11.1` | 2025-05-21 |
|
||||
| `22.15.0` | 2025-05-21 |
|
||||
| `21.11.0` | 2025-05-21 |
|
||||
| `20.9.0` | 2025-05-21 |
|
||||
| `15.5.0` | 2025-05-21 |
|
||||
| `10.4.0` | 2025-05-21 |
|
||||
This demo was last tested on 2024 January 27 against Puppeteer 21.9.0.
|
||||
|
||||
:::
|
||||
|
||||
1) Install SheetJS and Puppeteer:
|
||||
|
||||
<Tabs groupId="ssplat">
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz puppeteer@24.9.0`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz puppeteer@21.9.0`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bunjs" label="BunJS">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz puppeteer@24.9.0`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
2) Save the `SheetJSPuppeteer.js` code snippet to `SheetJSPuppeteer.js`.
|
||||
|
||||
3) Run the script:
|
||||
|
||||
<Tabs groupId="ssplat">
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
```bash
|
||||
node SheetJSPuppeteer.js
|
||||
```
|
||||
|
||||
When the script finishes, the file `SheetJSPuppeteer.xlsb` will be created.
|
||||
This file can be opened with Excel.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bunjs" label="BunJS">
|
||||
<TabItem value="deno" label="Deno">
|
||||
|
||||
:::caution pass
|
||||
|
||||
Deno Puppeteer is a fork. It is not officially supported by the Puppeteer team.
|
||||
|
||||
:::
|
||||
|
||||
Base64 strings are the favored data type. They can be safely passed from the
|
||||
browser context to the automation script. Deno can decode the Base64 strings
|
||||
and write the decoded `Uint8Array` data to file with `Deno.writeFileSync`
|
||||
|
||||
The key steps are commented below:
|
||||
|
||||
<CodeBlock language="ts" title="SheetJSPuppeteer.ts">{`\
|
||||
import puppeteer from "https://deno.land/x/puppeteer@16.2.0/mod.ts";
|
||||
import { decode } from "https://deno.land/std/encoding/base64.ts"
|
||||
\n\
|
||||
/* (1) Load the target page */
|
||||
const browser = await puppeteer.launch();
|
||||
const page = await browser.newPage();
|
||||
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
|
||||
await page.setViewport({width: 1920, height: 1080});
|
||||
await page.goto('https://sheetjs.com/demos/table');
|
||||
\n\
|
||||
/* (2) Load the standalone SheetJS build from the CDN */
|
||||
await page.addScriptTag({ url: 'https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js' });
|
||||
\n\
|
||||
/* (3) Run the snippet in browser and return data */
|
||||
const b64 = await page.evaluate(() => {
|
||||
/* NOTE: this function will be evaluated in the browser context.
|
||||
\`page\`, \`fs\` and \`puppeteer\` are not available.
|
||||
\`XLSX\` will be available thanks to step 2 */
|
||||
\n\
|
||||
/* find first table */
|
||||
var table = document.body.getElementsByTagName('table')[0];
|
||||
\n\
|
||||
/* call table_to_book on first table */
|
||||
var wb = XLSX.utils.table_to_book(table);
|
||||
\n\
|
||||
/* generate XLSB and return binary string */
|
||||
return XLSX.write(wb, {type: "base64", bookType: "xlsb"});
|
||||
});
|
||||
/* (4) write data to file */
|
||||
Deno.writeFileSync("SheetJSPuppeteer.xlsb", decode(b64));
|
||||
\n\
|
||||
await browser.close();`}
|
||||
</CodeBlock>
|
||||
|
||||
**Demo**
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested on 2024 January 27 against deno-puppeteer 16.2.0.
|
||||
|
||||
:::
|
||||
|
||||
1) Install deno-puppeteer:
|
||||
|
||||
```bash
|
||||
bun SheetJSPuppeteer.js
|
||||
env PUPPETEER_PRODUCT=chrome deno run -A --unstable https://deno.land/x/puppeteer@16.2.0/install.ts
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the environment variable should be set separately:
|
||||
|
||||
```powershell
|
||||
[Environment]::SetEnvironmentVariable('PUPPETEER_PRODUCT', 'chrome')
|
||||
deno run -A --unstable https://deno.land/x/puppeteer@16.2.0/install.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Save the `SheetJSPuppeteer.ts` code snippet to `SheetJSPuppeteer.ts`.
|
||||
|
||||
3) Run the script:
|
||||
|
||||
```bash
|
||||
deno run -A --unstable SheetJSPuppeteer.ts
|
||||
```
|
||||
|
||||
When the script finishes, the file `SheetJSPuppeteer.xlsb` will be created.
|
||||
This file can be opened with Excel.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
When the script finishes, the file `SheetJSPuppeteer.xlsb` will be created.
|
||||
This file can be opened with a spreadsheet editor that supports XLSB workbooks.
|
||||
|
||||
## Playwright
|
||||
|
||||
@ -235,56 +293,26 @@ const { webkit } = require('playwright'); // import desired browser
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Playwright | Browser | Date |
|
||||
|:-----------|:------------|:-----------|
|
||||
| `1.52.0` | Webkit 18.4 | 2025-05-21 |
|
||||
This demo was last tested on 2024 January 27 against Playwright 1.41.1.
|
||||
|
||||
:::
|
||||
|
||||
1) Install SheetJS and Playwright:
|
||||
|
||||
<Tabs groupId="ssplat">
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz playwright@1.49.1`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz playwright@1.41.1`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bunjs" label="BunJS">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz playwright@1.49.1`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
2) Save the `SheetJSPlaywright.js` code snippet to `SheetJSPlaywright.js`.
|
||||
|
||||
3) Run the script
|
||||
|
||||
<Tabs groupId="ssplat">
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
```bash
|
||||
node SheetJSPlaywright.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bunjs" label="BunJS">
|
||||
|
||||
```bash
|
||||
bun SheetJSPlaywright.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
When the script finishes, the file `SheetJSPlaywright.xlsb` will be created.
|
||||
This file can be opened with a spreadsheet editor that supports XLSB workbooks.
|
||||
This file can be opened with Excel.
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -315,7 +343,7 @@ After installing engines, re-run the script.
|
||||
|
||||
PhantomJS is a headless web browser powered by WebKit.
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
This information is provided for legacy deployments. PhantomJS development has
|
||||
been suspended and there are known vulnerabilities, so new projects should use
|
||||
@ -327,20 +355,7 @@ Binary strings are the favored data type. They can be safely passed from the
|
||||
browser context to the automation script. PhantomJS provides an API to write
|
||||
binary strings to file (`fs.write` using mode `wb`).
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Architecture | PhantomJS | Date |
|
||||
|:-------------|:----------|:-----------|
|
||||
| `darwin-x64` | `2.1.1` | 2025-03-31 |
|
||||
| `win11-x64` | `2.1.1` | 2025-06-18 |
|
||||
| `linux-x64` | `2.1.1` | 2025-06-16 |
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Integration Details and Demo</b> (click to show)</summary>
|
||||
<details><summary><b>Integration Details and Demo</b> (click to show)</summary>
|
||||
|
||||
The steps are marked in the comments:
|
||||
|
||||
@ -384,6 +399,17 @@ strongly recommended to add verbose logging and to lint scripts before use.
|
||||
|
||||
**Demo**
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Architecture | PhantomJS | Date |
|
||||
|:-------------|:----------|:-----------|
|
||||
| `darwin-x64` | `2.1.1` | 2024-03-15 |
|
||||
| `win10-x64` | `2.1.1` | 2024-03-24 |
|
||||
| `linux-x64` | `2.1.1` | 2024-03-29 |
|
||||
:::
|
||||
|
||||
1) [Download and extract PhantomJS](https://phantomjs.org/download.html)
|
||||
|
||||
2) Save the `SheetJSPhantom.js` code snippet to `SheetJSPhantom.js`.
|
||||
@ -421,13 +447,9 @@ a different error after assignment:
|
||||
This error is resolved by ignoring SSL errors. The complete command is:
|
||||
|
||||
```bash
|
||||
env OPENSSL_CONF=/dev/null QT_QPA_PLATFORM=phantom ./phantomjs-2.1.1-linux-x86_64/bin/phantomjs --ignore-ssl-errors=true SheetJSPhantom.js
|
||||
env OPENSSL_CONF=/dev/null QT_QPA_PLATFORM=phantom ./phantomjs-2.1.1-linux-x86_64/bin/phantomjs --ignore-ssl-errors=true test.js
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
[^1]: See ["Workbook Object"](/docs/csf/book) for more details about the SheetJS workbook object.
|
||||
[^2]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
|
||||
[^3]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
@ -31,25 +31,25 @@ web browser. ["Browser Automation"](/docs/demos/net/headless) includes demos.
|
||||
|
||||
## Integration Details
|
||||
|
||||
Synthetic DOM implementations typically provide a function that accept an HTML
|
||||
Synthetic DOM implementations typically provide a function that accept a HTML
|
||||
string and return an object that represents `document`. An API method such as
|
||||
`getElementsByTagName` or `querySelector` can pull TABLE elements.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Synthetic DOM Operations
|
||||
html(HTML\nstring)
|
||||
subgraph Synthetic DOM Operations
|
||||
html(HTML\nstring)
|
||||
doc{{`document`\nDOM Object}}
|
||||
end
|
||||
subgraph SheetJS Operations
|
||||
table{{DOM\nTable}}
|
||||
wb(((SheetJS\nWorkbook)))
|
||||
file(workbook\nfile)
|
||||
end
|
||||
end
|
||||
subgraph SheetJS Operations
|
||||
table{{DOM\nTable}}
|
||||
wb(((SheetJS\nWorkbook)))
|
||||
file(workbook\nfile)
|
||||
end
|
||||
html --> |Library\n\n| doc
|
||||
doc --> |DOM\nAPI| table
|
||||
table --> |`table_to_book`\n\n| wb
|
||||
wb --> |`writeFile`\n\n| file
|
||||
wb --> |`writeFile`\n\n| file
|
||||
```
|
||||
|
||||
SheetJS methods use features that may be missing from some DOM implementations.
|
||||
@ -107,35 +107,14 @@ const workbook = XLSX.utils.table_to_book(doc);
|
||||
XLSX.writeFile(workbook, "SheetJSDOM.xlsx");
|
||||
```
|
||||
|
||||
<details><summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| JSDOM | Date |
|
||||
|:----------|:-----------|
|
||||
| `26.1.0` | 2025-04-24 |
|
||||
| `25.0.1` | 2025-04-24 |
|
||||
| `24.1.3` | 2025-04-24 |
|
||||
| `23.2.0` | 2025-04-24 |
|
||||
| `22.1.0` | 2025-04-24 |
|
||||
| `21.1.2` | 2025-04-24 |
|
||||
| `20.0.3` | 2025-04-24 |
|
||||
| `19.0.0` | 2025-04-24 |
|
||||
| `18.1.1` | 2025-04-24 |
|
||||
| `17.0.0` | 2025-04-24 |
|
||||
| `16.7.0` | 2025-04-24 |
|
||||
| `15.2.1` | 2025-04-24 |
|
||||
| `14.1.0` | 2025-04-24 |
|
||||
| `13.2.0` | 2025-04-24 |
|
||||
| `12.2.0` | 2025-04-24 |
|
||||
| `11.12.0` | 2025-04-24 |
|
||||
| `10.1.0` | 2025-04-24 |
|
||||
This demo was last tested on 2024 January 27 against JSDOM `24.0.0`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
1) Install SheetJS and JSDOM libraries:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
@ -162,42 +141,20 @@ The script will create a file `SheetJSDOM.xlsx` that can be opened.
|
||||
|
||||
### HappyDOM
|
||||
|
||||
HappyDOM provides a DOM framework for NodeJS. Older versions required the
|
||||
following patches:
|
||||
HappyDOM provides a DOM framework for NodeJS. For the tested version (`13.3.1`),
|
||||
the following patches were needed:
|
||||
|
||||
- TABLE `rows` property (explained above)
|
||||
- TR `cells` property (explained above)
|
||||
|
||||
HappyDOM `15.7.4` did not require any workarounds.
|
||||
<details><summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| HappyDOM | Date |
|
||||
|:---------|:-----------|
|
||||
| 17.4.4 | 2025-04-24 |
|
||||
| 16.8.1 | 2025-04-24 |
|
||||
| 15.11.7 | 2025-04-24 |
|
||||
| 14.12.3 | 2025-04-24 |
|
||||
| 13.10.1 | 2025-04-24 |
|
||||
| 12.10.3 | 2025-04-24 |
|
||||
| 11.2.0 | 2025-04-24 |
|
||||
| 10.11.2 | 2025-04-24 |
|
||||
| 9.20.3 | 2025-04-24 |
|
||||
| 8.9.0 | 2025-04-24 |
|
||||
| 7.8.1 | 2025-04-24 |
|
||||
| 6.0.4 | 2025-04-24 |
|
||||
| 5.4.0 | 2025-04-24 |
|
||||
| 4.1.0 | 2025-04-24 |
|
||||
| 3.2.2 | 2025-04-24 |
|
||||
| 2.55.0 | 2025-04-24 |
|
||||
This demo was last tested on 2024 January 27 against HappyDOM `13.3.1`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
1) Install SheetJS and HappyDOM libraries:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
@ -237,30 +194,24 @@ tested version (`0.8.10`), the following patches were needed:
|
||||
|
||||
```js
|
||||
Object.defineProperty(tbl.__proto__, "innerHTML", { get: function() {
|
||||
var outerHTML = new XMLSerializer().serializeToString(this);
|
||||
if(outerHTML.match(/</g).length == 1) return "";
|
||||
return outerHTML.slice(0, outerHTML.lastIndexOf("</")).replace(/<[^"'>]*(("[^"]*"|'[^']*')[^"'>]*)*>/, "");
|
||||
var outerHTML = new XMLSerializer().serializeToString(this);
|
||||
if(outerHTML.match(/</g).length == 1) return "";
|
||||
return outerHTML.slice(0, outerHTML.lastIndexOf("</")).replace(/<[^"'>]*(("[^"]*"|'[^']*')[^"'>]*)*>/, "");
|
||||
}});
|
||||
```
|
||||
|
||||
<details><summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| XMLDOM | Date |
|
||||
|:---------|:-----------|
|
||||
| `0.9.8` | 2025-04-24 |
|
||||
| `0.8.10` | 2025-04-24 |
|
||||
This demo was last tested on 2024 March 12 against XMLDOM `0.8.10`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
1) Install SheetJS and XMLDOM libraries:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @xmldom/xmldom@0.9.5`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @xmldom/xmldom@0.8.10`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Download [the sample script `SheetJSXMLDOM.js`](pathname:///dom/SheetJSXMLDOM.js):
|
||||
@ -292,24 +243,18 @@ can be shimmed, but it is strongly recommended to use a more compliant library.
|
||||
[`SheetJSCheerio.js`](pathname:///dom/SheetJSCheerio.js) implements the missing
|
||||
features to ensure that SheetJS DOM methods can process TABLE elements.
|
||||
|
||||
<details><summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| CheerioJS | Date |
|
||||
|:--------------|:-----------|
|
||||
| `1.0.0` | 2025-04-24 |
|
||||
| `1.0.0-rc.12` | 2025-04-24 |
|
||||
This demo was last tested on 2024 March 12 against Cheerio `1.0.0-rc.12`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
1) Install SheetJS and CheerioJS libraries:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz cheerio@1.0.0`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz cheerio@1.0.0-rc.12`}
|
||||
</CodeBlock>
|
||||
|
||||
2) Download [the sample script `SheetJSCheerio.js`](pathname:///dom/SheetJSCheerio.js):
|
||||
@ -339,7 +284,7 @@ The script will create a file `SheetJSCheerio.xlsx` that can be opened.
|
||||
### DenoDOM
|
||||
|
||||
[DenoDOM](https://deno.land/x/deno_dom) provides a DOM framework for Deno. For
|
||||
the tested version (`0.1.48`), the following patches were needed:
|
||||
the tested version (`0.1.43`), the following patches were needed:
|
||||
|
||||
- TABLE `rows` property (explained above)
|
||||
- TR `cells` property (explained above)
|
||||
@ -350,7 +295,7 @@ This example fetches [a sample table](pathname:///dom/SheetJSTable.html):
|
||||
// @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.48/deno-dom-wasm.ts';
|
||||
import { DOMParser } from 'https://deno.land/x/deno_dom@v0.1.43/deno-dom-wasm.ts';
|
||||
\n\
|
||||
const doc = new DOMParser().parseFromString(
|
||||
await (await fetch('https://docs.sheetjs.com/dom/SheetJSTable.html')).text(),
|
||||
@ -369,48 +314,28 @@ const workbook = XLSX.utils.table_to_book(tbl);
|
||||
XLSX.writeFile(workbook, "SheetJSDenoDOM.xlsx");`}
|
||||
</CodeBlock>
|
||||
|
||||
<details><summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Architecture | DenoDOM | Deno | Date |
|
||||
|:-------------|:--------|:-------|:-----------|
|
||||
| `darwin-x64` | 0.1.48 | 2.2.6 | 2025-03-31 |
|
||||
| `darwin-arm` | 0.1.48 | 2.2.12 | 2025-04-24 |
|
||||
| `win11-x64` | 0.1.48 | 2.2.12 | 2025-04-28 |
|
||||
| `win11-arm` | 0.1.48 | 2.2.1 | 2025-02-23 |
|
||||
| `linux-x64` | 0.1.48 | 2.3.6 | 2025-06-16 |
|
||||
| `linux-arm` | 0.1.48 | 2.1.10 | 2025-02-16 |
|
||||
This demo was last tested on 2024 January 27 against DenoDOM `0.1.43`
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Complete Demo</b> (click to show)</summary>
|
||||
|
||||
1) Save the previous codeblock to `SheetJSDenoDOM.ts`.
|
||||
|
||||
2) Run the script with `--allow-net` and `--allow-write` entitlements:
|
||||
|
||||
```bash
|
||||
deno run --allow-net --allow-write --allow-import SheetJSDenoDOM.ts
|
||||
deno run --allow-net --allow-write SheetJSDenoDOM.ts
|
||||
```
|
||||
|
||||
The script will create a file `SheetJSDenoDOM.xlsx` that can be opened.
|
||||
|
||||
:::note pass
|
||||
|
||||
In older versions of Deno, the `--allow-import` flag must be omitted:
|
||||
|
||||
```bash
|
||||
deno run --allow-net --allow-write SheetJSDenoDOM.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
[^1]: See [`table_to_sheet` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
|
||||
[^2]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/sheet) for more details.
|
||||
[^2]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/book) for more details.
|
||||
[^3]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
|
||||
[^4]: See ["Workbook Object" in "SheetJS Data Model"](/docs/csf/book) for more details.
|
||||
[^5]: See [`sheet_add_dom` in "HTML" Utilities](/docs/api/utilities/html#add-to-sheet)
|
||||
@ -16,13 +16,7 @@ With a familiar UI, `x-spreadsheet` is an excellent choice for a modern editor.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 136 | 2025-05-21 |
|
||||
| Safari 18.2 | 2025-05-21 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
This demo was last verified on 2023 December 04.
|
||||
|
||||
:::
|
||||
|
||||
@ -39,7 +33,7 @@ features like scrolling may not work as expected.
|
||||
|
||||
```jsx live
|
||||
function SheetJSXSpread() {
|
||||
const [url, setUrl] = React.useState("https://docs.sheetjs.com/pres.numbers");
|
||||
const [url, setUrl] = React.useState("https://sheetjs.com/pres.numbers");
|
||||
const [done, setDone] = React.useState(false);
|
||||
const ref = React.useRef(); // ref to DIV container
|
||||
const set_url = (evt) => setUrl(evt.target.value);
|
||||
@ -78,7 +72,7 @@ The following snippet fetches a spreadsheet and loads the grid:
|
||||
|
||||
```js
|
||||
(async() => {
|
||||
const ab = await (await fetch("https://docs.sheetjs.com/pres.numbers")).arrayBuffer();
|
||||
const ab = await (await fetch("https://sheetjs.com/pres.numbers")).arrayBuffer();
|
||||
grid.loadData(stox(XLSX.read(ab)));
|
||||
})();
|
||||
```
|
||||
|
||||
@ -15,13 +15,7 @@ with a straightforward API.
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Browser | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 136 | 2025-05-21 |
|
||||
| Safari 18.2 | 2025-05-21 |
|
||||
| Konqueror 22 | 2025-04-23 |
|
||||
This demo was last verified on 2023 December 04.
|
||||
|
||||
:::
|
||||
|
||||
@ -38,7 +32,7 @@ features like scrolling may not work as expected.
|
||||
|
||||
```jsx live
|
||||
function SheetJSCDG() {
|
||||
const [url, setUrl] = React.useState("https://docs.sheetjs.com/pres.numbers");
|
||||
const [url, setUrl] = React.useState("https://sheetjs.com/pres.numbers");
|
||||
const [done, setDone] = React.useState(false);
|
||||
const ref = React.useRef(); // ref to DIV container
|
||||
const set_url = (evt) => setUrl(evt.target.value);
|
||||
|
||||
@ -1,168 +0,0 @@
|
||||
---
|
||||
title: Tabulator
|
||||
pagination_prev: demos/frontend/index
|
||||
pagination_next: demos/net/index
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[Tabulator](https://tabulator.info/) is a powerful data table library designed
|
||||
for ease of use.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
Tabulator offers deep integration with SheetJS for importing and exporting data.
|
||||
This demo covers additional detail including document customization.
|
||||
|
||||
[Click here for a live standalone integration demo.](pathname:///tabulator/)
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Browser | Version | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| Chromium 133 | `6.3.1` | 2025-03-31 |
|
||||
| Konqueror 22 | `6.3.1` | 2025-04-23 |
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
are appropriate for sites that use the Tabulator CDN scripts.
|
||||
|
||||
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||||
installation instructions for projects using a framework.
|
||||
|
||||
:::info pass
|
||||
|
||||
**The Tabulator script must be loaded after the SheetJS scripts!**
|
||||
|
||||
```html
|
||||
<!-- Load SheetJS Scripts -->
|
||||
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
|
||||
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
|
||||
<!-- Tabulator must be loaded after SheetJS scripts -->
|
||||
<script type="text/javascript" src="https://unpkg.com/tabulator-tables@6.3.1/dist/js/tabulator.min.js"></script>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Previewing Data
|
||||
|
||||
Tabulator offers a special `setData` method for assigning data after the table
|
||||
is created. Coupled with the `autoColumns` option, Tabulator will automatically
|
||||
refresh the table.
|
||||
|
||||
:::info pass
|
||||
|
||||
The library scans the first row object to determine the header labels. If a
|
||||
column is missing a value in the first object, it will not be loaded!
|
||||
|
||||
:::
|
||||
|
||||
#### Fetching Files
|
||||
|
||||
When files are stored remotely, the recommended approach is to fetch the files,
|
||||
parse with the SheetJS `read` method, generate arrays of objects from the target
|
||||
sheet using `sheet_to_json`, and load data with the Tabulator `setData` method.
|
||||
The following snippet fetches a sample file and loads the first sheet:
|
||||
|
||||
```html title="Fetching a spreadsheet and Displaying the first worksheet"
|
||||
<!-- Tabulator DIV -->
|
||||
<div id="htmlout"></div>
|
||||
|
||||
<script>
|
||||
/* Initialize Tabulator with the `autoColumns: true` setting */
|
||||
var tbl = new Tabulator('#htmlout', { autoColumns: true });
|
||||
|
||||
/* fetch and display https://docs.sheetjs.com/pres.numbers */
|
||||
(function() { try {
|
||||
fetch("https://docs.sheetjs.com/pres.numbers")
|
||||
.then(function(res) { return res.arrayBuffer(); })
|
||||
.then(function(ab) {
|
||||
/* parse ArrayBuffer */
|
||||
var wb = XLSX.read(ab);
|
||||
/* get first worksheet from SheetJS workbook object */
|
||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||
/* generate array of row objects */
|
||||
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
/* update Tabulator */
|
||||
tbl.setData(data);
|
||||
});
|
||||
} catch(e) {} })();
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Local Files
|
||||
|
||||
Tabulator provides a special `import` method to show a dialog and load data.
|
||||
Since the importer requires the raw binary data, the method must be called with
|
||||
the third argument set to `"buffer"`:
|
||||
|
||||
```html title="Parsing a local spreadsheet and Displaying the first worksheet"
|
||||
<button id="imp"><b>Click here to import from XLSX file</b></button>
|
||||
<!-- Tabulator DIV -->
|
||||
<div id="htmlout"></div>
|
||||
|
||||
<script>
|
||||
/* Initialize Tabulator with the `autoColumns: true` setting */
|
||||
var tbl = new Tabulator('#htmlout', { autoColumns: true });
|
||||
|
||||
/* use Tabulator SheetJS integration to import data */
|
||||
document.getElementById("imp").addEventListener("click", function() {
|
||||
tbl.import("xlsx", ".xlsx", "buffer");
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### Saving Data
|
||||
|
||||
Tabulator provides a special `download` method to initiate the export:
|
||||
|
||||
```html title="Exporting data from Tabulator to XLSX"
|
||||
<input type="submit" value="Export to XLSX!" id="xport" onclick="export_xlsx();">
|
||||
<!-- Tabulator DIV -->
|
||||
<div id="htmlout"></div>
|
||||
|
||||
<script>
|
||||
/* Initialize Tabulator with the `autoColumns: true` setting */
|
||||
var tbl = new Tabulator('#htmlout', { autoColumns: true });
|
||||
|
||||
/* use Tabulator SheetJS integration to import data */
|
||||
function export_xlsx() {
|
||||
/* use Tabulator SheetJS integration */
|
||||
tbl.download("xlsx", "SheetJSTabulator.xlsx");
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
[The official documentation](https://tabulator.info/docs/6.3/download#xlsx)
|
||||
covers supported options.
|
||||
|
||||
#### Post-processing
|
||||
|
||||
The `documentProcessing` event handler is called after Tabulator generates a
|
||||
SheetJS workbook object. This allows for adjustments before creating the final
|
||||
workbook file. The following example adds a second sheet that includes the date:
|
||||
|
||||
```js title="Exporting data and metadata"
|
||||
tbl.download("xlsx", "SheetJSTabulator.xlsx", {
|
||||
documentProcessing: function(wb) {
|
||||
|
||||
/* create a new worksheet */
|
||||
var ws = XLSX.utils.aoa_to_sheet([
|
||||
["SheetJS + Tabulator Demo"],
|
||||
["Export Date:", new Date()]
|
||||
]);
|
||||
|
||||
/* add to workbook */
|
||||
XLSX.utils.book_append_sheet(wb, ws, "Metadata");
|
||||
|
||||
return wb;
|
||||
}
|
||||
});
|
||||
```
|
||||
@ -7,46 +7,23 @@ pagination_next: demos/net/index
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[Vue 3 Table Lite](https://vue3-lite-table.vercel.app/) is a data table library
|
||||
designed for the VueJS web framework.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses Vue 3 Table Lite and SheetJS to pull data from a spreadsheet and
|
||||
display the content in a data table. We'll explore how to import data from files
|
||||
into the data grid and how to export modified data from the grid to workbooks.
|
||||
|
||||
The ["Demo"](#demo) section includes a complete example that displays data from
|
||||
user-supplied sheets and exports data to XLSX workbooks:
|
||||
|
||||

|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Browser | Version | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| Chromium 135 | `1.4.3` | 2025-04-23 |
|
||||
| Konqueror 22 | `1.4.3` | 2025-04-23 |
|
||||
This demo was tested against `vue3-table-lite 1.3.9`, VueJS `3.3.10` and ViteJS
|
||||
`5.0.5` on 2023 December 04.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
The demo creates a site that looks like the screenshot below:
|
||||
|
||||

|
||||
|
||||
## Integration Details
|
||||
|
||||
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||||
installation in ViteJS projects using Vue 3 Table Lite.
|
||||
|
||||
Using the `npm` tool, this command installs SheetJS and Vue 3 Table Lite:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.4.3`}
|
||||
</CodeBlock>
|
||||
|
||||
#### Rows and Columns Bindings
|
||||
|
||||
Vue 3 Table Lite presents two attribute bindings: an array of column metadata
|
||||
`vue3-table-lite` presents two attribute bindings: an array of column metadata
|
||||
(`columns`) and an array of objects representing the displayed data (`rows`).
|
||||
Typically both are `ref` objects:
|
||||
|
||||
@ -137,7 +114,7 @@ cd sheetjs-vtl
|
||||
2) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.4.3`}
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz vue3-table-lite@1.3.9`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Download [`src/App.vue`](pathname:///vtl/App.vue) and replace the contents:
|
||||
@ -154,5 +131,5 @@ npm run dev
|
||||
|
||||
5) Load the displayed URL (typically `http://localhost:5173`) in a web browser.
|
||||
|
||||
When the page loads, it will try to fetch https://docs.sheetjs.com/pres.numbers
|
||||
When the page loads, it will try to fetch <https://sheetjs.com/pres.numbers>
|
||||
and display the data. Click "Export" to generate a workbook.
|
||||
|
||||
@ -26,32 +26,7 @@ user-supplied sheets and exports data to XLSX workbooks:
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Browser | Version | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| Chromium 135 | `5.3.2` | 2025-04-23 |
|
||||
| Konqueror 22 | `5.3.2` | 2025-04-23 |
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
|
||||
**Glide Data Grid is not compatible with ReactJS 19!**
|
||||
|
||||
When trying to install in a new project, `npm install` will fail:
|
||||
|
||||
```
|
||||
npm error Found: react@19.1.0
|
||||
npm error node_modules/react
|
||||
npm error react@"^19.0.0" from the root project
|
||||
npm error
|
||||
npm error Could not resolve dependency:
|
||||
npm error peer react@"^16.12.0 || 17.x || 18.x" from @glideapps/glide-data-grid@5.3.2
|
||||
npm error node_modules/@glideapps/glide-data-grid
|
||||
```
|
||||
|
||||
This demo explicitly uses ReactJS 18.
|
||||
This demo was last tested on 2023 December 04 with Glide Data Grid 5.3.2
|
||||
|
||||
:::
|
||||
|
||||
@ -365,19 +340,13 @@ cd sheetjs-gdg
|
||||
npm i
|
||||
```
|
||||
|
||||
2) Explicitly downgrade ReactJS to version 18:
|
||||
|
||||
```bash
|
||||
npm i --save react@18 react-dom@18
|
||||
```
|
||||
|
||||
3) Install SheetJS and Glide Data Grid libraries:
|
||||
2) Install SheetJS and Glide Data Grid libraries:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @glideapps/glide-data-grid@5.3.2`}
|
||||
</CodeBlock>
|
||||
|
||||
4) Start the dev server:
|
||||
3) Start dev server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
@ -386,21 +355,21 @@ npm run dev
|
||||
The terminal window will display a URL (typically `http://localhost:5173`).
|
||||
Open the URL with a web browser and confirm that a page loads.
|
||||
|
||||
5) Download [`App.tsx`](pathname:///gdg/App.tsx) and replace `src/App.tsx`:
|
||||
4) Download [`App.tsx`](pathname:///gdg/App.tsx) and replace `src/App.tsx`:
|
||||
|
||||
```bash
|
||||
curl -L -o src/App.tsx https://docs.sheetjs.com/gdg/App.tsx
|
||||
```
|
||||
|
||||
#### Testing
|
||||
**Testing**
|
||||
|
||||
6) Refresh the browser window. A grid should be displayed:
|
||||
5) Refresh the browser window. A grid should be displayed:
|
||||
|
||||

|
||||
|
||||
The demo downloads and processes https://docs.sheetjs.com/pres.numbers .
|
||||
The demo downloads and processes <https://sheetjs.com/pres.numbers>.
|
||||
|
||||
7) Make some changes to the grid data.
|
||||
6) Make some changes to the grid data.
|
||||
|
||||
:::note pass
|
||||
|
||||
@ -415,14 +384,13 @@ values should be 41, 42, 43, 44, and 45, as shown in the screenshot below:
|
||||
|
||||

|
||||
|
||||
8) Click on the "Export" button. The browser should attempt to download a XLSX
|
||||
file (`sheetjs-gdg.xlsx`). Save the file.
|
||||
7) Click on the "Export" button to create a XLSX file (`sheetjs-gdg.xlsx`).
|
||||
|
||||
Open the generated file and verify the contents match the grid.
|
||||
|
||||
9) Reload the page. The contents will revert back to the original table.
|
||||
8) Reload the page. The contents will revert back to the original table.
|
||||
|
||||
10) Click "Choose File" and select the new `sheetjs-gdg.xlsx` file. The table
|
||||
9) Click "Choose File" and select the new `sheetjs-gdg.xlsx` file. The table
|
||||
should update with the data in the file.
|
||||
|
||||
[^1]: See ["Array of Objects" in the ReactJS demo](/docs/demos/frontend/react#array-of-objects)
|
||||
|
||||
@ -7,7 +7,7 @@ pagination_next: demos/net/index
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[React Data Grid](https://comcast.github.io/react-data-grid/) is a data grid
|
||||
[React Data Grid](https://adazzle.github.io/react-data-grid/) is a data grid
|
||||
designed for the ReactJS web framework.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
@ -26,21 +26,19 @@ user-supplied sheets and exports data to XLSX workbooks:
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Browser | Version | Date | Notes |
|
||||
|:-------------|:----------------|:-----------|:----------------------------|
|
||||
| Chromium 135 | `7.0.0-beta.19` | 2025-04-23 | Requires ReactJS 18 |
|
||||
| Chromium 135 | `7.0.0-beta.52` | 2025-04-23 | No edit support |
|
||||
| Konqueror 22 | `7.0.0-beta.52` | 2025-04-23 | No edit support, CSS errors |
|
||||
| Version | Date | Notes |
|
||||
|:----------------|:-----------|:---------------------|
|
||||
| `7.0.0-beta.19` | 2023-12-04 | |
|
||||
| `7.0.0-beta.41` | 2023-12-04 | Editing did not work |
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
When this demo was last tested against the latest version, the grid correctly
|
||||
displayed data but data could not be edited by the user.
|
||||
When this demo was last tested, the grid correctly displayed data but could not
|
||||
be edited by the user.
|
||||
|
||||
The current recommendation is to use version `7.0.0-beta.19` and to forcefully
|
||||
downgrade ReactJS to version 18.
|
||||
The current recommendation is to use version `7.0.0-beta.19`.
|
||||
|
||||
:::
|
||||
|
||||
@ -52,7 +50,7 @@ installation with Yarn and other package managers.
|
||||
Using the `npm` tool, this command installs SheetJS and React Data Grid:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.19`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.41`}
|
||||
</CodeBlock>
|
||||
|
||||
Methods and components in both libraries can be loaded in pages using `import`:
|
||||
@ -148,31 +146,19 @@ function rdg_to_ws(rows: Row[]): WorkSheet {
|
||||
|
||||
## Demo
|
||||
|
||||
1) Create a new ViteJS app using the `react-ts` template:
|
||||
1) Create a new TypeScript `create-react-app` app:
|
||||
|
||||
```bash
|
||||
npm create vite@latest -- sheetjs-rdg --template react-ts
|
||||
npx create-react-app sheetjs-rdg --template typescript
|
||||
cd sheetjs-rdg
|
||||
```
|
||||
|
||||
2) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.19`}
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.41`}
|
||||
</CodeBlock>
|
||||
|
||||
<details>
|
||||
<summary><b>Installing RDG version that supports editing</b> (click to show)</summary>
|
||||
|
||||
Editing support requires ReactJS 18 and React DataGrid version `7.0.0-beta.19`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S react@18 react-dom@18
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.19`}
|
||||
</CodeBlock>
|
||||
|
||||
</details>
|
||||
|
||||
3) Download [`App.tsx`](pathname:///rdg/App.tsx) and replace `src/App.tsx`.
|
||||
|
||||
```bash
|
||||
@ -182,48 +168,12 @@ curl -L -o src/App.tsx https://docs.sheetjs.com/rdg/App.tsx
|
||||
4) Start the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
npm start
|
||||
```
|
||||
|
||||
The terminal window will display a URL (typically `http://localhost:5173`).
|
||||
Open the URL with a web browser and confirm that a page loads.
|
||||
|
||||
:::caution pass
|
||||
|
||||
**There were breaking changes in point releases of React DataGrid!**
|
||||
|
||||
The JavaScript console may show an error message referencing `default`:
|
||||
|
||||
```
|
||||
Uncaught SyntaxError: The requested module '/node_modules/.vite/deps/react-data-grid.js?v=f9b1b87a' does not provide an export named 'default' (at App.tsx:2:8)
|
||||
```
|
||||
|
||||
In a point release, `DataGrid` was moved from the default export to a named
|
||||
export. The `src/App.tsx` script must be edited to reflect the change:
|
||||
|
||||
```js title="src/App.tsx (edit highlighted lines)"
|
||||
import React, { useEffect, useState, ChangeEvent } from "react";
|
||||
// highlight-next-line
|
||||
import { textEditor, Column, DataGrid } from "react-data-grid";
|
||||
import { read, utils, WorkSheet, writeFile } from "xlsx";
|
||||
```
|
||||
|
||||
After updating the script, the webpage must be manually refreshed.
|
||||
|
||||
:::
|
||||
|
||||
#### Testing
|
||||
|
||||
5) Confirm the table shows a list of Presidents.
|
||||
|
||||
When the page loads, it will fetch https://docs.sheetjs.com/pres.numbers, parse
|
||||
5) When the page loads, it will fetch <https://sheetjs.com/pres.numbers>, parse
|
||||
with SheetJS, and load the data in the data grid.
|
||||
|
||||
6) Click the "export [.xlsx]" button to export the grid data to XLSX. It should
|
||||
attempt to download `SheetJSRDG.xlsx`.
|
||||
|
||||
7) Open the generated file in a spreadsheet editor. Set cell A7 to "SheetJS Dev"
|
||||
and set cell B7 to 47. Save the file.
|
||||
|
||||
8) Use the file picker to select the modified file. The table will refresh and
|
||||
show the new data.
|
||||
6) Click one of the "export" buttons to export the grid data to file.
|
||||
|
||||
@ -5,174 +5,44 @@ pagination_next: demos/net/index
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
Material UI is a collection of ReactJS Components that follows the
|
||||
[Google Material Design system](https://material.io/)
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses Material UI and SheetJS to pull data from a spreadsheet and
|
||||
display the data. We'll explore how to import data from spreadsheets and export
|
||||
data to spreadsheets. The following Material UI components will be tested:
|
||||
|
||||
- ["Table"](#material-ui-table) is based on the core HTML TABLE element.
|
||||
|
||||
- ["Data Grid"](#material-ui-data-grid) is a data grid for larger datasets.
|
||||
|
||||
:::note pass
|
||||
|
||||
The [ReactJS demo](/docs/demos/frontend/react) covers basic ReactJS concepts.
|
||||
It should be perused before reading this demo.
|
||||
|
||||
:::
|
||||
This demo covers the traditional Material UI Table as well as the MUI Data Grid.
|
||||
|
||||
## Integration Details
|
||||
|
||||
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||||
installation in projects using Material UI.
|
||||
|
||||
After installing the SheetJS module in a ReactJS project, `import` statements
|
||||
can load relevant parts of the library.
|
||||
|
||||
```js
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
```
|
||||
|
||||
## Material UI Table
|
||||
|
||||
The `Table` component abstracts the `<table>` element in HTML.
|
||||
The `Table` component abstracts the `<table>` element in HTML. `table_to_book`
|
||||
can process a `ref` attached to the `Table` element:
|
||||
|
||||
### Importing Data
|
||||
|
||||
Starting from a SheetJS worksheet object[^1], the `sheet_to_json` method[^2]
|
||||
generates an array of row objects.
|
||||
|
||||
In the [ReactJS "Array of Objects" demo](/docs/demos/frontend/react), the array
|
||||
of objects is rendered by manually mapping over data. For example, starting from
|
||||
the following spreadsheet and data:
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>State</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
```js
|
||||
[
|
||||
{ Name: "Bill Clinton", Index: 42 },
|
||||
{ Name: "GeorgeW Bush", Index: 43 },
|
||||
{ Name: "Barack Obama", Index: 44 },
|
||||
{ Name: "Donald Trump", Index: 45 },
|
||||
{ Name: "Joseph Biden", Index: 46 }
|
||||
]
|
||||
```
|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
The HTML table elements map to MUI components:
|
||||
|
||||
| HTML | MUI |
|
||||
|:--------|:------------|
|
||||
| `TABLE` | `Table` |
|
||||
| `THEAD` | `TableHead` |
|
||||
| `TBODY` | `TableBody` |
|
||||
| `TR` | `TableRow` |
|
||||
| `TD` | `TableCell` |
|
||||
|
||||
The library requires a `TableContainer` container component.
|
||||
|
||||
The following example JSX shows a table using HTML and using MUI components:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="ReactJS" label="ReactJS">
|
||||
|
||||
```jsx title="Example JSX for displaying arrays of objects"
|
||||
<table>
|
||||
{/* The `thead` section includes the table header row */}
|
||||
<thead><tr><th>Name</th><th>Index</th></tr></thead>
|
||||
{/* The `tbody` section includes the data rows */}
|
||||
<tbody>
|
||||
{/* generate row (TR) for each president */}
|
||||
// highlight-start
|
||||
{pres.map(row => (
|
||||
<tr>
|
||||
{/* Generate cell (TD) for name / index */}
|
||||
<td>{row.Name}</td>
|
||||
<td>{row.Index}</td>
|
||||
</tr>
|
||||
))}
|
||||
// highlight-end
|
||||
</tbody>
|
||||
</table>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="MUI" label="Material UI">
|
||||
|
||||
```jsx title="Example JSX for displaying arrays of objects"
|
||||
<TableContainer><Table>
|
||||
{/* The `TableHead` section includes the table header row */}
|
||||
<TableHead><TableRow><TableCell>Name</TableCell><TableCell>Index</TableCell></TableRow></TableHead>
|
||||
{/* The `TableBody` section includes the data rows */}
|
||||
<TableBody>
|
||||
{/* generate row (TableRow) for each president */}
|
||||
// highlight-start
|
||||
{pres.map((row, idx) => (
|
||||
<TableRow key={idx}>
|
||||
{/* Generate cell (TableCell) for name / index */}
|
||||
<TableCell>{row.Name}</TableCell>
|
||||
<TableCell>{row.Index}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
// highlight-end
|
||||
</TableBody>
|
||||
</Table></TableContainer>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Exporting Data
|
||||
|
||||
The SheetJS `table_to_book` method[^3] can parse data from a DOM element.
|
||||
The MUI `Table` element is really an HTML TABLE element under the hood. A `ref`
|
||||
attached to the `Table` element can be processed by `table_to_book`.
|
||||
|
||||
The following snippet uses the `writeFileXLSX` method[^4] to generate and
|
||||
download a XLSX workbook:
|
||||
|
||||
```tsx title="Skeleton Component for exporting a Material UI Table"
|
||||
```tsx
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import Table from '@mui/material/Table';
|
||||
// ...
|
||||
// highlight-start
|
||||
import { utils, writeFileXLSX } from "xlsx";
|
||||
import { useRef } from "react";
|
||||
// highlight-end
|
||||
|
||||
export default function MUITableSheetJSExport() {
|
||||
/* This ref will be attached to the <Table> component */
|
||||
// ...
|
||||
export default function BasicTable() {
|
||||
// highlight-next-line
|
||||
const tbl = useRef<HTMLTableElement>(null);
|
||||
|
||||
const xport = () => {
|
||||
/* the .current field will be a TABLE element */
|
||||
const table_elt = tbl.current;
|
||||
/* generate SheetJS workbook */
|
||||
// highlight-next-line
|
||||
const wb = utils.table_to_book(table_elt);
|
||||
/* export to XLSX */
|
||||
writeFileXLSX(wb, "SheetJSMaterialUI.xlsx");
|
||||
};
|
||||
|
||||
return ( <>
|
||||
<button onClick={xport}>Export</button>
|
||||
<TableContainer>
|
||||
<button onClick={() => {
|
||||
// highlight-next-line
|
||||
const wb = utils.table_to_book(tbl.current);
|
||||
writeFileXLSX(wb, "SheetJSMaterialUI.xlsx");
|
||||
}}>Export</button>
|
||||
<TableContainer {...}>
|
||||
// highlight-next-line
|
||||
<Table ref={tbl}>{/* ... */}</Table>
|
||||
<Table {...} ref={tbl}>
|
||||
{/* ... material ui table machinations ... */}
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<>);
|
||||
}
|
||||
@ -182,12 +52,8 @@ export default function MUITableSheetJSExport() {
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Browser | Material UI | Emotion | Date |
|
||||
|:-------------|:------------|:----------|:-----------|
|
||||
| Chromium 135 | `7.0.2` | `11.11.4` | 2025-04-23 |
|
||||
| Konqueror 22 | `7.0.2` | `11.11.4` | 2025-04-23 |
|
||||
This demo was last run on 2023 December 04 against Material UI 5.14.19 paired
|
||||
with Emotion 11.11.1
|
||||
|
||||
:::
|
||||
|
||||
@ -201,7 +67,7 @@ cd sheetjs-mui
|
||||
2) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material@7.0.2 @emotion/react@11.11.4 @emotion/styled@11.11.5`}
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material@5.14.19 @emotion/react@11.11.1 @emotion/styled@11.11.0`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Download [`App.tsx`](pathname:///mui/table/App.tsx) and replace `src/App.tsx`.
|
||||
@ -216,14 +82,8 @@ curl -L -o src/App.tsx https://docs.sheetjs.com/mui/table/App.tsx
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The terminal window will display a URL (typically `http://localhost:5173`).
|
||||
Open the URL with a web browser and confirm that a page loads.
|
||||
|
||||
Confirm the table shows a list of Presidents. When the page loads, it will fetch
|
||||
https://docs.sheetjs.com/pres.numbers, parse with SheetJS, and load the data in
|
||||
the data grid.
|
||||
|
||||
Click the "Export" button and open the generated file in a spreadsheet editor.
|
||||
The script should open the live demo in a web browser. Click the "Export" button
|
||||
to save the file. Open the generated file in a spreadsheet editor.
|
||||
|
||||
## Material UI Data Grid
|
||||
|
||||
@ -335,12 +195,8 @@ export default function App() {
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Browser | Data Grid | Emotion | Date |
|
||||
|:-------------|:----------|:----------|:-----------|
|
||||
| Chromium 125 | `8.0.0` | `11.11.4` | 2025-04-23 |
|
||||
| Konqueror 22 | `8.0.0` | `11.11.4` | 2025-04-23 |
|
||||
This demo was last run on 2023 December 04 against MUI data grid 6.18.3 paired
|
||||
with Emotion 11.11.1
|
||||
|
||||
:::
|
||||
|
||||
@ -354,7 +210,7 @@ cd sheetjs-muidg
|
||||
2) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid@8.0.0 @emotion/react@11.11.4 @emotion/styled@11.11.5`}
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid@6.18.3 @emotion/react@11.11.1 @emotion/styled@11.11.0`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Download [`App.tsx`](pathname:///mui/dg/App.tsx) and replace `src/App.tsx`.
|
||||
@ -369,31 +225,4 @@ curl -L -o src/App.tsx https://docs.sheetjs.com/mui/dg/App.tsx
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The terminal window will display a URL (typically `http://localhost:5173`).
|
||||
Open the URL with a web browser and confirm that a page loads.
|
||||
|
||||
When the page loads, it will process https://docs.sheetjs.com/pres.numbers
|
||||
|
||||
:::caution pass
|
||||
|
||||
The data grid uses nascent ECMAScript features that are not supported in older
|
||||
browsers. Shims can be added in the `<head>` section of `index.html`:
|
||||
|
||||
```html title="index.html (add highlighted lines in HEAD)"
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- highlight-start -->
|
||||
<script>
|
||||
/* workarounds for legacy browsers */
|
||||
if(!Object.hasOwn) Object.hasOwn = function(o, v) { return o.hasOwnProperty(v); };
|
||||
if(!Array.prototype.at) Array.prototype.at = function(idx) { return this[idx < 0 ? idx + this.length : idx]; };
|
||||
</script>
|
||||
<!-- highlight-end -->
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See ["Sheet Objects"](/docs/csf/sheet)
|
||||
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^3]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
|
||||
[^4]: See [`writeFileXLSX` in "Writing Files"](/docs/api/write-options)
|
||||
When the page loads, it will fetch and process <https://sheetjs.com/pres.numbers>
|
||||
|
||||
@ -30,7 +30,8 @@ The `sheet_to_json` utility function generates arrays of objects, which is
|
||||
suitable for a number of libraries. When more advanced shapes are needed,
|
||||
it is easier to process an array of arrays.
|
||||
|
||||
#### x-spreadsheet
|
||||
|
||||
### x-spreadsheet
|
||||
|
||||
With a familiar UI, `x-spreadsheet` is an excellent choice for a modern editor.
|
||||
|
||||
@ -38,7 +39,7 @@ With a familiar UI, `x-spreadsheet` is an excellent choice for a modern editor.
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/grid/xs)**
|
||||
|
||||
#### Canvas Datagrid
|
||||
### Canvas Datagrid
|
||||
|
||||
After extensive testing, `canvas-datagrid` stood out as a high-performance grid
|
||||
with a straightforward API.
|
||||
@ -47,16 +48,15 @@ with a straightforward API.
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/grid/cdg)**
|
||||
|
||||
#### Tabulator
|
||||
### Tabulator
|
||||
|
||||
[Tabulator](https://tabulator.info/docs/5.4/download#xlsx) includes deep support
|
||||
through a special Export button. It handles the SheetJS operations internally.
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/grid/tabulator)**
|
||||
|
||||
#### Angular UI Grid
|
||||
### Angular UI Grid
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
This UI Grid is for AngularJS, not the modern Angular. New projects should not
|
||||
use AngularJS. This demo is included for legacy applications.
|
||||
@ -67,8 +67,7 @@ The [AngularJS demo](/docs/demos/frontend/angularjs) covers more general strateg
|
||||
|
||||
[Click here for a live integration demo.](pathname:///angularjs/ui-grid.html)
|
||||
|
||||
<details>
|
||||
<summary><b>Notes</b> (click to show)</summary>
|
||||
<details><summary><b>Notes</b> (click to show)</summary>
|
||||
|
||||
The library does not provide any way to modify the import button, so the demo
|
||||
includes a simple directive for a File Input HTML element. It also includes a
|
||||
@ -93,21 +92,21 @@ and idioms. The same `sheet_to_json` and `json_to_sheet` / `aoa_to_sheet`
|
||||
methods are used, but they pull from a shared state object that can be mutated
|
||||
with other buttons and components on the page.
|
||||
|
||||
#### React Data Grid
|
||||
### React Data Grid
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/grid/rdg)**
|
||||
|
||||
#### Glide Data Grid
|
||||
### Glide Data Grid
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/grid/gdg)**
|
||||
|
||||
#### Material UI Data Grid
|
||||
### Material UI Data Grid
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/grid/mui#material-ui-data-grid)**
|
||||
|
||||
<!-- spellchecker-disable -->
|
||||
|
||||
#### vue3-table-lite
|
||||
### vue3-table-lite
|
||||
|
||||
<!-- spellchecker-enable -->
|
||||
|
||||
@ -134,7 +133,7 @@ TABLE elements and when writing to XLSX and other spreadsheet formats.
|
||||
|
||||
:::
|
||||
|
||||
#### Fixed Tables
|
||||
### Fixed Tables
|
||||
|
||||
When the page has a raw HTML table, the easiest solution is to attach an `id`:
|
||||
|
||||
@ -171,10 +170,10 @@ XLSX.writeFile(wb, "HTMLFlicker.xlsx");
|
||||
document.body.removeChild(tbl);
|
||||
```
|
||||
|
||||
#### React
|
||||
### React
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/frontend/react#html)**
|
||||
|
||||
#### Material UI Table
|
||||
### Material UI Table
|
||||
|
||||
**[The exposition has been moved to a separate page.](/docs/demos/grid/mui#material-ui-table)**
|
||||
|
||||
@ -8,11 +8,6 @@ sidebar_custom_props:
|
||||
type: native
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[Lume](https://lume.land/) is a lightweight unopinionated static site generator.
|
||||
It has a rich ecosystem of JavaScript-powered plugins[^1]
|
||||
|
||||
@ -28,11 +23,8 @@ powered by an XLSX spreadsheet.
|
||||
## Integration Details
|
||||
|
||||
The official "Sheets" plugin[^2] uses SheetJS to load data from spreadsheets.
|
||||
Under the hood, the plugin uses the following SheetJS methods:
|
||||
|
||||
- [`read`](/docs/api/parse-options) parses spreadsheet files.
|
||||
- [`sheet_to_json`](/docs/api/utilities/array#array-output) generates arrays of
|
||||
objects from worksheet data.
|
||||
Under the hood, the plugin uses the SheetJS `read`[^3] method to parse files and
|
||||
the `sheet_to_json`[^4] method to generate arrays of objects.
|
||||
|
||||
Lume supports refreshing data during development. The generated static sites
|
||||
include the raw data without referencing the underlying spreadsheet files.
|
||||
@ -65,8 +57,8 @@ The lines are automatically added if `sheets` plugin is enabled during setup.
|
||||
Spreadsheet files added in the `_data` subdirectory are accessible from template
|
||||
files using the name stem.
|
||||
|
||||
For example, [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) can be accessed
|
||||
using the variable `pres` in a template.
|
||||
For example, [`pres.xlsx`](https://sheetjs.com/pres.xlsx) can be accessed using
|
||||
the variable `pres` in a template.
|
||||
|
||||
#### Single-Sheet Workbooks
|
||||
|
||||
@ -121,7 +113,7 @@ named `"VicePresidents"`, then the following snippet would print data from the
|
||||
|
||||
#### File Formats
|
||||
|
||||
As explained in the official plugin documentation[^3], the loader loads XLSX.
|
||||
As explained in the official plugin documentation[^5], the loader loads XLSX.
|
||||
NUMBERS, and CSV files. Other extensions can be added through the `extensions`
|
||||
property in the argument to the `sheets` plugin:
|
||||
|
||||
@ -141,9 +133,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Lume | Date |
|
||||
|:---------|:-----------|
|
||||
| `1.19.4` | 2025-09-13 |
|
||||
| `2.5.3` | 2025-09-13 |
|
||||
| `3.0.9` | 2025-09-13 |
|
||||
| `1.19.4` | 2024-03-16 |
|
||||
| `2.1.2` | 2024-03-16 |
|
||||
|
||||
This example uses the Nunjucks template format. Lume plugins support additional
|
||||
template formats, including Markdown and JSX.
|
||||
@ -152,24 +143,24 @@ template formats, including Markdown and JSX.
|
||||
|
||||
### Initial Setup
|
||||
|
||||
0) Install Deno[^4]
|
||||
0) Install Deno[^6]
|
||||
|
||||
1) Create a stock site:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="v1" label="v1">
|
||||
|
||||
```bash
|
||||
mkdir -p sheetjs-lume
|
||||
cd sheetjs-lume
|
||||
deno run -Ar https://deno.land/x/lume@v1.19.4/init.ts
|
||||
deno run -Ar https://deno.land/x/lume@v2.1.2/init.ts
|
||||
```
|
||||
|
||||
When prompted, enter the following options:
|
||||
|
||||
- `Choose the configuration file format`: select `_config.ts`
|
||||
- `Do you want to install some plugins now?`: select `Yes`
|
||||
- `Select the plugins to install`: select `sheets` and press <kbd>Enter</kbd>
|
||||
- `Select the plugins to install`: select `sheets` and `nunjucks`
|
||||
- `Do you want to setup a CMS?`: select `Maybe later`
|
||||
|
||||
The project will be configured and modules will be installed.
|
||||
|
||||
:::note pass
|
||||
|
||||
@ -177,58 +168,11 @@ The `nunjucks` plugin was included by default in Lume version 1.
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="v2" label="v2">
|
||||
|
||||
```bash
|
||||
mkdir -p sheetjs-lume
|
||||
cd sheetjs-lume
|
||||
deno run -Ar https://deno.land/x/lume@v2.5.3/init.ts
|
||||
```
|
||||
|
||||
When prompted, enter the following options:
|
||||
|
||||
- `What kind of setup do you want?`: select `Basic + plugins`
|
||||
- `Select the plugins to install`: select `sheets` and `nunjucks`
|
||||
- `Do you want to setup a CMS?`: select `No` or `Maybe later`
|
||||
|
||||
:::note pass
|
||||
|
||||
The `nunjucks` plugin is not included by default in Lume version 2.
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="v3" label="v3">
|
||||
|
||||
```bash
|
||||
mkdir -p sheetjs-lume
|
||||
cd sheetjs-lume
|
||||
deno run -A https://lume.land/init.ts
|
||||
```
|
||||
|
||||
When prompted, enter the following options:
|
||||
|
||||
- `What kind of setup do you want?`: select `Basic + plugins`
|
||||
- `Select the plugins to install`: select `sheets` and `nunjucks`
|
||||
- `Do you want to setup a CMS?`: select `No` or `Maybe later`
|
||||
|
||||
:::note pass
|
||||
|
||||
The `nunjucks` plugin is not included by default in Lume version 3.
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The project will be configured and modules will be installed.
|
||||
|
||||
2) Download https://docs.sheetjs.com/pres.xlsx and place in a `_data` subfolder:
|
||||
2) Download <https://sheetjs.com/pres.xlsx> and place in a `_data` subfolder:
|
||||
|
||||
```bash
|
||||
mkdir -p _data
|
||||
curl -L -o _data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
curl -L -o _data/pres.xlsx https://sheetjs.com/pres.xlsx
|
||||
```
|
||||
|
||||
3) Create a `index.njk` file that references the file:
|
||||
@ -272,9 +216,7 @@ After saving the spreadsheet, the page will refresh and show the new contents.
|
||||
|
||||
### Static Site
|
||||
|
||||
6) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
7) Build the static site:
|
||||
6) Stop the server (press `CTRL+C` in the terminal window) and run
|
||||
|
||||
```bash
|
||||
deno task lume
|
||||
@ -282,10 +224,10 @@ deno task lume
|
||||
|
||||
This will create a static site in the `_site` folder
|
||||
|
||||
8) Test the generated site by starting a web server:
|
||||
7) Test the generated site by running
|
||||
|
||||
```bash
|
||||
npx -y http-server _site
|
||||
npx http-server _site
|
||||
```
|
||||
|
||||
The program will display a URL (typically `http://localhost:8080`). Accessing
|
||||
@ -298,5 +240,7 @@ This site is self-contained and ready for deployment!
|
||||
|
||||
[^1]: See ["Plugins"](https://lume.land/plugins/?status=all) in the Lume documentation
|
||||
[^2]: See ["Sheets"](https://lume.land/plugins/sheets/) in the Lume documentation
|
||||
[^3]: See ["Formats"](https://lume.land/plugins/sheets/#formats) in the Lume documentation
|
||||
[^4]: See ["Installation"](https://docs.deno.com/runtime/getting_started/installation/) in the Deno documentation
|
||||
[^3]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^5]: See ["Formats"](https://lume.land/plugins/sheets/#formats) in the Lume documentation
|
||||
[^6]: See ["Installation"](https://deno.com/manual/getting_started/installation) in the Deno documentation
|
||||
@ -13,9 +13,6 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
export const r = {style: {color:"red"}};
|
||||
export const R = {style: {backgroundColor:"darkred"}};
|
||||
|
||||
GatsbyJS is a framework for creating websites. It uses React components for page
|
||||
templates and GraphQL for loading data.
|
||||
|
||||
@ -51,26 +48,14 @@ overridden through a `package.json` override in the latest versions of NodeJS:
|
||||
|
||||
:::
|
||||
|
||||
:::danger Telemetry
|
||||
:::warning Telemetry
|
||||
|
||||
Older versions of GatsbyJS collect telemetry by default. The `telemetry`
|
||||
subcommand can disable telemetry:
|
||||
GatsbyJS collects telemetry by default. The `telemetry` subcommand can disable it:
|
||||
|
||||
```js
|
||||
npx gatsby telemetry --disable
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
GatsbyJS `5.14.1` does not transmit telemetry:
|
||||
|
||||
```text title="Expected output in GatsbyJS 5.14.1"
|
||||
Telemetry is no longer gathered and is always disabled
|
||||
```
|
||||
|
||||
It is still strongly encouraged to disable telemetry since older projects may
|
||||
use a GatsbyJS version that embeds telemetry.
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
@ -191,8 +176,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| GatsbyJS | Date |
|
||||
|:---------|:-----------|
|
||||
| `5.15.0` | 2025-09-13 |
|
||||
| `4.25.8` | 2025-09-13 |
|
||||
| `5.12.1` | 2023-12-04 |
|
||||
| `4.25.8` | 2024-03-27 |
|
||||
|
||||
:::
|
||||
|
||||
@ -204,25 +189,6 @@ This demo was tested in the following environments:
|
||||
npx gatsby telemetry --disable
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
In NodeJS 22, the process displayed an error:
|
||||
|
||||
<pre>
|
||||
<span {...R}> ERROR </span><span {...r}> UNKNOWN</span>
|
||||
{`\n`}
|
||||
{`\n`}
|
||||
(node:25039) [DEP0040] DeprecationWarning: The `punycode` module is deprecated.
|
||||
Please use a userland alternative instead.
|
||||
(Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
</pre>
|
||||
|
||||
**This is a false report!**
|
||||
|
||||
The error can be safely ignored.
|
||||
|
||||
:::
|
||||
|
||||
1) Create a template site:
|
||||
|
||||
```bash
|
||||
@ -233,25 +199,23 @@ npx gatsby new sheetjs-gatsby
|
||||
|
||||
For older Gatsby versions, the project must be built from the starter project.
|
||||
|
||||
The starter commit for GatsbyJS 4 is `6bc4466090845f20650117b3d27e68e6e46dc8d5`.
|
||||
This version of the starter can be fetched with `git`:
|
||||
For GatsbyJS 4, the starter commit is `6bc4466090845f20650117b3d27e68e6e46dc8d5`
|
||||
and the steps are shown below:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/gatsbyjs/gatsby-starter-default sheetjs-gatsby
|
||||
cd sheetjs-gatsby
|
||||
git checkout 6bc4466090845f20650117b3d27e68e6e46dc8d5
|
||||
npm install
|
||||
mkdir .cache
|
||||
cd ..
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Start the local development server:
|
||||
2) Follow the on-screen instructions for starting the local development server:
|
||||
|
||||
```bash
|
||||
cd sheetjs-gatsby
|
||||
npm i
|
||||
npm run develop
|
||||
```
|
||||
|
||||
@ -271,7 +235,7 @@ Open a web browser to the displayed URL (typically `http://localhost:8000/`)
|
||||
`}
|
||||
</CodeBlock>
|
||||
|
||||
4) Install the SheetJS library and GatsbyJS plugins:
|
||||
4) Install the library and plugins:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
@ -291,12 +255,12 @@ npm i --save gatsby-transformer-excel@4 gatsby-source-filesystem@4
|
||||
|
||||
:::
|
||||
|
||||
5) Make a `src/data` directory, download https://docs.sheetjs.com/pres.xlsx, and
|
||||
5) Make a `src/data` directory, download <https://sheetjs.com/pres.xlsx>, and
|
||||
move the downloaded file into the new folder:
|
||||
|
||||
```bash
|
||||
mkdir -p src/data
|
||||
curl -L -o src/data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
curl -L -o src/data/pres.xlsx https://sheetjs.com/pres.xlsx
|
||||
```
|
||||
|
||||
6) Edit `gatsby-config.js` and add the following lines to the `plugins` array:
|
||||
@ -343,18 +307,14 @@ If the `plugins` array exists, the two plugins should be added at the beginning:
|
||||
|
||||
:::
|
||||
|
||||
7) Stop and restart the development server process:
|
||||
|
||||
```bash
|
||||
npm run develop
|
||||
```
|
||||
Stop and restart the development server process (`npm run develop`).
|
||||
|
||||
### GraphiQL test
|
||||
|
||||
8) Open the GraphiQL editor in a web browser. The output of the previous step
|
||||
displayed the URL (typically `http://localhost:8000/___graphql` ).
|
||||
7) Open the GraphiQL editor. The output of the previous step displayed the URL
|
||||
(typically `http://localhost:8000/___graphql` )
|
||||
|
||||
9) Paste the following query into the code editor:
|
||||
There is an editor in the left pane. Paste the following query into the editor:
|
||||
|
||||
```graphql title="GraphQL Query (paste into editor)"
|
||||
{
|
||||
@ -373,60 +333,11 @@ Press the Execute Query button (`▶`) and data should show up in the right pane
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary><b>Sample Output</b> (click to show)</summary>
|
||||
|
||||
In GatsbyJS versions `5.13.4` and `4.25.8`, the raw output was:
|
||||
|
||||
```json title="GraphQL query result from GatsbyJS 5.13.4"
|
||||
{
|
||||
"data": {
|
||||
"allPresXlsxSheet1": {
|
||||
"edges": [
|
||||
{
|
||||
"node": {
|
||||
"Name": "Bill Clinton",
|
||||
"Index": 42
|
||||
}
|
||||
},
|
||||
{
|
||||
"node": {
|
||||
"Name": "GeorgeW Bush",
|
||||
"Index": 43
|
||||
}
|
||||
},
|
||||
{
|
||||
"node": {
|
||||
"Name": "Barack Obama",
|
||||
"Index": 44
|
||||
}
|
||||
},
|
||||
{
|
||||
"node": {
|
||||
"Name": "Donald Trump",
|
||||
"Index": 45
|
||||
}
|
||||
},
|
||||
{
|
||||
"node": {
|
||||
"Name": "Joseph Biden",
|
||||
"Index": 46
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"extensions": {}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### React page
|
||||
|
||||
10) Create a new page `src/pages/pres.js` that displays the raw query result:
|
||||
8) Create a new file `src/pages/pres.js` that uses the query and displays the result:
|
||||
|
||||
```jsx title="src/pages/pres.js (create new file)"
|
||||
```jsx title="src/pages/pres.js"
|
||||
import { graphql } from "gatsby"
|
||||
import * as React from "react"
|
||||
|
||||
@ -447,10 +358,8 @@ const PageComponent = ({data}) => {
|
||||
export default PageComponent;
|
||||
```
|
||||
|
||||
Save the file.
|
||||
|
||||
11) Access the `/pres` page (typically `http://localhost:8000/pres` ) in a web
|
||||
browser. The displayed JSON is the data that the component receives:
|
||||
After saving the file, access `http://localhost:8000/pres` in the browser. The
|
||||
displayed JSON is the data that the component receives:
|
||||
|
||||
```js title="Expected contents of /pres"
|
||||
{
|
||||
@ -465,7 +374,7 @@ browser. The displayed JSON is the data that the component receives:
|
||||
// ....
|
||||
```
|
||||
|
||||
12) Change `PageComponent` to display a table based on the data:
|
||||
9) Change `PageComponent` to display a table based on the data:
|
||||
|
||||
```jsx title="src/pages/pres.js (replace PageComponent)"
|
||||
import { graphql } from "gatsby"
|
||||
@ -504,7 +413,7 @@ Going back to the browser, `http://localhost:8000/pres` will show a table:
|
||||
|
||||
### Live refresh
|
||||
|
||||
13) Open the file `src/data/pres.xlsx` in Excel or another spreadsheet editor.
|
||||
10) Open the file `src/data/pres.xlsx` in Excel or another spreadsheet editor.
|
||||
Add a new row at the end of the file, setting cell `A7` to "SheetJS Dev" and
|
||||
cell `B7` to `47`. The sheet should look like the following screenshot:
|
||||
|
||||
@ -516,7 +425,7 @@ Save the file and observe that the table has refreshed with the new data:
|
||||
|
||||
### Static site
|
||||
|
||||
14) Stop the development server and build the site:
|
||||
11) Stop the development server and build the site:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -525,6 +434,13 @@ npm run build
|
||||
The build output will confirm that the `/pres` route is static:
|
||||
|
||||
```text title="Output from GatsbyJS build process"
|
||||
Pages
|
||||
|
||||
┌ src/pages/404.js
|
||||
│ ├ /404/
|
||||
│ └ /404.html
|
||||
├ src/pages/index.js
|
||||
│ └ /
|
||||
└ src/pages/pres.js
|
||||
└ /pres/
|
||||
|
||||
@ -540,7 +456,7 @@ The build output will confirm that the `/pres` route is static:
|
||||
|
||||
The generated page will be placed in `public/pres/index.html`.
|
||||
|
||||
15) Open `public/pres/index.html` with a text editor and search for "SheetJS".
|
||||
12) Open `public/pres/index.html` with a text editor and search for "SheetJS".
|
||||
There will be a HTML row:
|
||||
|
||||
```html title="public/pres/index.html (Expected contents)"
|
||||
|
||||
@ -32,8 +32,8 @@ importing the SheetJS library in a browser script.
|
||||
|
||||
## ESBuild Loader
|
||||
|
||||
ESBuild releases starting from `0.9.1` support custom loader plugins. The loader
|
||||
receives an absolute path to the spreadsheet on the filesystem.
|
||||
ESBuild supports custom loader plugins. The loader receives an absolute path to
|
||||
the spreadsheet on the filesystem.
|
||||
|
||||
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from ESBuild loader plugins.
|
||||
@ -69,9 +69,9 @@ flowchart LR
|
||||
aoo --> |app.js\nfrontend code| html
|
||||
```
|
||||
|
||||
### ESBuild Configuration
|
||||
### ESBuild Config
|
||||
|
||||
Plugins can be referenced in the `plugins` array of the build configuration:
|
||||
Plugins can be referenced in the `plugins` array of the build config object:
|
||||
|
||||
```js title="build.mjs (structure)"
|
||||
import * as esbuild from 'esbuild'
|
||||
@ -200,28 +200,7 @@ document.body.appendChild(elt);
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| `esbuild` | Date |
|
||||
|:----------|:-----------|
|
||||
| `0.25.5` | 2025-06-20 |
|
||||
| `0.24.2` | 2025-06-20 |
|
||||
| `0.23.1` | 2025-06-20 |
|
||||
| `0.22.0` | 2025-06-20 |
|
||||
| `0.21.5` | 2025-06-20 |
|
||||
| `0.20.2` | 2025-06-20 |
|
||||
| `0.19.12` | 2025-06-20 |
|
||||
| `0.18.20` | 2025-06-20 |
|
||||
| `0.17.19` | 2025-06-20 |
|
||||
| `0.16.17` | 2025-06-20 |
|
||||
| `0.15.18` | 2025-06-20 |
|
||||
| `0.14.54` | 2025-06-20 |
|
||||
| `0.13.15` | 2025-06-20 |
|
||||
| `0.12.29` | 2025-06-20 |
|
||||
| `0.11.23` | 2025-06-20 |
|
||||
| `0.10.2` | 2025-06-20 |
|
||||
| `0.9.7` | 2025-06-20 |
|
||||
| `0.9.1` | 2025-06-20 |
|
||||
This demo was last tested on 2023 December 04 against ESBuild 0.19.8
|
||||
|
||||
:::
|
||||
|
||||
@ -233,7 +212,7 @@ This demo was tested in the following environments:
|
||||
mkdir sheetjs-esb
|
||||
cd sheetjs-esb
|
||||
npm init -y
|
||||
npm i --save esbuild@0.20.2
|
||||
npm i --save esbuild@0.19.8
|
||||
```
|
||||
|
||||
1) Install the SheetJS NodeJS module:
|
||||
@ -276,10 +255,10 @@ document.body.appendChild(elt);
|
||||
curl -LO https://docs.sheetjs.com/esbuild/build.mjs
|
||||
```
|
||||
|
||||
5) Download https://docs.sheetjs.com/pres.numbers to the project folder:
|
||||
5) Download <https://sheetjs.com/pres.numbers> to the project folder:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -LO https://sheetjs.com/pres.numbers
|
||||
```
|
||||
|
||||
### Static Site Test
|
||||
@ -295,7 +274,7 @@ The final script will be saved to `out.js`
|
||||
7) Start a local web server to host the project folder:
|
||||
|
||||
```bash
|
||||
npx -y http-server .
|
||||
npx http-server .
|
||||
```
|
||||
|
||||
The command will print a list of URLs.
|
||||
|
||||
@ -12,15 +12,15 @@ sidebar_custom_props:
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[ViteJS](https://vitejs.dev/) is a build tool for generating static websites. It
|
||||
has a robust JavaScript-powered plugin system[^1].
|
||||
[ViteJS](https://vitejs.dev/) is a modern build tool for generating static sites.
|
||||
It has a robust JavaScript-powered plugin system[^1]
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses ViteJS and SheetJS to pull data from a spreadsheet and display
|
||||
the content in an HTML table. We'll explore how to load SheetJS in a ViteJS
|
||||
plugin and evaluate data loading strategies.
|
||||
plugin and compare a few different data loading strategies.
|
||||
|
||||
The ["Complete Demo"](#complete-demo) section creates a complete website powered
|
||||
by a XLSX spreadsheet.
|
||||
@ -32,97 +32,16 @@ suitable for end of week or end of month (EOM) reports published in HTML tables.
|
||||
|
||||
For processing user-submitted files in the browser, the
|
||||
[ViteJS "Bundlers" demo](/docs/demos/frontend/bundler/vitejs) shows client-side
|
||||
bundling of SheetJS libraries. The ["ReactJS" demo](/docs/demos/frontend/react)
|
||||
bundling of the SheetJS library. The ["ReactJS" demo](/docs/demos/frontend/react)
|
||||
shows example sites using ViteJS with the ReactJS starter.
|
||||
|
||||
:::
|
||||
|
||||
## Plugins
|
||||
|
||||
ViteJS supports static asset imports[^2], but the default raw loader interprets
|
||||
data as UTF-8 strings. This corrupts binary formats including XLSX and XLS. A
|
||||
custom loader can bypass the raw loader and directly read files.
|
||||
|
||||
Since a custom loader must be used, some data processing work can be performed
|
||||
by the loader. Three approaches are explored in this demo.
|
||||
|
||||
The following diagrams show the ViteJS data flow. The pink "Main Script import"
|
||||
boxes represent the division between the loader and the main script. The green
|
||||
"SheetJS Operations" boxes represent the steps performed by SheetJS libraries.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>[HTML](#html-plugin)</th>
|
||||
<th>[Data](#pure-data-plugin)</th>
|
||||
<th>[Base64](#base64-plugin)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{verticalAlign: "top"}}>
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
file[(workbook\nfile)]
|
||||
buffer(NodeJS\nBuffer)
|
||||
sheetjs[[SheetJS Operations]]
|
||||
tabeller{{HTML\nString}}
|
||||
handoff[[Main Script import]]
|
||||
html{{HTML\nTABLE}}
|
||||
style handoff fill:#FFC7CE
|
||||
style sheetjs fill:#C6EFCE
|
||||
file --> buffer
|
||||
buffer --> sheetjs
|
||||
sheetjs --> tabeller
|
||||
tabeller --> handoff
|
||||
handoff --------> html
|
||||
```
|
||||
|
||||
</td>
|
||||
<td style={{verticalAlign: "top"}}>
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
file[(workbook\nfile)]
|
||||
buffer(NodeJS\nBuffer)
|
||||
sheetjs[[SheetJS Operations]]
|
||||
aoo(array of\nobjects)
|
||||
handoff[[Main Script import]]
|
||||
import(array of\nobjects)
|
||||
html{{HTML\nTABLE}}
|
||||
style handoff fill:#FFC7CE
|
||||
style sheetjs fill:#C6EFCE
|
||||
file --> buffer
|
||||
buffer --> sheetjs
|
||||
sheetjs --> aoo
|
||||
aoo --> handoff
|
||||
handoff ------> import
|
||||
import --> html
|
||||
```
|
||||
|
||||
</td>
|
||||
<td style={{verticalAlign: "top"}}>
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
file[(workbook\nfile)]
|
||||
base64(Base64\nString)
|
||||
handoff[[Main Script import]]
|
||||
import(Base64\nString)
|
||||
sheetjs[[SheetJS Operations]]
|
||||
aoo(array of\nobjects)
|
||||
html{{HTML\nTABLE}}
|
||||
style handoff fill:#FFC7CE
|
||||
style sheetjs fill:#C6EFCE
|
||||
file --> base64
|
||||
base64 ------> handoff
|
||||
handoff --> import
|
||||
import --> sheetjs
|
||||
sheetjs --> aoo
|
||||
aoo --> html
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
ViteJS supports static asset imports[^2], but the default raw loader interprets data
|
||||
as UTF-8 strings. This corrupts binary formats like XLSX and XLS, but a custom
|
||||
loader can override the default behavior.
|
||||
|
||||
For simple tables of data, ["Pure Data Plugin"](#pure-data-plugin) is strongly
|
||||
recommended. The file processing is performed at build time and the generated
|
||||
@ -132,9 +51,6 @@ For more complex parsing or display logic, ["Base64 Plugin"](#base64-plugin) is
|
||||
preferable. Since the raw parsing logic is performed in the page, the library
|
||||
will be included in the final bundle.
|
||||
|
||||
The ["HTML Plugin"](#html-plugin) generates HTML in the loader script. The
|
||||
SheetJS HTML writer renders merged cells and other features.
|
||||
|
||||
### Pure Data Plugin
|
||||
|
||||
For a pure static site, a plugin can load data into an array of row objects. The
|
||||
@ -156,7 +72,7 @@ flowchart LR
|
||||
```
|
||||
|
||||
This ViteJS plugin will read spreadsheets using the SheetJS `read` method[^3]
|
||||
and generate arrays of row objects with the SheetJS `sheet_to_json`[^4] method:
|
||||
and generate arrays of row objects with `sheet_to_json`[^4]:
|
||||
|
||||
```js title="vite.config.js"
|
||||
import { readFileSync } from 'fs';
|
||||
@ -169,27 +85,17 @@ export default defineConfig({
|
||||
plugins: [
|
||||
{ // this plugin handles ?sheetjs tags
|
||||
name: "vite-sheet",
|
||||
transform(_code, id) {
|
||||
transform(code, id) {
|
||||
if(!id.match(/\?sheetjs$/)) return;
|
||||
var wb = read(readFileSync(id.replace(/\?sheetjs$/, "")));
|
||||
var data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
return `export default JSON.parse('${JSON.stringify(data).replace(/\\/g, "\\\\")}')`;
|
||||
return `export default JSON.parse('${JSON.stringify(data)}')`;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
ViteJS plugins are expected to return strings representing ECMAScript modules.
|
||||
|
||||
The plugin uses `JSON.stringify` to encode the array of objects. The generated
|
||||
string is injected into the new module code. When ViteJS processes the module,
|
||||
`JSON.parse` recovers the array of objects.
|
||||
|
||||
:::
|
||||
|
||||
In frontend code, the loader will look for all modules with a `?sheetjs`
|
||||
query string. The default export is an array of row objects.
|
||||
|
||||
@ -209,75 +115,9 @@ document.querySelector('#app').innerHTML = `<table>
|
||||
</table>`;
|
||||
```
|
||||
|
||||
### HTML Plugin
|
||||
|
||||
A plugin can generate raw HTML strings that can be added to a page. The SheetJS
|
||||
libraries are used in the plugin but will not be added to the site.
|
||||
|
||||
The following diagram depicts the workbook waltz:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
file[(workbook\nfile)]
|
||||
subgraph SheetJS operations
|
||||
buffer(NodeJS\nBuffer)
|
||||
tavolo{{HTML\nString}}
|
||||
end
|
||||
html{{HTML\nTABLE}}
|
||||
file --> |vite.config.js\ncustom plugin| buffer
|
||||
buffer --> |vite.config.js\ncustom plugin| tavolo
|
||||
tavolo --> |main.js\nfrontend code| html
|
||||
```
|
||||
|
||||
This ViteJS plugin will read spreadsheets using the SheetJS `read` method[^5]
|
||||
and generate HTML using the SheetJS `sheet_to_html`[^6] method:
|
||||
|
||||
```js title="vite.config.js"
|
||||
import { readFileSync } from 'fs';
|
||||
import { read, utils } from 'xlsx';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
assetsInclude: ['**/*.xlsx'], // xlsx file should be treated as assets
|
||||
|
||||
plugins: [
|
||||
{ // this plugin handles ?html tags
|
||||
name: "vite-sheet-html",
|
||||
transform(_code, id) {
|
||||
if(!id.match(/\?html/)) return;
|
||||
var wb = read(readFileSync(id.replace(/\?html/, "")));
|
||||
var html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
return (`export default JSON.parse('${JSON.stringify(html).replace(/\\/g, "\\\\")}')`);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
ViteJS plugins are expected to return strings representing ECMAScript modules.
|
||||
|
||||
The plugin uses `JSON.stringify` to encode the HTML string. The generated string
|
||||
is injected into the new module code. When ViteJS processes the module,
|
||||
`JSON.parse` recovers the original HTML string.
|
||||
|
||||
:::
|
||||
|
||||
In frontend code, the loader will look for all modules with a `?html` query
|
||||
string. The default export is a string that can be directly added to the page.
|
||||
|
||||
The following example script sets the `innerHTML` property of the container:
|
||||
|
||||
```js title="main.js"
|
||||
import html from './data/pres.xlsx?html';
|
||||
|
||||
document.querySelector('#app').innerHTML = html;
|
||||
```
|
||||
|
||||
### Base64 Plugin
|
||||
|
||||
This plugin pulls in data as a Base64 string that can be read with `read`[^7].
|
||||
This plugin pulls in data as a Base64 string that can be read with `read`[^5].
|
||||
While this approach works, it is not recommended since it loads the library in
|
||||
the front-end site.
|
||||
|
||||
@ -309,7 +149,7 @@ export default defineConfig({
|
||||
plugins: [
|
||||
{ // this plugin handles ?b64 tags
|
||||
name: "vite-b64-plugin",
|
||||
transform(_code, id) {
|
||||
transform(code, id) {
|
||||
if(!id.match(/\?b64$/)) return;
|
||||
var path = id.replace(/\?b64/, "");
|
||||
var data = readFileSync(path, "base64");
|
||||
@ -321,7 +161,7 @@ export default defineConfig({
|
||||
```
|
||||
|
||||
When importing using the `b64` query, the raw Base64 string will be exposed.
|
||||
`read` will process the Base64 string using the `base64` input type[^8]:
|
||||
`read` will process the Base64 string using the `base64` input type[^6]:
|
||||
|
||||
```js title="main.js"
|
||||
import { read, utils } from "xlsx";
|
||||
@ -347,23 +187,22 @@ document.querySelector('#app').innerHTML = `<table>
|
||||
|
||||
## Complete Demo
|
||||
|
||||
The demo walks through the process of creating a new ViteJS website from scratch.
|
||||
A Git repository with the completed site can be cloned[^9].
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| ViteJS | Date |
|
||||
|:---------|:-----------|
|
||||
| `6.2.3` | 2025-03-30 |
|
||||
| `5.4.15` | 2025-03-30 |
|
||||
| `4.5.10` | 2025-03-30 |
|
||||
| `3.2.11` | 2025-03-30 |
|
||||
| `2.9.18` | 2025-03-30 |
|
||||
| `5.0.5` | 2023-12-04 |
|
||||
| `4.5.0` | 2023-12-04 |
|
||||
| `3.2.7` | 2023-12-04 |
|
||||
| `2.9.16` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
The demo walks through the process of creating a new ViteJS website from scratch.
|
||||
A Git repository with the completed site can be cloned[^7].
|
||||
|
||||
### Initial Setup
|
||||
|
||||
1) Create a new site with the `vue-ts` template and install the SheetJS package:
|
||||
@ -388,11 +227,11 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
curl -O https://docs.sheetjs.com/vitejs/vite.config.ts
|
||||
```
|
||||
|
||||
3) Make a `data` folder and download https://docs.sheetjs.com/pres.xlsx :
|
||||
3) Make a `data` folder and download <https://sheetjs.com/pres.xlsx> :
|
||||
|
||||
```bash
|
||||
mkdir -p data
|
||||
curl -L -o data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
curl -L -o data/pres.xlsx https://sheetjs.com/pres.xlsx
|
||||
```
|
||||
|
||||
### Pure Data Test
|
||||
@ -430,36 +269,15 @@ Save and refresh the page. A data table should be displayed
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npx -y http-server dist/
|
||||
npx http-server dist/
|
||||
```
|
||||
|
||||
The terminal will display a URL, typically `http://127.0.0.1:8080` . Access
|
||||
that page with a web browser.
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was tested against ViteJS `2.9.18`, the build failed:
|
||||
|
||||
```
|
||||
src/App.vue:8:3 - error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
|
||||
|
||||
8 <img alt="Vue logo" src="./assets/logo.png" />
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
```
|
||||
|
||||
**As it affects the project template, this is a bug in ViteJS.**
|
||||
|
||||
The simplest workaround is to force upgrade the `vue-tsc` dependency:
|
||||
|
||||
```bash
|
||||
npm i vue-tsc@latest
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
7) To confirm that only the raw data is present in the page, view the page
|
||||
source. The code will reference a script `/assets/index-HASH.js` where `HASH` is
|
||||
a string of characters. Open that script.
|
||||
source. The code will reference some script like `/assets/index-HASH.js`.
|
||||
Open that script.
|
||||
|
||||
Searching for `Bill Clinton` reveals the following:
|
||||
|
||||
@ -473,11 +291,11 @@ included in the final site!
|
||||
:::info pass
|
||||
|
||||
ViteJS also supports "Server-Side Rendering". In SSR, only the HTML table
|
||||
would be added to the final page. Details are covered in the ViteJS docs[^10].
|
||||
would be added to the final page. Details are covered in the ViteJS docs[^8].
|
||||
|
||||
:::
|
||||
|
||||
### HTML Test
|
||||
### Base64 Test
|
||||
|
||||
8) Run the dev server:
|
||||
|
||||
@ -485,88 +303,10 @@ would be added to the final page. Details are covered in the ViteJS docs[^10].
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open a browser window to the displayed URL (typically `http://localhost:5173` )
|
||||
Open a browser window to the displayed URL.
|
||||
|
||||
9) Replace the component `src/components/HelloWorld.vue` with:
|
||||
|
||||
```html title="src/components/HelloWorld.vue"
|
||||
<script setup lang="ts">
|
||||
// @ts-ignore
|
||||
import html from '../../data/pres.xlsx?html';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-html="html"></div>
|
||||
</template>
|
||||
```
|
||||
|
||||
Save and refresh the page. A data table should be displayed
|
||||
|
||||
10) Stop the dev server and build the site
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npx -y http-server dist/
|
||||
```
|
||||
|
||||
The terminal will display a URL, typically `http://127.0.0.1:8080` . Access
|
||||
that page with a web browser.
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was tested against ViteJS `2.9.18`, the build failed:
|
||||
|
||||
```
|
||||
src/App.vue:8:3 - error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
|
||||
|
||||
8 <img alt="Vue logo" src="./assets/logo.png" />
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
```
|
||||
|
||||
**As it affects the project template, this is a bug in ViteJS.**
|
||||
|
||||
The simplest workaround is to force upgrade the `vue-tsc` dependency:
|
||||
|
||||
```bash
|
||||
npm i vue-tsc@latest
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
11) To confirm that only the raw HTML is present in the page, view the page
|
||||
source. The code will reference a script `/assets/index-HASH.js` where `HASH` is
|
||||
a string of characters. Open that script.
|
||||
|
||||
Searching for `Bill Clinton` reveals the following encoded HTML element:
|
||||
|
||||
```
|
||||
<td data-t=\\"s\\" data-v=\\"Bill Clinton\\" id=\\"sjs-A2\\">Bill Clinton</td>
|
||||
```
|
||||
|
||||
Searching for `BESSELJ` should reveal no results. The SheetJS scripts are not
|
||||
included in the final site!
|
||||
|
||||
:::info pass
|
||||
|
||||
The HTML code is still stored in a script and is injected dynamically.
|
||||
|
||||
ViteJS "Server-Side Rendering" offers the option to render the site at build
|
||||
time, ensuring that the HTML table is directly added to the page.
|
||||
|
||||
:::
|
||||
|
||||
### Base64 Test
|
||||
|
||||
12) Run the dev server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open a browser window to the displayed URL (typically `http://localhost:5173` )
|
||||
|
||||
13) Replace the component `src/components/HelloWorld.vue` with:
|
||||
|
||||
```html title="src/components/HelloWorld.vue"
|
||||
<script setup lang="ts">
|
||||
// @ts-ignore
|
||||
@ -590,40 +330,19 @@ const data = utils.sheet_to_json<IPresident>(ws);
|
||||
</template>
|
||||
```
|
||||
|
||||
14) Stop the dev server and build the site
|
||||
10) Stop the dev server and build the site
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npx -y http-server dist/
|
||||
npx http-server dist/
|
||||
```
|
||||
|
||||
The terminal will display a URL ( `http://127.0.0.1:8080` ). Access that page
|
||||
with a web browser.
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was tested against ViteJS `2.9.18`, the build failed:
|
||||
|
||||
```
|
||||
src/App.vue:8:3 - error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
|
||||
|
||||
8 <img alt="Vue logo" src="./assets/logo.png" />
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
```
|
||||
|
||||
**As it affects the project template, this is a bug in ViteJS.**
|
||||
|
||||
The simplest workaround is to force upgrade the `vue-tsc` dependency:
|
||||
|
||||
```bash
|
||||
npm i vue-tsc@latest
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
15) To confirm that the object data is not present in the page, view the page
|
||||
source. The code will reference a script `/assets/index-HASH.js` where `HASH` is
|
||||
a string of characters. Open that script.
|
||||
11) To confirm that the object data is not present in the page, view the page
|
||||
source. The code will reference some script like `/assets/index-HASH.js` with
|
||||
a different hash from the previous test. Open that script.
|
||||
|
||||
Searching for `BESSELJ` should match the code:
|
||||
|
||||
@ -637,10 +356,8 @@ embedded in the final site and the data is parsed when the page is loaded.
|
||||
[^1]: See ["Using Plugins"](https://vitejs.dev/guide/using-plugins.html) in the ViteJS documentation.
|
||||
[^2]: See ["Static Asset Handling"](https://vitejs.dev/guide/assets.html) in the ViteJS documentation.
|
||||
[^3]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^4]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
|
||||
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^5]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^8]: See [the "base64" type in "Reading Files"](/docs/api/parse-options#input-type)
|
||||
[^9]: See [`examples/sheetjs-vite`](https://git.sheetjs.com/examples/sheetjs-vite/) on the SheetJS Git server.
|
||||
[^10]: See ["Server-Side Rendering"](https://vitejs.dev/guide/ssr.html) in the ViteJS documentation.
|
||||
[^6]: See [the "base64" type in "Reading Files"](/docs/api/parse-options#input-type)
|
||||
[^7]: See [`SheetJS/sheetjs-vite`](https://git.sheetjs.com/sheetjs/sheetjs-vite/) on the SheetJS git server.
|
||||
[^8]: See ["Server-Side Rendering"](https://vitejs.dev/guide/ssr.html) in the ViteJS documentation.
|
||||
@ -55,25 +55,11 @@ flowchart LR
|
||||
aoo --> |src/index.js\nfrontend code| html
|
||||
```
|
||||
|
||||
### Webpack Configuration
|
||||
### Webpack Config
|
||||
|
||||
The Webpack configuration is normally saved to `webpack.config.js`.
|
||||
A special rule should be added to `module.rules`:
|
||||
|
||||
#### Required Settings
|
||||
|
||||
`module.rules` is an array of rule objects that controls module synthesis.[^2]
|
||||
For the SheetJS Webpack integration, the following properties are required:
|
||||
|
||||
- `test` describes whether the rule is relevant. If the property is a regular
|
||||
expression, Webpack will test the filename against the `test` property.
|
||||
|
||||
- `use` lists the loaders that will process files matching the `test`. The
|
||||
loaders are specified using the `loader` property of the loader object.
|
||||
|
||||
The following example instructs Webpack to use the `sheetjs-loader.js` script
|
||||
when the file name ends in `.numbers` or `.xls` or `.xlsx` or `.xlsb`:
|
||||
|
||||
```js title="webpack.config.js (define loader)"
|
||||
```js title="webpack.config.js"
|
||||
// ...
|
||||
module.exports = {
|
||||
// ...
|
||||
@ -82,7 +68,7 @@ module.exports = {
|
||||
// highlight-start
|
||||
{
|
||||
/* `test` matches file extensions */
|
||||
test: /\.(numbers|xls|xlsx|xlsb)$/,
|
||||
test: /\.(numbers|xls|xlsx|xlsb)/,
|
||||
/* use the loader script */
|
||||
use: [ { loader: './sheetjs-loader' } ]
|
||||
}
|
||||
@ -92,21 +78,24 @@ module.exports = {
|
||||
};
|
||||
```
|
||||
|
||||
#### Recommended Settings
|
||||
Hot Module Replacement enables reloading when files are updated:
|
||||
|
||||
It is strongly recommended to enable other Webpack features:
|
||||
```js title="webpack.config.js"
|
||||
// ...
|
||||
module.exports = {
|
||||
// ...
|
||||
// highlight-start
|
||||
devServer: {
|
||||
static: './dist',
|
||||
hot: true,
|
||||
}
|
||||
// highlight-end
|
||||
};
|
||||
```
|
||||
|
||||
- `resolve.alias` defines path aliases. If data files are stored in one folder,
|
||||
an alias ensures that each page can reference the files using the same name[^3].
|
||||
It is strongly recommended to add an alias to simplify imports:
|
||||
|
||||
- `devServer.hot` enables "hot module replacement"[^4], ensuring that pages will
|
||||
refresh in development mode when spreadsheets are saved.
|
||||
|
||||
The following example instructs Webpack to treat `~` as the root of the project
|
||||
(so `~/data/pres.xlsx` refers to `pres.xlsx` in the data folder) and to enable
|
||||
live reloading:
|
||||
|
||||
```js title="webpack.config.js (other recommended settings)"
|
||||
```js title="webpack.config.js"
|
||||
// ...
|
||||
module.exports = {
|
||||
// ...
|
||||
@ -118,32 +107,21 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
// highlight-end
|
||||
// ...
|
||||
// highlight-start
|
||||
/* enable live reloading in development mode */
|
||||
devServer: { static: './dist', hot: true }
|
||||
// highlight-end
|
||||
};
|
||||
```
|
||||
|
||||
### SheetJS Loader
|
||||
|
||||
The SheetJS loader script must be saved to the script referenced in the Webpack
|
||||
configuration (`sheetjs-loader.js`).
|
||||
|
||||
As with [ViteJS](/docs/demos/static/vitejs), Webpack will interpret data as
|
||||
UTF-8 strings. This corrupts binary formats including XLSX and XLS. To suppress
|
||||
this behavior and instruct Webpack to pass a NodeJS `Buffer` object, the loader
|
||||
script must export a `raw` property that is set to `true`[^5].
|
||||
The SheetJS loader script must export a `raw` property that is set to `true`.
|
||||
|
||||
The base export is expected to be the loader function. The loader receives the
|
||||
file bytes as a Buffer, which can be parsed with the SheetJS `read` method[^6].
|
||||
`read` returns a SheetJS workbook object[^7].
|
||||
file bytes as a Buffer, which can be parsed with the SheetJS `read` method[^2].
|
||||
`read` returns a SheetJS workbook object[^3].
|
||||
|
||||
The loader in this demo will parse the workbook, pull the first worksheet, and
|
||||
generate an array of row objects using the `sheet_to_json` method[^8]:
|
||||
generate an array of row objects using the `sheet_to_json` method[^4]:
|
||||
|
||||
```js title="sheetjs-loader.js (Webpack loader)"
|
||||
```js title="sheetjs-loader.js"
|
||||
const XLSX = require("xlsx");
|
||||
|
||||
function loader(content) {
|
||||
@ -153,11 +131,8 @@ function loader(content) {
|
||||
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
return `export default JSON.parse('${JSON.stringify(data)}')`;
|
||||
}
|
||||
|
||||
/* ensure the function receives a Buffer */
|
||||
loader.raw = true;
|
||||
|
||||
/* export the loader */
|
||||
module.exports = loader;
|
||||
```
|
||||
|
||||
@ -166,7 +141,7 @@ module.exports = loader;
|
||||
Spreadsheets can be imported using the plugin. Assuming `pres.xlsx` is stored
|
||||
in the `data` subfolder, `~/data/pres.xlsx` can be imported from any script:
|
||||
|
||||
```js title="src/index.js (main script)"
|
||||
```js title="src/index.js"
|
||||
import data from '~/data/pres.xlsx';
|
||||
/* `data` is an array of objects from data/pres.xlsx */
|
||||
|
||||
@ -184,11 +159,7 @@ document.body.appendChild(elt);
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following deployments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `5.97.1` | 2025-01-08 |
|
||||
This demo was last tested on 2023 December 04 against Webpack 5.89.0
|
||||
|
||||
:::
|
||||
|
||||
@ -200,7 +171,7 @@ This demo was tested in the following deployments:
|
||||
mkdir sheetjs-wp5
|
||||
cd sheetjs-wp5
|
||||
npm init -y
|
||||
npm install webpack@5.97.1 webpack-cli@6.0.1 webpack-dev-server@5.2.0 --save
|
||||
npm install webpack@5.89.0 webpack-cli@5.1.4 webpack-dev-server@4.15.1 --save
|
||||
mkdir -p dist
|
||||
mkdir -p src
|
||||
mkdir -p data
|
||||
@ -264,7 +235,7 @@ module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(numbers|xls|xlsx|xlsb)$/,
|
||||
test: /\.(numbers|xls|xlsx|xlsb)/,
|
||||
use: [ { loader: './sheetjs-loader' } ]
|
||||
}
|
||||
]
|
||||
@ -289,10 +260,10 @@ loader.raw = true;
|
||||
module.exports = loader;
|
||||
```
|
||||
|
||||
6) Download https://docs.sheetjs.com/pres.xlsx and save to the `data` folder:
|
||||
6) Download <https://sheetjs.com/pres.xlsx> and save to the `data` folder:
|
||||
|
||||
```bash
|
||||
curl -L -o data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
curl -L -o data/pres.xlsx https://sheetjs.com/pres.xlsx
|
||||
```
|
||||
|
||||
### Live Reload Test
|
||||
@ -316,10 +287,9 @@ The terminal will print URLs for the development server:
|
||||
|
||||
It should display a table of Presidents with "Name" and "Index" columns
|
||||
|
||||
10) Add a new row to the spreadsheet (set `A7` to "SheetJS Dev" and `B7` to 47)
|
||||
and save the file.
|
||||
10) Add a new row to the spreadsheet and save the file.
|
||||
|
||||
After saving the file, the page should automatically refresh with the new data.
|
||||
Upon saving, the page should refresh with the new data.
|
||||
|
||||
### Static Site Test
|
||||
|
||||
@ -334,7 +304,7 @@ The final site will be placed in the `dist` folder.
|
||||
12) Start a local web server to host the `dist` folder:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
The command will print a list of URLs.
|
||||
@ -350,10 +320,6 @@ To verify that the data was added to the page, append `main.js` to the URL
|
||||
president names. It will not include SheetJS library references!
|
||||
|
||||
[^1]: See ["Plugins"](https://webpack.js.org/concepts/plugins/) in the Webpack documentation.
|
||||
[^2]: See [`module.rules`](https://webpack.js.org/configuration/module/#modulerules) in the Webpack documentation.
|
||||
[^3]: See [`resolve.alias`](https://webpack.js.org/configuration/resolve/#resolvealias) in the Webpack documentation.
|
||||
[^4]: See ["Hot Module Replacement"](https://webpack.js.org/concepts/hot-module-replacement/) in the Webpack documentation.
|
||||
[^5]: See ["Raw" Loader](https://webpack.js.org/api/loaders/#raw-loader) in the Webpack documentation.
|
||||
[^6]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^7]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^8]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
|
||||
@ -32,7 +32,6 @@ flowchart LR
|
||||
file --> |.eleventy.js\ncustom parser| buffer
|
||||
buffer --> |.eleventy.js\ncustom parser| aoo
|
||||
aoo --> |index.njk\ntemplate| html
|
||||
linkStyle 1 color:blue,stroke:blue;
|
||||
```
|
||||
|
||||
:::tip No Telemetry
|
||||
@ -92,8 +91,8 @@ module.exports = (eleventyConfig) => {
|
||||
Spreadsheet files added in the `_data` subdirectory are accessible from template
|
||||
files using the name stem.
|
||||
|
||||
For example, [`pres.numbers`](https://docs.sheetjs.com/pres.numbers) can be
|
||||
accessed using the variable `pres` in a template:
|
||||
For example, [`pres.numbers`](https://sheetjs.com/pres.numbers) can be accessed
|
||||
using the variable `pres` in a template:
|
||||
|
||||
```liquid title="index.njk"
|
||||
<table><thead><tr><th>Name</th><th>Index</th></tr></thead>
|
||||
@ -115,11 +114,10 @@ accessed using the variable `pres` in a template:
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Eleventy | Date |
|
||||
|:---------------|:-----------|
|
||||
| `2.0.1` | 2025-05-07 |
|
||||
| `3.0.0` | 2025-05-07 |
|
||||
| `3.1.0-beta.1` | 2025-05-07 |
|
||||
| Eleventy | Date |
|
||||
|:----------------|:-----------|
|
||||
| `2.0.1` | 2024-03-15 |
|
||||
| `3.0.0-alpha.5` | 2024-03-15 |
|
||||
|
||||
:::
|
||||
|
||||
@ -136,28 +134,28 @@ npm init -y
|
||||
2) Install Eleventy and SheetJS libraries:
|
||||
|
||||
<Tabs groupId="11ty">
|
||||
<TabItem value="2" label="2.x">
|
||||
<TabItem value="2" label="Stable">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @11ty/eleventy@2.0.1`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3" label="3.x">
|
||||
<TabItem value="3" label="Alpha">
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @11ty/eleventy@3.0.0`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @11ty/eleventy@3.0.0-alpha.5`}
|
||||
</CodeBlock>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
3) Make a new `_data` subdirectory in the project. Download the example file
|
||||
[`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) into `_data`:
|
||||
[`pres.xlsx`](https://sheetjs.com/pres.xlsx) into `_data`:
|
||||
|
||||
```bash
|
||||
mkdir _data
|
||||
curl -Lo _data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
curl -Lo _data/pres.xlsx https://sheetjs.com/pres.xlsx
|
||||
```
|
||||
|
||||
4) Download the following files to the project folder:
|
||||
@ -214,7 +212,7 @@ Eleventy will place the generated site in the `_site` subfolder.
|
||||
9) Start a web server to host the static site:
|
||||
|
||||
```bash
|
||||
npx -y http-server _site
|
||||
npx http-server _site
|
||||
```
|
||||
|
||||
Open a web browser and access the displayed URL ( `http://localhost:8080` ).
|
||||
|
||||
@ -31,18 +31,18 @@ The ["Demo"](#demo) uses NextJS and SheetJS to pull data from a spreadsheet.
|
||||
We'll explore how to create asset modules that process spreadsheet data at build
|
||||
time and how to read files on the server in NextJS lifecycle methods.
|
||||
|
||||
:::danger Telemetry
|
||||
:::warning Telemetry
|
||||
|
||||
NextJS collects telemetry by default. The `telemetry` subcommand can disable it:
|
||||
|
||||
```js
|
||||
npx -y next@13.5.6 telemetry disable
|
||||
npx next@13.5.6 telemetry disable
|
||||
```
|
||||
|
||||
The setting can be verified by running
|
||||
|
||||
```js
|
||||
npx -y next@13.5.6 telemetry status
|
||||
npx next@13.5.6 telemetry status
|
||||
```
|
||||
|
||||
:::
|
||||
@ -75,27 +75,12 @@ This demo was tested in the following environments:
|
||||
|
||||
| NextJS | NodeJS | Date |
|
||||
|:----------|:----------|:-----------|
|
||||
| ` 9.5.5` | `16.20.2` | 2025-04-24 |
|
||||
| `10.2.3` | `16.20.2` | 2025-04-24 |
|
||||
| `11.1.4` | `16.20.2` | 2025-04-24 |
|
||||
| `12.3.7` | `20.14.0` | 2025-04-24 |
|
||||
| `13.5.11` | `20.14.0` | 2025-04-24 |
|
||||
| `14.2.28` | `20.14.0` | 2025-04-24 |
|
||||
| `15.3.1` | `20.18.0` | 2025-04-24 |
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
SheetJS libraries work in legacy NextJS apps. Older versions of this demo have
|
||||
been tested against versions `3.2.3`, `4.2.3`, `5.1.0`, `6.1.2` and `7.0.3`.
|
||||
|
||||
NextJS has made a number of breaking changes over the years. Older versions of
|
||||
NextJS use legacy versions of ReactJS that do not support function components
|
||||
and other idioms.
|
||||
|
||||
[`examples/reactjs-legacy`](https://git.sheetjs.com/examples/reactjs-legacy) on
|
||||
the SheetJS Git server includes code samples for legacy NextJS versions.
|
||||
| ` 9.5.5` | `16.20.2` | 2023-12-04 |
|
||||
| `10.2.3` | `16.20.2` | 2023-12-04 |
|
||||
| `11.1.4` | `16.20.2` | 2023-12-04 |
|
||||
| `12.3.4` | `20.10.0` | 2023-12-04 |
|
||||
| `13.5.6` | `20.10.0` | 2023-12-04 |
|
||||
| `14.0.3` | `20.10.0` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -114,7 +99,7 @@ but does not support live reloading.
|
||||
:::caution pass
|
||||
|
||||
When the demo was last tested, Turbopack did not support true raw loaders. For
|
||||
development use, the normal `npx -y next dev` should be used.
|
||||
development use, the normal `npx next dev` should be used.
|
||||
|
||||
:::
|
||||
|
||||
@ -211,7 +196,7 @@ export async function getStaticProps() {
|
||||
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from page scripts.
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
[The SheetJS ESM build](/docs/getting-started/installation/nodejs#esm-import)
|
||||
does not load NodeJS native modules directly. The Installation section includes
|
||||
@ -258,7 +243,7 @@ export async function getServerSideProps() {
|
||||
}
|
||||
```
|
||||
|
||||
:::danger Reading and writing files during the build process
|
||||
:::warning Reading and writing files during the build process
|
||||
|
||||
As the NextJS workaround is non-traditional, it bears repeating:
|
||||
|
||||
@ -569,13 +554,13 @@ When upgrading NextJS is not an option, NodeJS should be downgraded to v16.
|
||||
0) Disable NextJS telemetry:
|
||||
|
||||
```js
|
||||
npx -y next@13.5.6 telemetry disable
|
||||
npx next@13.5.6 telemetry disable
|
||||
```
|
||||
|
||||
Confirm it is disabled by running
|
||||
|
||||
```js
|
||||
npx -y next@13.5.6 telemetry status
|
||||
npx next@13.5.6 telemetry status
|
||||
```
|
||||
|
||||
1) Set up folder structure. At the end, a `pages` folder with a `sheets`
|
||||
@ -584,7 +569,6 @@ npx -y next@13.5.6 telemetry status
|
||||
```bash
|
||||
mkdir sheetjs-next
|
||||
cd sheetjs-next
|
||||
npm init -y
|
||||
mkdir -p pages/sheets/
|
||||
```
|
||||
|
||||
@ -667,7 +651,7 @@ cd ../..
|
||||
6) Test the deployment:
|
||||
|
||||
```bash
|
||||
npx -y next
|
||||
npx next
|
||||
```
|
||||
|
||||
Open a web browser and access:
|
||||
@ -693,7 +677,7 @@ After saving the file, the website should refresh with the new row.
|
||||
8) Stop the server and run a production build:
|
||||
|
||||
```bash
|
||||
npx -y next build
|
||||
npx next build
|
||||
```
|
||||
|
||||
The final output will show a list of the routes and types:
|
||||
@ -715,26 +699,19 @@ As explained in the summary, the `/getStaticPaths` and `/getStaticProps` routes
|
||||
are completely static. 2 `/sheets/#` pages were generated, corresponding to 2
|
||||
worksheets in the file. `/getServerSideProps` is server-rendered.
|
||||
|
||||
:::info pass
|
||||
|
||||
NextJS historically used lowercase Lambda (`λ`) to denote dynamic paths. This
|
||||
was changed to a stylized lowercase F (`ƒ`) in recent versions of NextJS.
|
||||
|
||||
:::
|
||||
|
||||
9) Try to build a static site:
|
||||
|
||||
<Tabs groupId="nextver">
|
||||
<TabItem value="13" label="NextJS 9 - 13">
|
||||
|
||||
```bash
|
||||
npx -y next export
|
||||
npx next export
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="14" label="NextJS 14">
|
||||
|
||||
:::danger NextJS breaking changes
|
||||
:::warning NextJS breaking changes
|
||||
|
||||
**NextJS 14 removed the `export` subcommand!**
|
||||
|
||||
@ -753,7 +730,7 @@ module.exports = {
|
||||
After adding the line, run the `build` command:
|
||||
|
||||
```bash
|
||||
npx -y next build
|
||||
npx next build
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -764,13 +741,7 @@ This build will fail. A static page cannot be generated at this point because
|
||||
|
||||
### Static Site
|
||||
|
||||
10) Delete `pages/getServerSideProps.js`:
|
||||
|
||||
```bash
|
||||
rm -f pages/getServerSideProps.js
|
||||
```
|
||||
|
||||
11) Rebuild the static site:
|
||||
10) Delete `pages/getServerSideProps.js` and rebuild:
|
||||
|
||||
<Tabs groupId="nextver">
|
||||
<TabItem value="13" label="NextJS 9 - 13">
|
||||
@ -787,16 +758,17 @@ module.exports = {
|
||||
webpack: (config) => {
|
||||
```
|
||||
|
||||
After editing `next.config.js`, run the build command:
|
||||
After editing `next.config.js`:
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
```bash
|
||||
npx -y next build
|
||||
rm -f pages/getServerSideProps.js
|
||||
npx next build
|
||||
```
|
||||
|
||||
Inspecting the output, there should be no lines with `λ` or `ƒ`:
|
||||
Inspecting the output, there should be no lines with the `λ` symbol:
|
||||
|
||||
```
|
||||
Route (pages) Size First Load JS
|
||||
@ -810,13 +782,13 @@ Route (pages) Size First Load JS
|
||||
└ /sheets/1
|
||||
```
|
||||
|
||||
12) Generate the static site:
|
||||
11) Generate the static site:
|
||||
|
||||
<Tabs groupId="nextver">
|
||||
<TabItem value="13" label="NextJS 9 - 13">
|
||||
|
||||
```bash
|
||||
npx -y next export
|
||||
npx next export
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -834,7 +806,7 @@ module.exports = {
|
||||
After adding the line, run the `build` command:
|
||||
|
||||
```bash
|
||||
npx -y next build
|
||||
npx next build
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -842,10 +814,10 @@ npx -y next build
|
||||
|
||||
The static site will be written to the `out` subfolder
|
||||
|
||||
13) Serve the static site:
|
||||
12) Serve the static site:
|
||||
|
||||
```bash
|
||||
npx -y http-server out
|
||||
npx http-server out
|
||||
```
|
||||
|
||||
The command will start a local HTTP server at `http://localhost:8080/` for
|
||||
@ -853,16 +825,16 @@ testing the generated site. Note that `/getServerSideProps` will 404 since the
|
||||
page was removed.
|
||||
|
||||
[^1]: See the ["Webpack" asset module demo](/docs/demos/static/webpack) for more details.
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options).
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See ["SheetJS Data Model"](/docs/csf/) for more details.
|
||||
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output).
|
||||
[^5]: See [`readFile` in "Reading Files"](/docs/api/parse-options).
|
||||
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output).
|
||||
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^5]: See [`readFile` in "Reading Files"](/docs/api/parse-options)
|
||||
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^7]: See [`getStaticProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-props) in the NextJS documentation.
|
||||
[^8]: See [`getStaticPaths`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-paths) in the NextJS documentation.
|
||||
[^9]: See [`getServerSideProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props) in the NextJS documentation.
|
||||
[^10]: See [`fallback` in `getStaticPaths`](https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-false) in the NextJS documentation.
|
||||
[^10]: See [`fallback` in getStaticPaths](https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-false) in the NextJS documentation.
|
||||
[^11]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
|
||||
[^12]: [`dangerouslySetInnerHTML`](https://react.dev/reference/react-dom/components/common#common-props) is a ReactJS prop supported for all built-in components.
|
||||
[^13]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output).
|
||||
[^14]: See ["Array of Objects" in the ReactJS demo](/docs/demos/frontend/react#rendering-data).
|
||||
[^13]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
[^14]: See ["Array of Objects" in the ReactJS demo](/docs/demos/frontend/react#rendering-data)
|
||||
|
||||
@ -33,9 +33,6 @@ Content v1 (paired with VueJS 2.x and NuxtJS 2.x)
|
||||
The ["Nuxt Content v2"](#nuxt-content-v2) section explores "transformers" for
|
||||
NuxtJS Content v2 (paired with VueJS 3.x and NuxtJS 3.x)
|
||||
|
||||
The ["Nuxt Content v3"](#nuxt-content-v3) section explores raw ViteJS modules
|
||||
(paired with VueJS 3.x and NuxtJS 3.x)
|
||||
|
||||
:::info pass
|
||||
|
||||
This demo focuses on server-side processing with NuxtJS and VueJS.
|
||||
@ -49,23 +46,22 @@ that process spreadsheets in the browser.
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Nuxt Content | Nuxt | Date |
|
||||
|:-------------|:-----------|:-----------|
|
||||
| `1.15.1` | `2.18.1` | 2025-04-23 |
|
||||
| `2.13.4` | `3.17.2` | 2025-05-12 |
|
||||
| `3.5.1` | `3.17.3` | 2025-05-18 |
|
||||
| Nuxt Content | Nuxt | Date |
|
||||
|:-------------|:---------|:-----------|
|
||||
| `1.15.1` | `2.17.2` | 2023-12-04 |
|
||||
| `2.9.0` | `3.8.2` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
:::danger Telemetry
|
||||
:::warning Telemetry
|
||||
|
||||
Nuxt embeds telemetry. According to the docs, it can be disabled with:
|
||||
|
||||
```bash
|
||||
npx -y nuxt telemetry disable
|
||||
npx nuxt telemetry disable
|
||||
```
|
||||
|
||||
**When the demo was last tested, this command did not work.**
|
||||
**At the time the demo was last tested, this command did not work.**
|
||||
|
||||
Disabling telemetry requires a few steps:
|
||||
|
||||
@ -104,14 +100,6 @@ Click "OK" in each window (3 windows) and restart your computer.
|
||||
telemetry.enabled=false
|
||||
```
|
||||
|
||||
The following command can be run in the Linux / MacOS terminal:
|
||||
|
||||
```bash
|
||||
cat >~/.nuxtrc <<EOF
|
||||
telemetry.enabled=false
|
||||
EOF
|
||||
```
|
||||
|
||||
3) For Nuxt 3 sites, set the `telemetry` option in the Nuxt config file (either `nuxt.config.ts` or `nuxt.config.js`):
|
||||
|
||||
```js title="nuxt.config.js"
|
||||
@ -272,23 +260,23 @@ The recommended solution is to switch to Node 18.
|
||||
1) Create a stock app:
|
||||
|
||||
```bash
|
||||
npx -y create-nuxt-app@4.0.0 sheetjs-nuxt
|
||||
npx create-nuxt-app@4.0.0 sheetjs-nuxt
|
||||
```
|
||||
|
||||
When prompted, enter the following options:
|
||||
|
||||
- `Project name`: press <kbd>Enter</kbd> (use default `sheetjs-nuxt`)
|
||||
- `Programming language`: press <kbd>↓</kbd> (`TypeScript` selected) then <kbd>Enter</kbd>
|
||||
- `Package manager`: select `Npm` and press <kbd>Enter</kbd>
|
||||
- `UI framework`: select `None` and press <kbd>Enter</kbd>
|
||||
- `Nuxt.js modules`: scroll to `Content`, select with <kbd>Space</kbd>, then press <kbd>Enter</kbd>
|
||||
- `Linting tools`: press <kbd>Enter</kbd> (do not select any Linting tools)
|
||||
- `Testing framework`: select `None` and press <kbd>Enter</kbd>
|
||||
- `Rendering mode`: select `Universal (SSR / SSG)` and press <kbd>Enter</kbd>
|
||||
- `Deployment target`: select `Static (Static/Jamstack hosting)` and press <kbd>Enter</kbd>
|
||||
- `Development tools`: press <kbd>Enter</kbd> (do not select any Development tools)
|
||||
- `What is your GitHub username?`: press <kbd>Enter</kbd> (use default)
|
||||
- `Version control system`: select `None` and press <kbd>Enter</kbd>
|
||||
- `Project name`: press Enter (use default `sheetjs-nuxt`)
|
||||
- `Programming language`: press Down Arrow (`TypeScript` selected) then Enter
|
||||
- `Package manager`: select `Npm` and press Enter
|
||||
- `UI framework`: select `None` and press Enter
|
||||
- `Nuxt.js modules`: scroll to `Content`, select with Space, then press Enter
|
||||
- `Linting tools`: press Enter (do not select any Linting tools)
|
||||
- `Testing framework`: select `None` and press Enter
|
||||
- `Rendering mode`: select `Universal (SSR / SSG)` and press Enter
|
||||
- `Deployment target`: select `Static (Static/Jamstack hosting)` and press Enter
|
||||
- `Development tools`: press Enter (do not select any Development tools)
|
||||
- `What is your GitHub username?`: press Enter
|
||||
- `Version control system`: select `None`
|
||||
|
||||
The project will be configured and modules will be installed.
|
||||
|
||||
@ -308,17 +296,17 @@ When the build finishes, the terminal will display a URL like:
|
||||
|
||||
The server is listening on that URL. Open the link in a web browser.
|
||||
|
||||
3) Download https://docs.sheetjs.com/pres.xlsx and move to the `content` folder.
|
||||
3) Download <https://sheetjs.com/pres.xlsx> and move to the `content` folder.
|
||||
|
||||
```bash
|
||||
curl -L -o content/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
curl -L -o content/pres.xlsx https://sheetjs.com/pres.xlsx
|
||||
```
|
||||
|
||||
4) Modify `nuxt.config.js` as follows:
|
||||
|
||||
- Add the following to the top of the script:
|
||||
|
||||
```js title="nuxt.config.js (add to top)"
|
||||
```js
|
||||
import { readFile, utils } from 'xlsx';
|
||||
|
||||
// This will be called when the files change
|
||||
@ -339,7 +327,7 @@ const parseSheet = (file, { path }) => {
|
||||
|
||||
Replace the property with the following definition:
|
||||
|
||||
```js title="nuxt.config.js (replace content key in object)"
|
||||
```js
|
||||
// content.extendParser allows us to hook into the parsing step
|
||||
content: {
|
||||
extendParser: {
|
||||
@ -356,7 +344,7 @@ Replace the property with the following definition:
|
||||
|
||||
5) Replace `pages/index.vue` with the following:
|
||||
|
||||
```html title="pages/index.vue (replace contents)"
|
||||
```html title="pages/index.vue"
|
||||
<!-- sheetjs (C) 2013-present SheetJS -- https://sheetjs.com -->
|
||||
<template><div>
|
||||
<div v-for="item in data.data" v-bind:key="item.name">
|
||||
@ -403,31 +391,21 @@ The page should automatically refresh with the new content:
|
||||
|
||||

|
||||
|
||||
7) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
8) Build the static site:
|
||||
7) Stop the server (press `CTRL+C` in the terminal window) and run
|
||||
|
||||
```bash
|
||||
npm run generate
|
||||
```
|
||||
|
||||
This will create a static site in the `dist` folder.
|
||||
|
||||
9) Serve the static site:
|
||||
This will create a static site in the `dist` folder, which can be served with:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) in a web browser.
|
||||
|
||||
To confirm that the spreadsheet data is added to the site, view the page source.
|
||||
|
||||
Searching for `Bill Clinton` reveals the following encoded HTML row:
|
||||
|
||||
```html
|
||||
<tr><td>Bill Clinton</td> <td>42</td></tr>
|
||||
```
|
||||
Accessing the page `http://localhost:8080` will show the page contents. Verifying
|
||||
the static nature is trivial: make another change in Excel and save. The page
|
||||
will not change.
|
||||
|
||||
## Nuxt Content v2
|
||||
|
||||
@ -503,22 +481,11 @@ export default defineTransformer({
|
||||
The data object returned by the transformer must have the original `_id` key.
|
||||
The data is stored in the `body` property of the final object.
|
||||
|
||||
:::danger pass
|
||||
|
||||
When this demo was last tested, there were errors in the NuxtJS types.
|
||||
|
||||
**As this affects the official examples, this is a bug in NuxtJS!**
|
||||
|
||||
Until the bugs are fixed, type checking should be disabled.
|
||||
|
||||
:::
|
||||
|
||||
### Custom Modules
|
||||
|
||||
NuxtJS modules are the main mechanism for adding transformers to the pipeline.
|
||||
|
||||
<details>
|
||||
<summary><b>Module Details</b> (click to show)</summary>
|
||||
<details><summary><b>Module Details</b> (click to show)</summary>
|
||||
|
||||
Due to the structure of the NuxtJS system, modules must be defined in separate
|
||||
script files. The module script is expected to export a module configured with
|
||||
@ -529,8 +496,8 @@ script files. The module script is expected to export a module configured with
|
||||
- Add the transformer to Nuxt Content in the `content:context` hook
|
||||
|
||||
```js title="sheetmodule.ts (Module)"
|
||||
import { resolve } from 'path';
|
||||
import { defineNuxtModule } from '@nuxt/kit';
|
||||
import { resolve } from 'path'
|
||||
import { defineNuxtModule } from '@nuxt/kit'
|
||||
|
||||
export default defineNuxtModule({
|
||||
/* module setup method */
|
||||
@ -553,7 +520,7 @@ The module must be loaded in `nuxt.config.ts` and added to the `modules` array:
|
||||
|
||||
```ts title="nuxt.config.ts"
|
||||
// highlight-next-line
|
||||
import SheetJSModule from './sheetmodule';
|
||||
import SheetJSModule from './sheetmodule'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
// @ts-ignore
|
||||
@ -610,7 +577,7 @@ from the script setup will be shaped like the return value from the transformer.
|
||||
:::caution pass
|
||||
|
||||
For some older versions, parts of the Nuxt dependency tree did not support
|
||||
NodeJS version 20. If the `pnpm install` step fails with a message like
|
||||
NodeJS version 20. If the `yarn install` step fails with a message like
|
||||
|
||||
```
|
||||
error @nuxt/kit@3.4.1: The engine "node" is incompatible with this module.
|
||||
@ -623,18 +590,17 @@ The recommended solution is to switch to Node 18.
|
||||
1) Create a stock app and install dependencies:
|
||||
|
||||
```bash
|
||||
npx -y nuxi init -t content --packageManager pnpm --no-gitInit sheetjs-nc2 -M ,
|
||||
npx -y nuxi init -t content sheetjs-nc2
|
||||
cd sheetjs-nc2
|
||||
npx -y pnpm install
|
||||
npx -y pnpm install @nuxt/content@2 --save
|
||||
npx -y pnpm install @types/node @nuxt/kit --save
|
||||
npx -y yarn install
|
||||
npx -y yarn add --dev @types/node
|
||||
```
|
||||
|
||||
2) Install the SheetJS library and start the server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npx -y pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npx -y pnpm dev`}
|
||||
npx -y yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npx -y yarn dev`}
|
||||
</CodeBlock>
|
||||
|
||||
|
||||
@ -644,22 +610,22 @@ When the build finishes, the terminal will display a URL like:
|
||||
> Local: http://localhost:3000/
|
||||
```
|
||||
|
||||
The server is listening on that URL. Open the link in a web browser.
|
||||
The server is listening on that URL. Open the link in a web browser.
|
||||
|
||||
3) Download https://docs.sheetjs.com/pres.xlsx and move to the `content` folder.
|
||||
3) Download <https://sheetjs.com/pres.xlsx> and move to the `content` folder.
|
||||
|
||||
```bash
|
||||
curl -L -o content/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
curl -L -o content/pres.xlsx https://sheetjs.com/pres.xlsx
|
||||
```
|
||||
|
||||
4) Create the transformer. Two files must be saved at the root of the project:
|
||||
|
||||
- [`sheetformer.ts`](https://docs.sheetjs.com/nuxt/2/sheetformer.ts) (raw transformer module)
|
||||
- [`sheetformer.ts`](https://docs.sheetjs.com/nuxt/3/sheetformer.ts) (raw transformer module)
|
||||
- [`sheetmodule.ts`](https://docs.sheetjs.com/nuxt/3/sheetmodule.ts) (Nuxt configuration module)
|
||||
|
||||
```bash
|
||||
curl -O https://docs.sheetjs.com/nuxt/2/sheetformer.ts
|
||||
curl -O https://docs.sheetjs.com/nuxt/2/sheetmodule.ts
|
||||
curl -O https://docs.sheetjs.com/nuxt/3/sheetformer.ts
|
||||
curl -O https://docs.sheetjs.com/nuxt/3/sheetmodule.ts
|
||||
```
|
||||
|
||||
After creating the source files, the module must be added to `nuxt.config.ts`:
|
||||
@ -673,71 +639,71 @@ export default defineNuxtConfig({
|
||||
// @ts-ignore
|
||||
telemetry: false,
|
||||
// highlight-end
|
||||
devtools: { enabled: true },
|
||||
// highlight-start
|
||||
modules: [
|
||||
// highlight-next-line
|
||||
SheetJSModule,
|
||||
'@nuxt/content'
|
||||
],
|
||||
devtools: { enabled: true },
|
||||
// ...
|
||||
content: {}
|
||||
// highlight-end
|
||||
});
|
||||
```
|
||||
|
||||
Stop the dev server (<kbd>CTRL</kbd>+<kbd>C</kbd>) and run the following:
|
||||
Restart the dev server by exiting the process (Control+C) and running:
|
||||
|
||||
```bash
|
||||
npx -y nuxi clean
|
||||
npx -y nuxi cleanup
|
||||
npx -y pnpm run dev
|
||||
npx -y nuxi typecheck
|
||||
npx -y yarn run dev
|
||||
```
|
||||
|
||||
5) Download [`pres.vue`](pathname:///nuxt/2/pres.vue) and save to `app/pages`:
|
||||
Loading `http://localhost:3000/pres` should show some JSON data:
|
||||
|
||||
```json
|
||||
{
|
||||
// ...
|
||||
"data": {
|
||||
"_path": "/pres",
|
||||
// ...
|
||||
"_id": "content:pres.xlsx",
|
||||
"body": [
|
||||
{
|
||||
"name": "Sheet1", // <-- sheet name
|
||||
"data": [ // <-- array of data objects
|
||||
{
|
||||
"Name": "Bill Clinton",
|
||||
"Index": 42
|
||||
},
|
||||
```
|
||||
|
||||
5) Download [`pres.vue`](pathname:///nuxt/3/pres.vue) and save to `pages`:
|
||||
|
||||
```bash
|
||||
curl -o app/pages/pres.vue https://docs.sheetjs.com/nuxt/2/pres.vue
|
||||
curl -o pages/pres.vue https://docs.sheetjs.com/nuxt/3/pres.vue
|
||||
```
|
||||
|
||||
Stop the dev server (<kbd>CTRL</kbd>+<kbd>C</kbd>) and run the following:
|
||||
Restart the dev server by exiting the process (Control+C) and running:
|
||||
|
||||
```bash
|
||||
npx -y nuxi clean
|
||||
npx -y nuxi cleanup
|
||||
npx -y pnpm run dev
|
||||
npx -y yarn run dev
|
||||
```
|
||||
|
||||
6) From the browser window in step 2, access `/pres` from the site. For example,
|
||||
if the URL in step 2 was `http://localhost:3000/`, the new page should be
|
||||
`http://localhost:3000/pres`.
|
||||
The browser should now display an HTML table.
|
||||
|
||||
This page should now display an HTML table.
|
||||
|
||||
7) To verify that hot loading works, open `pres.xlsx` from the `content` folder
|
||||
with a spreadsheet editor.
|
||||
6) To verify that hot loading works, open `pres.xlsx` from the `content` folder
|
||||
with Excel or another spreadsheet editor.
|
||||
|
||||
Set cell `A7` to "SheetJS Dev" and set `B7` to `47`. Save the spreadsheet.
|
||||
|
||||
The page should automatically refresh with the new content.
|
||||
|
||||
8) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
9) Copy `app/pages/pres.vue` to `app/pages/index.vue`:
|
||||
7) Stop the server (press `CTRL+C` in the terminal window) and run
|
||||
|
||||
```bash
|
||||
cp app/pages/pres.vue app/pages/index.vue
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
In older test runs, the Nuxt starter created a default `/` page. The most recent
|
||||
test did not create the index page, resulting in build errors. This step ensures
|
||||
some sort of index page exists.
|
||||
|
||||
:::
|
||||
|
||||
10) Build the static site:
|
||||
|
||||
```bash
|
||||
npx -y pnpm run generate
|
||||
npx -y yarn run generate
|
||||
```
|
||||
|
||||
This will create a static site in `.output/public`, which can be served with:
|
||||
@ -746,220 +712,9 @@ This will create a static site in `.output/public`, which can be served with:
|
||||
npx -y http-server .output/public
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) in a web browser.
|
||||
|
||||
To confirm that the spreadsheet data is added to the site, view the page source.
|
||||
|
||||
Searching for `Bill Clinton` reveals the following encoded HTML row:
|
||||
|
||||
```html
|
||||
<tr><td>Bill Clinton</td><td>42</td></tr>
|
||||
```
|
||||
|
||||
## Nuxt Content v3
|
||||
|
||||
:::danger pass
|
||||
|
||||
When this demo was last tested, the official Nuxt Content v3 custom transformers
|
||||
and custom collections examples did not work.
|
||||
|
||||
:::
|
||||
|
||||
[ViteJS modules](/docs/demos/static/vitejs) can be used in Nuxt v3.
|
||||
|
||||
### Installation
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
safely imported from `nuxt.config.ts` or transformer or module scripts. As long
|
||||
as the SheetJS modules are not imported in the various `.vue` pages, the library
|
||||
will not be added to the final page bundle!
|
||||
|
||||
### Configuration
|
||||
|
||||
The `vite` property in the NuxtJS config is passed to ViteJS. Plugins and other
|
||||
configuration options can be copied to the object. `vite.config.js` for the
|
||||
[Pure Data Plugin](/docs/demos/static/vitejs#pure-data-plugin) is shown below:
|
||||
|
||||
```js title="vite.config.js (raw ViteJS)"
|
||||
import { readFileSync } from 'fs';
|
||||
import { read, utils } from 'xlsx';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
assetsInclude: ['**/*.xlsx'], // xlsx file should be treated as assets
|
||||
|
||||
plugins: [
|
||||
{ // this plugin handles ?sheetjs tags
|
||||
name: "vite-sheet",
|
||||
transform(_code, id) {
|
||||
if(!id.match(/\?sheetjs$/)) return;
|
||||
var wb = read(readFileSync(id.replace(/\?sheetjs$/, "")));
|
||||
var data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
return `export default JSON.parse('${JSON.stringify(data).replace(/\\/g, "\\\\")}')`;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
The `assetsInclude` and `plugins` properties should be added within the `vite`
|
||||
property in the object passed to `defineNuxtConfig`.
|
||||
|
||||
:::danger pass
|
||||
|
||||
NuxtJS does not properly honor the `?sheetjs` tag! As a result, the transform
|
||||
explicitly tests for the `.xlsx` extension.
|
||||
|
||||
:::
|
||||
|
||||
```ts title="nuxt.config.ts"
|
||||
import { readFileSync } from 'fs';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
export default defineNuxtConfig({
|
||||
// highlight-next-line
|
||||
vite: { // these options are passed to ViteJS
|
||||
assetsInclude: ['**/*.xlsx'], // xlsx file should be treated as assets
|
||||
|
||||
plugins: [
|
||||
{ // this plugin handles .xlsx
|
||||
name: "vite-sheet",
|
||||
transform(_code, id) {
|
||||
if(!id.match(/\.xlsx$/)) return;
|
||||
var wb = read(readFileSync(id.replace(/\?sheetjs$/, "")));
|
||||
var data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
return `export default JSON.parse('${JSON.stringify(data).replace(/\\/g, "\\\\")}')`;
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
// ...
|
||||
```
|
||||
|
||||
### Template Use
|
||||
|
||||
Pages can reference spreadsheets using a relative file reference. The ViteJS
|
||||
plugin will transform files with the `.xlsx` extension.
|
||||
|
||||
```js title="Script section of .vue VueJS page"
|
||||
import data from '../../pres.xlsx'; // data is an array of objects
|
||||
```
|
||||
|
||||
In the template, `data` is an array of objects that works with `v-for`[^4]:
|
||||
|
||||
```xml title="Template section of .vue VueJS page"
|
||||
<table>
|
||||
<thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
|
||||
<!-- loop over the rows of each worksheet -->
|
||||
<tr v-for="row in data" v-bind:key="row.Index">
|
||||
<!-- here `row` is a row object generated from sheet_to_json -->
|
||||
<td>{{ row.Name }}</td>
|
||||
<td>{{ row.Index }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
### Nuxt Content 3 Demo
|
||||
|
||||
1) Create a stock app and install dependencies:
|
||||
|
||||
```bash
|
||||
npx -y nuxi init -t content --packageManager pnpm --no-gitInit sheetjs-nc3 -M ,
|
||||
cd sheetjs-nc3
|
||||
npx -y pnpm install
|
||||
```
|
||||
|
||||
2) Install the SheetJS library and start the server:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npx -y pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npx -y pnpm dev`}
|
||||
</CodeBlock>
|
||||
|
||||
When the build finishes, the terminal will display a URL like:
|
||||
|
||||
```
|
||||
> Local: http://localhost:3000/
|
||||
```
|
||||
|
||||
The server is listening on that URL. Open the link in a web browser.
|
||||
|
||||
3) Download https://docs.sheetjs.com/pres.xlsx and move to the root folder:
|
||||
|
||||
```bash
|
||||
curl -o pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
```
|
||||
|
||||
4) Replace `nuxt.config.ts` with the following codeblock:
|
||||
|
||||
```ts title="nuxt.config.ts"
|
||||
import { readFileSync } from 'fs';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
export default defineNuxtConfig({
|
||||
vite: { // these options are passed to ViteJS
|
||||
assetsInclude: ['**/*.xlsx'], // xlsx file should be treated as assets
|
||||
|
||||
plugins: [
|
||||
{ // this plugin handles .xlsx
|
||||
name: "vite-sheet",
|
||||
transform(_code, id) {
|
||||
if(!id.match(/\.xlsx$/)) return;
|
||||
var wb = read(readFileSync(id.replace(/\?sheetjs$/, "")));
|
||||
var data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
return `export default JSON.parse('${JSON.stringify(data).replace(/\\/g, "\\\\")}')`;
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
modules: [
|
||||
'@nuxt/content',
|
||||
],
|
||||
devtools: { enabled: true },
|
||||
});
|
||||
```
|
||||
|
||||
5) Create a new file `app.vue` with the following contents:
|
||||
|
||||
```html title="app.vue (create new file)"
|
||||
<script setup>
|
||||
import data from '../../pres.xlsx'
|
||||
</script>
|
||||
<template>
|
||||
<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
|
||||
<tr v-for="row in data" v-bind:key="row.Index">
|
||||
<td>{{ row.Name }}</td>
|
||||
<td>{{ row.Index }}</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</template>
|
||||
```
|
||||
|
||||
6) Refresh the browser window. This page should now display an HTML table.
|
||||
|
||||
7) Stop the server (press <kbd>CTRL</kbd>+<kbd>C</kbd> in the terminal window).
|
||||
|
||||
8) Build the static site:
|
||||
|
||||
```bash
|
||||
npx -y pnpm run generate
|
||||
```
|
||||
|
||||
This will create a static site in `.output/public`, which can be served with:
|
||||
|
||||
```bash
|
||||
npx -y http-server .output/public
|
||||
```
|
||||
|
||||
Access the displayed URL (typically `http://localhost:8080`) in a web browser.
|
||||
|
||||
To confirm that the spreadsheet data is added to the site, view the page source.
|
||||
|
||||
Searching for `Bill Clinton` reveals the following encoded HTML row:
|
||||
|
||||
```html
|
||||
<tr><td>Bill Clinton</td><td>42</td></tr>
|
||||
```
|
||||
Accessing `http://localhost:8080/pres` will show the page contents. Verifying
|
||||
the static nature is trivial: make another change in Excel and save. The page
|
||||
will not change.
|
||||
|
||||
[^1]: See [`readFile` in "Reading Files"](/docs/api/parse-options)
|
||||
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
|
||||
@ -49,11 +49,10 @@ flowchart LR
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| SvelteJS | Kit | Date |
|
||||
|:----------|:---------|:-----------|
|
||||
| `5.27.2` | `2.20.7` | 2025-04-17 |
|
||||
| `4.2.19` | `2.5.10` | 2025-04-17 |
|
||||
| `3.59.2` | `1.30.4` | 2025-04-17 |
|
||||
| Svelte | Kit | Date |
|
||||
|:----------------|:---------|:-----------|
|
||||
| `4.2.8` | `1.27.6` | 2023-12-04 |
|
||||
| `5.0.0-next.17` | `1.27.6` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -179,75 +178,39 @@ a simple HTML table without any reference to the existing spreadsheet file!
|
||||
|
||||
## Complete Example
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested, SvelteKit required NodeJS major version 20.
|
||||
|
||||
:::
|
||||
|
||||
### Initial Setup
|
||||
|
||||
1) Create a new site:
|
||||
|
||||
```bash
|
||||
mkdir -p sheetjs-svelte
|
||||
cd sheetjs-svelte
|
||||
npx sv create
|
||||
```
|
||||
|
||||
When prompted:
|
||||
|
||||
- `Where would you like your project to be created?`: press <kbd>Enter</kbd>
|
||||
- `Which template would you like?` select `SvelteKit minimal`
|
||||
- `Add type checking with TypeScript?` select `Yes, using JavaScript with JSDoc`
|
||||
- `What would you like to add to your project?` press <kbd>Enter</kbd> (do not select options)
|
||||
- `Which package manager do you want to install dependencies with?`: select `npm` and press <kbd>Enter</kbd>
|
||||
|
||||
<details>
|
||||
<summary><b>Older SvelteJS versions</b> (click to show)</summary>
|
||||
|
||||
The following commands create SvelteJS projects for older versions:
|
||||
|
||||
**SvelteJS 3.x**
|
||||
|
||||
```bash
|
||||
npm create svelte@4 sheetjs-svelte
|
||||
```
|
||||
|
||||
|
||||
**SvelteJS 4.x**
|
||||
|
||||
```bash
|
||||
npm create svelte@6 sheetjs-svelte
|
||||
npm create svelte@latest sheetjs-svelte
|
||||
```
|
||||
|
||||
When prompted:
|
||||
|
||||
- `Which Svelte app template?` select `Skeleton Project`
|
||||
- `Add type checking with TypeScript?` select `Yes, using JavaScript with JSDoc`
|
||||
- `Select additional options` press <kbd>Enter</kbd> (do not select options)
|
||||
- `Select additional options` press Enter (do not select options)
|
||||
|
||||
After creating the project, enter the project folder and install dependencies:
|
||||
:::note pass
|
||||
|
||||
To test the Svelte 5 beta, select "Try out Svelte 5 beta" before pressing Enter.
|
||||
|
||||
:::
|
||||
|
||||
2) Enter the project folder and install dependencies:
|
||||
|
||||
```bash
|
||||
cd sheetjs-svelte
|
||||
npm i
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
2) Install dependencies:
|
||||
|
||||
```bash
|
||||
npm i
|
||||
```
|
||||
|
||||
3) Fetch the example file [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) and
|
||||
move to a `data` subdirectory in the root of the project:
|
||||
3) Fetch the example file [`pres.xlsx`](https://sheetjs.com/pres.xlsx) and move
|
||||
to a `data` subdirectory in the root of the project:
|
||||
|
||||
```bash
|
||||
mkdir -p data
|
||||
curl -Lo data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
curl -Lo data/pres.xlsx https://sheetjs.com/pres.xlsx
|
||||
```
|
||||
|
||||
4) Install the SheetJS library:
|
||||
@ -256,7 +219,7 @@ curl -Lo data/pres.xlsx https://docs.sheetjs.com/pres.xlsx
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
5) Replace the contents of `vite.config.js` with the following codeblock:
|
||||
5) Replace the contents of `vite.config.js` with the following:
|
||||
|
||||
```js title="vite.config.js"
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
@ -286,8 +249,7 @@ declare global {
|
||||
}
|
||||
```
|
||||
|
||||
7) Replace the contents of `src/routes/+page.server.js` with the following code.
|
||||
Create the file if it does not exist.
|
||||
7) Replace the contents of `src/routes/+page.server.js` with following:
|
||||
|
||||
```js title="src/routes/+page.server.js"
|
||||
import b64 from "../../data/pres.xlsx";
|
||||
@ -304,8 +266,9 @@ export async function load({ params }) {
|
||||
}
|
||||
```
|
||||
|
||||
8) Replace the contents of `src/routes/+page.svelte` with the following code.
|
||||
Create the file if it does not exist.
|
||||
If the file does not exist, create a new file.
|
||||
|
||||
8) Replace the contents of `src/routes/+page.svelte` with the following:
|
||||
|
||||
```html title="src/routes/+page.svelte"
|
||||
<script>
|
||||
@ -342,14 +305,6 @@ observe that the data from the spreadsheet is displayed in the page.
|
||||
11) In the spreadsheet, set cell A7 to `SheetJS Dev` and cell B7 to `47`. Save
|
||||
the file. After saving, the browser should automatically refresh with new data.
|
||||
|
||||
:::caution pass
|
||||
|
||||
**Live reloading may not work in newer projects!**
|
||||
|
||||
If the page does not reload, manually refresh the page.
|
||||
|
||||
:::
|
||||
|
||||
### Static Site
|
||||
|
||||
12) Stop the development server and install the static adapter:
|
||||
|
||||
@ -36,7 +36,7 @@ flowchart LR
|
||||
aoo --> |index.astro\ntemplate body| html
|
||||
```
|
||||
|
||||
:::danger Telemetry
|
||||
:::warning Telemetry
|
||||
|
||||
AstroJS enables telemetry by default. The tool has an option to disable telemetry:
|
||||
|
||||
@ -50,16 +50,10 @@ npx astro telemetry disable
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| AstroJS | Template | Date |
|
||||
|:--------|:-----------------|:-----------|
|
||||
| `4.6.1` | Starlight 0.22.4 | 2025-01-07 |
|
||||
| `5.1.3` | Starlight 0.30.5 | 2025-01-07 |
|
||||
|
||||
In previous test runs, this demo successfully ran using older AstroJS versions:
|
||||
|
||||
| AstroJS | Template |
|
||||
|:--------|:-----------------|
|
||||
| `3.6.5` | Starlight 0.14.0 |
|
||||
| AstroJS | Date |
|
||||
|:---------------|:-----------|
|
||||
| `3.6.4` | 2023-12-04 |
|
||||
| `4.0.0-beta.4` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -75,7 +69,7 @@ AstroJS has introduced a number of breaking changes in minor releases.
|
||||
|
||||
:::info pass
|
||||
|
||||
This demo uses ["Base64 Loader"](/docs/demos/static/vitejs#base64-plugin)
|
||||
This demo uses ["Base64 Loader"](/docs/demos/static/vitejs#base64-loader)
|
||||
from the ViteJS demo.
|
||||
|
||||
The ViteJS demo used the query `?b64` to identify files. To play nice with
|
||||
@ -116,7 +110,7 @@ export default defineConfig({
|
||||
|
||||
#### Types
|
||||
|
||||
For VSCodium integration, types can be specified in `src/env.d.ts`.
|
||||
For VSCode and VSCodium integration, types can be specified in `src/env.d.ts`.
|
||||
|
||||
This data loader returns Base64 strings:
|
||||
|
||||
@ -218,13 +212,10 @@ cd sheetjs-astro
|
||||
|
||||
:::note pass
|
||||
|
||||
To test an older version of AstroJS, install the specific version of `astro` and
|
||||
a supported starter template after creating the project.
|
||||
|
||||
For major version 4, Starlight must be version `0.22.4`:
|
||||
To test the AstroJS 4 beta release, run the following command:
|
||||
|
||||
```bash
|
||||
npm install --force astro@4 @astrojs/starlight@0.22.4
|
||||
npm install --force @astrojs/starlight@^0.14.0 astro@4.0.0-beta.4
|
||||
```
|
||||
|
||||
The version can be verified by running:
|
||||
@ -233,29 +224,13 @@ The version can be verified by running:
|
||||
npx astro --version
|
||||
```
|
||||
|
||||
**When using older versions of AstroJS, the sidebar must be removed!**
|
||||
|
||||
In `astro.config.mjs`, the `sidebar` property in the config must be removed:
|
||||
|
||||
```js title="astro.config.mjs (snippet)"
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
starlight({
|
||||
// ...
|
||||
// remove the `sidebar` array if it exists in the object
|
||||
sidebar: [
|
||||
// remove this entire array
|
||||
],
|
||||
}),
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
2) Fetch the example file [`pres.numbers`](https://docs.sheetjs.com/pres.numbers):
|
||||
2) Fetch the example file [`pres.numbers`](https://sheetjs.com/pres.numbers):
|
||||
|
||||
```bash
|
||||
mkdir -p src/data
|
||||
curl -Lo src/data/pres.numbers https://docs.sheetjs.com/pres.numbers
|
||||
curl -Lo src/data/pres.numbers https://sheetjs.com/pres.numbers
|
||||
```
|
||||
|
||||
3) Install the SheetJS library:
|
||||
@ -276,7 +251,7 @@ declare module '*.xlsx' { const data: string; export default data; }
|
||||
|
||||
- At the top of the script, import `readFileSync`:
|
||||
|
||||
```js title="astro.config.mjs (add higlighted lines)"
|
||||
```js title="astro.config.mjs"
|
||||
// highlight-start
|
||||
/* import `readFileSync` at the top of the script*/
|
||||
import { readFileSync } from 'fs';
|
||||
@ -286,7 +261,7 @@ import { defineConfig } from 'astro/config';
|
||||
|
||||
- In the object argument to `defineConfig`, add a `vite` section:
|
||||
|
||||
```js title="astro.config.mjs (add highlighted lines)"
|
||||
```js title="astro.config.mjs"
|
||||
export default defineConfig({
|
||||
// highlight-start
|
||||
/* this vite section should be added as a property of the object */
|
||||
@ -345,11 +320,12 @@ AstroJS will place the generated site in the `dist` subfolder.
|
||||
9) Start a web server to host the static site:
|
||||
|
||||
```bash
|
||||
npx -y http-server dist
|
||||
npx http-server dist
|
||||
```
|
||||
|
||||
Open a web browser and access the displayed URL ( `http://localhost:8080` ).
|
||||
View the page source and confirm that the raw data is stored in an HTML table.
|
||||
View the page source and confirm that no JS was added to the page. It only
|
||||
contains the content from the file in an HTML table.
|
||||
|
||||
:::caution pass
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Sheets on the Go with React Native
|
||||
title: React Native
|
||||
sidebar_label: React Native
|
||||
description: Build data-intensive mobile apps with React Native. Seamlessly integrate spreadsheets into your app using SheetJS. Securely process and generate Excel files in the field.
|
||||
pagination_prev: demos/static/index
|
||||
@ -9,6 +9,8 @@ sidebar_custom_props:
|
||||
summary: React + Native Rendering
|
||||
---
|
||||
|
||||
# Sheets on the Go with React Native
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
@ -67,9 +69,7 @@ imported from any component or script in the app.
|
||||
|
||||
For simplicity, this demo uses an "Array of Arrays"[^2] as the internal state.
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>Array of Arrays</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
<table><thead><tr><th>Spreadsheet</th><th>Array of Arrays</th></tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
@ -97,7 +97,7 @@ _Complete State_
|
||||
|
||||
The complete state is initialized with the following snippet:
|
||||
|
||||
```js title="State variables"
|
||||
```js
|
||||
const [data, setData] = useState([
|
||||
"SheetJS".split(""),
|
||||
[5,4,3,3,7,9,5],
|
||||
@ -112,7 +112,7 @@ const [widths, setWidths] = useState(Array.from({length:7}, () => 20));
|
||||
Starting from a SheetJS worksheet object, `sheet_to_json`[^3] with the `header`
|
||||
option can generate an array of arrays:
|
||||
|
||||
```js title="Updating state from a workbook"
|
||||
```js
|
||||
/* assuming `wb` is a SheetJS workbook */
|
||||
function update_state(wb) {
|
||||
/* convert first worksheet to AOA */
|
||||
@ -133,7 +133,7 @@ _Calculating Column Widths_
|
||||
Column widths can be calculated by walking each column and calculating the max
|
||||
data width. Using the array of arrays:
|
||||
|
||||
```js title="Calculating column widths"
|
||||
```js
|
||||
/* this function takes an array of arrays and generates widths */
|
||||
function make_width(aoa) {
|
||||
/* walk each row */
|
||||
@ -154,7 +154,7 @@ function make_width(aoa) {
|
||||
|
||||
`aoa_to_sheet`[^4] builds a SheetJS worksheet object from the array of arrays:
|
||||
|
||||
```js title="Exporting state data to a workbook"
|
||||
```js
|
||||
/* generate a SheetJS workbook from the state */
|
||||
function export_state() {
|
||||
/* convert AOA back to worksheet */
|
||||
@ -170,19 +170,13 @@ function export_state() {
|
||||
|
||||
### Displaying Data
|
||||
|
||||
React Native does not ship with a component for displaying tabular data.
|
||||
|
||||
`react-native-table-component` is a simple UI component designed for legacy
|
||||
versions of React Native.
|
||||
|
||||
[`react-native-tabeller`](https://git.sheetjs.com/asadbek064/react-native-tabeller)
|
||||
uses a similar API and follows modern React Native design patterns.
|
||||
The demos uses `react-native-table-component` to display the first worksheet.
|
||||
|
||||
The demos use components similar to the example below:
|
||||
|
||||
```jsx title="Example JSX for displaying data"
|
||||
```jsx
|
||||
import { ScrollView } from 'react-native';
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-tabeller';
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
||||
|
||||
(
|
||||
{/* Horizontal scroll */}
|
||||
@ -213,11 +207,11 @@ row. This neatly skips the first header row.
|
||||
|
||||
React Native versions starting from `0.72.0`[^5] support binary data in `fetch`.
|
||||
|
||||
This snippet downloads and parses https://docs.sheetjs.com/pres.xlsx:
|
||||
This snippet downloads and parses <https://sheetjs.com/pres.xlsx>:
|
||||
|
||||
```js
|
||||
/* fetch data into an ArrayBuffer */
|
||||
const ab = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const ab = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
/* parse data */
|
||||
const wb = XLSX.read(ab);
|
||||
```
|
||||
@ -228,185 +222,66 @@ const wb = XLSX.read(ab);
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | RN | Dev Platform | Date |
|
||||
|:-----------|:------------------|:---------|:-------------|:-----------|
|
||||
| Android 34 | Pixel 3a | `0.73.6` | `darwin-x64` | 2024-03-13 |
|
||||
| iOS 17.4 | iPhone 15 Pro Max | `0.73.6` | `darwin-x64` | 2024-03-13 |
|
||||
| Android 34 | Pixel 3a | `0.73.5` | `win10-x64` | 2024-03-05 |
|
||||
|
||||
**Real Devices**
|
||||
|
||||
| OS | Device | RN | Date |
|
||||
|:-----------|:------------------|:---------|:-----------|
|
||||
| iOS 15.6 | iPhone 13 Pro Max | `0.76.8` | 2025-03-26 |
|
||||
| Android 34 | NVIDIA Shield | `0.76.8` | 2025-03-26 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | RN | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:---------|:-------------|:-----------|
|
||||
| Android 34 | Pixel 3a | `0.76.8` | `darwin-arm` | 2025-03-26 |
|
||||
| iOS 18.3 | iPhone 16 Pro | `0.76.8` | `darwin-arm` | 2025-03-26 |
|
||||
| Android 35 | Pixel 9 Pro XL | `0.79.2` | `win11-x64` | 2025-06-08 |
|
||||
| Android 36 | Pixel 9 | `0.79.2` | `linux-x64` | 2025-07-06 |
|
||||
| iOS 15.1 | iPhone 12 Pro Max | `0.73.6` | 2024-03-13 |
|
||||
| Android 29 | NVIDIA Shield | `0.73.6` | 2024-03-13 |
|
||||
|
||||
:::
|
||||
|
||||
:::caution
|
||||
|
||||
**Before testing this demo, follow the official React Native CLI Guide!**[^1]
|
||||
|
||||
Make sure you can run a basic test app on your phone/simulator before continuing!
|
||||
|
||||
:::
|
||||
|
||||
0) Install React Native dependencies
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
**Java JDK**
|
||||
|
||||
On the Steam Deck, JDK17 was installed using `pacman`:
|
||||
|
||||
```bash
|
||||
sudo pacman -Syu jdk17-openjdk
|
||||
```
|
||||
|
||||
Debian and Ubuntu require the `openjdk-17-jdk` package:
|
||||
|
||||
```bash
|
||||
sudo apt install openjdk-17-jdk
|
||||
```
|
||||
|
||||
**Android Studio for Linux**
|
||||
|
||||
[The Android Studio tarball](https://developer.android.com/studio) was
|
||||
downloaded and extracted. After extracting:
|
||||
|
||||
```bash
|
||||
cd ./android-studio/bin
|
||||
./studio.sh
|
||||
```
|
||||
|
||||
In Android Studio, select "SDK Manager" and switch to the "SDK Tools" tab. Check
|
||||
"Show Package Details" and install "Android SDK Command-line Tools (latest)".
|
||||
|
||||
**Environment Variables for Linux**
|
||||
|
||||
When this demo was last tested, the following environment variables were used:
|
||||
|
||||
```bash
|
||||
export ANDROID_HOME=~/Android/Sdk
|
||||
export PATH=$PATH:$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools
|
||||
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
1) Create project:
|
||||
|
||||
```bash
|
||||
npx -y @react-native-community/cli@18 init SheetJSRNFetch --version="0.79.2"
|
||||
npx -y react-native@0.73.6 init SheetJSRNFetch --version="0.73.6"
|
||||
```
|
||||
|
||||
On macOS, if prompted to install `CocoaPods`, press <kbd>Y</kbd>
|
||||
|
||||
:::info pass
|
||||
|
||||
If you were prompted to install CocoaPods, verify it worked by opening a new
|
||||
terminal and running:
|
||||
|
||||
```bash
|
||||
pod --version
|
||||
```
|
||||
|
||||
If you see "command not found", install it via `brew`:
|
||||
|
||||
```bash
|
||||
brew install cocoapods
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
Older versions of this demo used the `react-native` package. The `init` command
|
||||
was officially deprecated.
|
||||
|
||||
React Native now recommends using `@react-native-community/cli`. The versioning
|
||||
scheme is fundamentally different from `react-native`.[^6]
|
||||
|
||||
:::
|
||||
|
||||
2) Install shared dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd SheetJSRNFetch
|
||||
curl -LO https://docs.sheetjs.com/logo.png
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm i -S https://cdn.sheetjs.com/react-native-tabeller-0.1.0/react-native-tabeller-0.1.0.tgz`}
|
||||
npm i -S react-native-table-component@1.2.0 @types/react-native-table-component`}
|
||||
</CodeBlock>
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd SheetJSRNFetch
|
||||
curl.exe -LO https://docs.sheetjs.com/logo.png
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm i -S https://cdn.sheetjs.com/react-native-tabeller-0.1.0/react-native-tabeller-0.1.0.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
3) Download and replace [`App.tsx`](pathname:///reactnative/App.tsx):
|
||||
3) Download [`App.tsx`](pathname:///reactnative/App.tsx) and replace:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/reactnative/App.tsx
|
||||
```
|
||||
|
||||
**Android Testing**
|
||||
|
||||
4) Install or switch to Java 17[^6]
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
When the demo was last tested on macOS, `java -version` displayed the following:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://docs.sheetjs.com/reactnative/App.tsx
|
||||
openjdk version "17.0.10" 2024-01-16
|
||||
OpenJDK Runtime Environment Temurin-17.0.10+7 (build 17.0.10+7)
|
||||
OpenJDK 64-Bit Server VM Temurin-17.0.10+7 (build 17.0.10+7, mixed mode)
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**Android Testing**
|
||||
|
||||
4) Install or switch to Java 17[^7]
|
||||
|
||||
5) Start the Android emulator:
|
||||
|
||||
```bash
|
||||
npx react-native run-android
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
On Linux, the command may silently stall. It is strongly recommended to launch
|
||||
the interactive CLI tool:
|
||||
|
||||
```bash
|
||||
npx react-native start
|
||||
```
|
||||
|
||||
Once the dev server is ready, the terminal will display a few options. Press
|
||||
<kbd>r</kbd> to relaod the app.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
If the initial launch fails with an error referencing the emulator, manually
|
||||
@ -423,74 +298,6 @@ This error can be resolved by installing and switching to the requested version.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested on Windows 11, there was a Gradle error
|
||||
mentioning a "temporary workspace":
|
||||
|
||||
```
|
||||
> java.io.UncheckedIOException: Could not move temporary workspace (C:\Users\sheetjs\Documents\SheetJSRNFetch\android\.gradle\8.10.2\dependencies-accessors\569c8b261a8a714d7731d5f568e0e5c05babae10-4756c477-c60a-4328-ba8c-e12cc53b4ed0) to immutable location (C:\Users\sheetjs\Documents\SheetJSRNFetch\android\.gradle\8.10.2\dependencies-accessors\569c8b261a8a714d7731d5f568e0e5c05babae10)
|
||||
```
|
||||
|
||||
**This is a known bug in the version of Gradle used by React Native 0.76.5!**
|
||||
|
||||
The Gradle version should be set to `8.11.1` in `gradle-wrapper.properties`:
|
||||
|
||||
```text title="android/gradle/wrapper/gradle-wrapper.properties (change line)"
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested on Linux, the process failed to launch the emulator:
|
||||
|
||||
<pre>
|
||||
<b {...y}>warn</b> Please launch an emulator manually or connect a device. Otherwise app may fail to launch.
|
||||
</pre>
|
||||
|
||||
|
||||
**This is a known bug in React Native!**
|
||||
|
||||
If an emulator is installed, run the following command:
|
||||
|
||||
```bash
|
||||
npx react-native doctor
|
||||
```
|
||||
|
||||
Under `Android`, there will be one error:
|
||||
|
||||
<pre>
|
||||
<span {...gr}>Android</span> {`\n`}
|
||||
{` `}<span {...r}>✖</span> Adb - No devices and/or emulators connected. Please create emulator with Android Studio or connect Android device.
|
||||
</pre>
|
||||
|
||||
Press `f` and a list of available emulators will be shown. Select the emulator
|
||||
(typically the last line) and press Enter.
|
||||
|
||||
<pre>
|
||||
<span {...g}>✔</span> <b>Select the device / emulator you want to use</b> <span {...gr}>›</span> <b>Emulator</b> <span {...g}>Pixel_3a_API_34_extension_level_7_x86_64</span> (disconnected)
|
||||
</pre>
|
||||
|
||||
The text in green is the name of the virtual device
|
||||
(`Pixel_3a_API_34_extension_level_7_x86_64` in this example).
|
||||
Run the following command to manually start the emulator:
|
||||
|
||||
```bash
|
||||
$ANDROID_HOME/tools/emulator -avd Pixel_3a_API_34_extension_level_7_x86_64
|
||||
```
|
||||
|
||||
(replace `Pixel_3a_API_34_extension_level_7_x86_64` with the name of the virtual device)
|
||||
|
||||
To confirm React Native detects the emulator, run the following command again:
|
||||
|
||||
```bash
|
||||
npx react-native doctor
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
6) When opened, the app should look like the "Before" screenshot below. After
|
||||
tapping "Import data from a spreadsheet", verify that the app shows new data:
|
||||
|
||||
@ -509,7 +316,7 @@ tapping "Import data from a spreadsheet", verify that the app shows new data:
|
||||
|
||||
**iOS Testing**
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
**iOS testing can only be performed on Apple hardware running macOS!**
|
||||
|
||||
@ -559,12 +366,6 @@ If the device asks to allow USB debugging, tap "Allow".
|
||||
npx react-native run-android
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
Depending on the device, it may take up to 10 seconds for app to update.
|
||||
|
||||
:::
|
||||
|
||||
**iOS Device Testing**
|
||||
|
||||
13) Connect an iOS device using a USB cable.
|
||||
@ -573,12 +374,11 @@ If the device asks to trust the computer, tap "Trust" and enter the passcode.
|
||||
|
||||
14) Close any Android / iOS emulators.
|
||||
|
||||
15) Enable developer code signing certificates[^8].
|
||||
15) Enable developer code signing certificates[^7].
|
||||
|
||||
<details open>
|
||||
<summary><b>Enabling Code Signing</b> (click to show)</summary>
|
||||
<details open><summary><b>Enabling Code Signing</b> (click to show)</summary>
|
||||
|
||||
These instructions were verified against Xcode 16.2.
|
||||
These instructions were verified against Xcode 15.3.
|
||||
|
||||
A) Open the included iOS workspace in Xcode:
|
||||
|
||||
@ -610,29 +410,6 @@ brew install ios-deploy
|
||||
npx react-native run-ios
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
If the device is not detected, the terminal will print
|
||||
|
||||
```
|
||||
info No booted devices or simulators found. Launching first available simulator...
|
||||
```
|
||||
|
||||
This may happen if the device goes to sleep while connected. Disconnect and
|
||||
reconnect the device before trying again.
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
In some test runs, the app requested for local network access:
|
||||
|
||||
> "SheetJSRNFetch" would like to find and connect to devices on your local network.
|
||||
|
||||
Local network access is not required for the demo. Select "Don't Allow".
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some tests, the app failed to run on the device due to "Untrusted Developer":
|
||||
@ -641,7 +418,7 @@ In some tests, the app failed to run on the device due to "Untrusted Developer":
|
||||
Your device management settings do not allow apps from developer ... on this iPhone. You can allow using these apps in Settings.
|
||||
```
|
||||
|
||||
These instructions were verified against iOS 15.6.
|
||||
These instructions were verified against iOS 15.1.
|
||||
|
||||
A) Open the Settings app and select "General" > "VPN & Device Management".
|
||||
|
||||
@ -764,7 +541,7 @@ Click on the icon and select the real device from the list.
|
||||
|
||||
## Local Files
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
React Native does not provide a native file picker or a method for reading and
|
||||
writing data from documents on the devices. A third-party library must be used.
|
||||
@ -789,17 +566,16 @@ The following table lists tested file plugins. "OS" lists tested platforms
|
||||
Due to privacy concerns, apps must request file access. There are special APIs
|
||||
for accessing data and are subject to change in future platform versions.
|
||||
|
||||
<details>
|
||||
<summary><b>Technical Details</b> (click to show)</summary>
|
||||
<details><summary><b>Technical Details</b> (click to show)</summary>
|
||||
|
||||
**iOS**
|
||||
|
||||
iOS applications typically require two special settings in `Info.plist`:
|
||||
|
||||
- `UIFileSharingEnabled`[^9] allows users to use files written by the app. A
|
||||
- `UIFileSharingEnabled`[^8] allows users to use files written by the app. A
|
||||
special folder will appear in the "Files" app.
|
||||
|
||||
- `LSSupportsOpeningDocumentsInPlace`[^10] allows the app to open files without
|
||||
- `LSSupportsOpeningDocumentsInPlace`[^9] allows the app to open files without
|
||||
creating a local copy.
|
||||
|
||||
Both settings must be set to `true`:
|
||||
@ -922,8 +698,7 @@ is the continuation of other libraries that date back to 2016.
|
||||
The `ascii` type returns an array of numbers corresponding to the raw bytes.
|
||||
A `Uint8Array` from the data is compatible with the `buffer` type.
|
||||
|
||||
<details>
|
||||
<summary><b>Reading and Writing snippets</b> (click to hide)</summary>
|
||||
<details><summary><b>Reading and Writing snippets</b> (click to hide)</summary>
|
||||
|
||||
_Reading Data_
|
||||
|
||||
@ -1004,8 +779,7 @@ is a filesystem API that uses modern iOS and Android development patterns.
|
||||
|
||||
The `base64` encoding returns strings compatible with the `base64` type:
|
||||
|
||||
<details>
|
||||
<summary><b>Reading and Writing snippets</b> (click to hide)</summary>
|
||||
<details><summary><b>Reading and Writing snippets</b> (click to hide)</summary>
|
||||
|
||||
_Reading Data_
|
||||
|
||||
@ -1065,8 +839,7 @@ The [`expo-document-picker`](#expo-document-picker) snippet makes a local copy.
|
||||
|
||||
The `EncodingType.Base64` encoding is compatible with `base64` type.
|
||||
|
||||
<details>
|
||||
<summary><b>Reading and Writing snippets</b> (click to hide)</summary>
|
||||
<details><summary><b>Reading and Writing snippets</b> (click to hide)</summary>
|
||||
|
||||
_Reading Data_
|
||||
|
||||
@ -1130,29 +903,28 @@ try {
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
**Real Devices**
|
||||
|
||||
| OS | Device | RN | Date |
|
||||
|:-----------|:------------------|:---------|:-----------|
|
||||
| iOS 15.6 | iPhone 13 Pro Max | `0.76.5` | 2025-01-05 |
|
||||
| Android 34 | NVIDIA Shield | `0.76.5` | 2025-01-05 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | RN | Dev Platform | Date |
|
||||
|:-----------|:------------------|:---------|:-------------|:-----------|
|
||||
| Android 34 | Pixel 3a | `0.76.5` | `darwin-arm` | 2025-01-05 |
|
||||
| iOS 18.2 | iPhone 16 Pro | `0.76.5` | `darwin-arm` | 2025-01-05 |
|
||||
| Android 35 | Pixel 9 Pro XL | `0.76.5` | `win11-x64` | 2025-06-08 |
|
||||
| Android 36 | Pixel 9 | `0.76.5` | `linux-x64` | 2025-07-06 |
|
||||
| Android 34 | Pixel 3a | `0.73.6` | `darwin-x64` | 2024-03-31 |
|
||||
| iOS 17.4 | iPhone 15 Pro Max | `0.73.6` | `darwin-x64` | 2024-03-31 |
|
||||
| Android 34 | Pixel 3a | `0.73.6` | `win10-x64` | 2024-03-31 |
|
||||
|
||||
**Real Devices**
|
||||
|
||||
| OS | Device | RN | Date |
|
||||
|:-----------|:------------------|:---------|:-----------|
|
||||
| iOS 15.5 | iPhone 13 Pro Max | `0.73.6` | 2024-03-31 |
|
||||
| Android 29 | NVIDIA Shield | `0.73.6` | 2024-03-31 |
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
There are many moving parts and pitfalls with React Native apps. It is strongly
|
||||
recommended to follow the official React Native tutorials for iOS and Android
|
||||
before approaching this demo.[^11] Details including Android Virtual Device
|
||||
before approaching this demo.[^10] Details including Android Virtual Device
|
||||
configuration are not covered here.
|
||||
|
||||
:::
|
||||
@ -1164,10 +936,10 @@ This example tries to separate the library-specific functions.
|
||||
1) Create project:
|
||||
|
||||
```bash
|
||||
npx -y @react-native-community/cli@15 init SheetJSRN --version="0.76.5"
|
||||
npx react-native init SheetJSRN --version="0.73.6"
|
||||
```
|
||||
|
||||
On macOS, if prompted to install `CocoaPods`, press <kbd>Y</kbd>
|
||||
On macOS, if prompted to install `CocoaPods`, press `y`.
|
||||
|
||||
2) Install shared dependencies:
|
||||
|
||||
@ -1175,50 +947,15 @@ On macOS, if prompted to install `CocoaPods`, press <kbd>Y</kbd>
|
||||
cd SheetJSRN
|
||||
curl -LO https://docs.sheetjs.com/logo.png
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm i -S https://cdn.sheetjs.com/react-native-tabeller-0.1.0/react-native-tabeller-0.1.0.tgz react-native-document-picker@9.3.1`}
|
||||
npm i -S react-native-table-component@1.2.0 react-native-document-picker@9.1.1`}
|
||||
</CodeBlock>
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd SheetJSRN
|
||||
curl.exe -LO https://docs.sheetjs.com/logo.png
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm i -S https://cdn.sheetjs.com/react-native-tabeller-0.1.0/react-native-tabeller-0.1.0.tgz react-native-document-picker@9.3.1`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
3) Download [`index.js`](pathname:///mobile/index.js) and replace:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/mobile/index.js
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://docs.sheetjs.com/mobile/index.js
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
4) Start the Android emulator:
|
||||
|
||||
```bash
|
||||
@ -1229,53 +966,20 @@ The app should look like the following screenshot:
|
||||
|
||||

|
||||
|
||||
:::info pass
|
||||
|
||||
On Linux, the command may silently stall. It is strongly recommended to launch
|
||||
the interactive CLI tool:
|
||||
|
||||
```bash
|
||||
npx react-native start
|
||||
```
|
||||
|
||||
Once the dev server is ready, the terminal will display a few options. Press
|
||||
<kbd>r</kbd> to relaod the app.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs on Windows, the build failed with an error:
|
||||
When this demo was last tested on Windows, the build failed with an error:
|
||||
|
||||
```
|
||||
> Failed to apply plugin 'com.android.internal.application'.
|
||||
> Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
|
||||
```
|
||||
|
||||
Java 17 must be installed[^12] and the `JAVA_HOME` environment variable must
|
||||
Java 17 must be installed[^11] and the `JAVA_HOME` environment variable must
|
||||
point to the Java 17 location.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested on Windows 11, there was a Gradle error
|
||||
mentioning a "temporary workspace":
|
||||
|
||||
```
|
||||
> java.io.UncheckedIOException: Could not move temporary workspace (C:\Users\sheetjs\Documents\SheetJSRNFetch\android\.gradle\8.10.2\dependencies-accessors\569c8b261a8a714d7731d5f568e0e5c05babae10-4756c477-c60a-4328-ba8c-e12cc53b4ed0) to immutable location (C:\Users\sheetjs\Documents\SheetJSRNFetch\android\.gradle\8.10.2\dependencies-accessors\569c8b261a8a714d7731d5f568e0e5c05babae10)
|
||||
```
|
||||
|
||||
**This is a known bug in the version of Gradle used by React Native 0.76.5!**
|
||||
|
||||
The Gradle version should be set to `8.11.1` in `gradle-wrapper.properties`:
|
||||
|
||||
```text title="android/gradle/wrapper/gradle-wrapper.properties (change line)"
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Stop the dev server and close the React Native Metro NodeJS window.
|
||||
|
||||
**File Integration**
|
||||
@ -1288,13 +992,13 @@ Stop the dev server and close the React Native Metro NodeJS window.
|
||||
Install `react-native-blob-util` dependency:
|
||||
|
||||
```bash
|
||||
npm i -S react-native-blob-util@0.22.2
|
||||
npm i -S react-native-blob-util@0.19.8
|
||||
```
|
||||
|
||||
Add the highlighted lines to `index.js`:
|
||||
|
||||
```js title="index.js (add highlighted lines)"
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-tabeller';
|
||||
```js title="index.js"
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
||||
|
||||
// highlight-start
|
||||
import { read, write } from 'xlsx';
|
||||
@ -1336,13 +1040,13 @@ const make_width = ws => {
|
||||
Install `react-native-file-access` dependency:
|
||||
|
||||
```bash
|
||||
npm i -S react-native-file-access@3.1.1
|
||||
npm i -S react-native-file-access@3.0.7
|
||||
```
|
||||
|
||||
Add the highlighted lines to `index.js`:
|
||||
|
||||
```js title="index.js"
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-tabeller';
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
||||
|
||||
// highlight-start
|
||||
import { read, write } from 'xlsx';
|
||||
@ -1391,25 +1095,10 @@ If prompted to install Expo CLI integration, choose No.
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
On Windows, `npx install-expo-modules` will throw errors related to CocoaPods:
|
||||
|
||||
```
|
||||
Uncaught Error CocoaPodsError: Command `pod install` failed.
|
||||
└─ Cause: spawn pod ENOENT
|
||||
'pod' is not recognized as an internal or external command,
|
||||
operable program or batch file.
|
||||
```
|
||||
|
||||
These errors can be ignored.
|
||||
|
||||
:::
|
||||
|
||||
Add the highlighted lines to `index.js`:
|
||||
|
||||
```js title="index.js"
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-tabeller';
|
||||
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
|
||||
|
||||
// highlight-start
|
||||
import { read, write } from 'xlsx';
|
||||
@ -1507,7 +1196,7 @@ npx react-native doctor
|
||||
|
||||
:::
|
||||
|
||||
8) Download https://docs.sheetjs.com/pres.numbers and open the Downloads folder.
|
||||
8) Download <https://sheetjs.com/pres.numbers> and open the Downloads folder.
|
||||
|
||||
9) Click and drag `pres.numbers` from the Downloads folder into the simulator.
|
||||
|
||||
@ -1565,7 +1254,7 @@ npx xlsx-cli sheetjsw.xlsx
|
||||
|
||||
**iOS Testing**
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
**iOS testing can only be performed on Apple hardware running macOS!**
|
||||
|
||||
@ -1587,7 +1276,7 @@ cd ios; pod install; cd -
|
||||
npx react-native run-ios
|
||||
```
|
||||
|
||||
17) Download https://docs.sheetjs.com/pres.numbers and open the Downloads folder.
|
||||
17) Download <https://sheetjs.com/pres.numbers> and open the Downloads folder.
|
||||
|
||||
18) In the simulator, click the Home icon to return to the home screen.
|
||||
|
||||
@ -1663,7 +1352,7 @@ If the device asks to allow USB debugging, tap "Allow".
|
||||
npx react-native run-android
|
||||
```
|
||||
|
||||
30) Download https://docs.sheetjs.com/pres.numbers on the device.
|
||||
30) Download <https://sheetjs.com/pres.numbers> on the device.
|
||||
|
||||
31) Switch back to the "SheetJSRN" app.
|
||||
|
||||
@ -1752,7 +1441,7 @@ npx react-native run-ios
|
||||
If the build fails, some troubleshooting notes are included in the "iOS Device
|
||||
Testing" part of the [Fetch Demo](#fetch-demo) (step 17).
|
||||
|
||||
41) Download https://docs.sheetjs.com/pres.numbers on the device.
|
||||
41) Download <https://sheetjs.com/pres.numbers> on the device.
|
||||
|
||||
42) Switch back to the "SheetJSRN" app.
|
||||
|
||||
@ -1778,15 +1467,14 @@ Swipe left until the "Numbers" app icon appears and tap the app icon.
|
||||
|
||||
The Numbers app will load the spreadsheet, confirming that the file is valid.
|
||||
|
||||
[^1]: Follow the ["Set Up Your Environment" guide](https://reactnative.dev/docs/set-up-your-environment) and select the appropriate "Development OS".
|
||||
[^1]: Follow the ["React Native CLI Quickstart"](https://reactnative.dev/docs/environment-setup) and select the appropriate "Development OS".
|
||||
[^2]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays)
|
||||
[^3]: See ["Array Output" in "Utility Functions"](/docs/api/utilities/array#array-output)
|
||||
[^4]: See ["Array of Arrays Input" in "Utility Functions"](/docs/api/utilities/array#array-of-arrays-input)
|
||||
[^5]: React-Native commit [`5b597b5`](https://github.com/facebook/react-native/commit/5b597b5ff94953accc635ed3090186baeecb3873) added the final piece required for `fetch` support. It is available in official releases starting from `0.72.0`.
|
||||
[^6]: See [the compatibility table](https://github.com/react-native-community/cli/blob/main/README.md#compatibility) in the CLI project repository to determine which version of `@react-native-community/cli` is required for a given `react-native` version.
|
||||
[^7]: When the demo was last tested, the Temurin distribution of Java 17 was installed through the macOS Brew package manager by running `brew install temurin17`. [Direct downloads are available at `adoptium.net`](https://adoptium.net/temurin/releases/?version=17)
|
||||
[^8]: See ["Running On Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation
|
||||
[^9]: See [`UIFileSharingEnabled`](https://developer.apple.com/documentation/bundleresources/information_property_list/uifilesharingenabled) in the Apple Developer Documentation.
|
||||
[^10]: See [`LSSupportsOpeningDocumentsInPlace`](https://developer.apple.com/documentation/bundleresources/information_property_list/lssupportsopeningdocumentsinplace) in the Apple Developer Documentation.
|
||||
[^11]: Follow the ["React Native CLI Quickstart"](https://reactnative.dev/docs/environment-setup) for Android (and iOS, if applicable)
|
||||
[^12]: See the [JDK Archive](https://jdk.java.net/archive/) for Java 17 JDK download links.
|
||||
[^5]: React-Native commit [`5b597b5`](https://github.com/facebook/react-native/commit/5b597b5ff94953accc635ed3090186baeecb3873) added the final piece required for `fetch` support. It landed in version `0.72.0-rc.1` and is available in official releases starting from `0.72.0`.
|
||||
[^6]: When the demo was last tested, the Temurin distribution of Java 17 was installed through the macOS Brew package manager by running `brew install temurin17`. [Direct downloads are available at `adoptium.net`](https://adoptium.net/temurin/releases/?version=17)
|
||||
[^7]: See ["Running On Device"](https://reactnative.dev/docs/running-on-device) in the React Native documentation
|
||||
[^8]: See [`UIFileSharingEnabled`](https://developer.apple.com/documentation/bundleresources/information_property_list/uifilesharingenabled) in the Apple Developer Documentation.
|
||||
[^9]: See [`LSSupportsOpeningDocumentsInPlace`](https://developer.apple.com/documentation/bundleresources/information_property_list/lssupportsopeningdocumentsinplace) in the Apple Developer Documentation.
|
||||
[^10]: Follow the ["React Native CLI Quickstart"](https://reactnative.dev/docs/environment-setup) for Android (and iOS, if applicable)
|
||||
[^11]: See the [JDK Archive](https://jdk.java.net/archive/) for Java 17 JDK download links.
|
||||
@ -13,7 +13,7 @@ import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
export const g = {style: {color:"green"}};
|
||||
export const r = {style: {color:"red"}};
|
||||
export const y = {style: {color:"gold"}};
|
||||
export const y = {style: {color:"yellow"}};
|
||||
|
||||
[NativeScript](https://nativescript.org/) is a mobile app framework. It builds
|
||||
iOS and Android apps that use JavaScript for describing layouts and events.
|
||||
@ -25,8 +25,7 @@ This demo uses NativeScript and SheetJS to process and generate spreadsheets.
|
||||
We'll explore how to load SheetJS in a NativeScript app; parse and generate
|
||||
spreadsheets stored on the device; and fetch and parse remote files.
|
||||
|
||||
The ["Complete Example"](#complete-example) creates an app that looks like the
|
||||
screenshots below:
|
||||
The "Complete Example" creates an app that looks like the screenshots below:
|
||||
|
||||
<table><thead><tr>
|
||||
<th><a href="#complete-example">iOS</a></th>
|
||||
@ -52,25 +51,16 @@ Angular and TypeScript is assumed.
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
**Real Devices**
|
||||
|
||||
| OS | Device | NS | Date |
|
||||
|:-----------|:--------------------|:---------|:-----------|
|
||||
| Android 30 | NVIDIA Shield | `8.9.2` | 2025-05-06 |
|
||||
| iOS 15.1 | iPad Pro | `8.9.2` | 2025-05-06 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | NS | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:---------|:-------------|:-----------|
|
||||
| Android 35 | Pixel 9 Pro XL | `8.9.2` | `darwin-x64` | 2025-05-06 |
|
||||
| iOS 18.4 | iPhone 16 Pro Max | `8.9.2` | `darwin-x64` | 2025-05-06 |
|
||||
| Android 35 | Pixel 9 | `8.9.2` | `win11-x64` | 2025-06-08 |
|
||||
| Android 36 | Pixel 9 | `8.9.2` | `linux-x64` | 2025-07-06 |
|
||||
| OS | Type | Device | NS | Date |
|
||||
|:-----------|:-----|:--------------------|:---------|:-----------|
|
||||
| Android 34 | Sim | Pixel 3a | `8.6.1` | 2023-12-04 |
|
||||
| iOS 17.0.1 | Sim | iPhone SE (3rd gen) | `8.6.1` | 2023-12-04 |
|
||||
| Android 29 | Real | NVIDIA Shield | `8.6.1` | 2023-12-04 |
|
||||
| iOS 15.1 | Real | iPad Pro | `8.6.1` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
:::danger Telemetry
|
||||
:::warning Telemetry
|
||||
|
||||
Before starting this demo, manually disable telemetry.
|
||||
|
||||
@ -78,15 +68,15 @@ NativeScript 8.6.1 split the telemetry into two parts: "usage" and "error". Both
|
||||
must be disabled separately:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns usage-reporting disable
|
||||
npx -y -p nativescript ns error-reporting disable
|
||||
npx -p nativescript ns usage-reporting disable
|
||||
npx -p nativescript ns error-reporting disable
|
||||
```
|
||||
|
||||
To verify telemetry was disabled:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns usage-reporting status
|
||||
npx -y -p nativescript ns error-reporting status
|
||||
npx -p nativescript ns usage-reporting status
|
||||
npx -p nativescript ns error-reporting status
|
||||
```
|
||||
|
||||
:::
|
||||
@ -98,9 +88,9 @@ imported from any component or script in the app.
|
||||
|
||||
The `@nativescript/core/file-system` package provides classes for file access.
|
||||
The `File` class does not support binary data, but the file access singleton
|
||||
from `@nativescript/core` does support reading and writing `ArrayBuffer` data.
|
||||
from `@nativescript/core` does support reading and writing `ArrayBuffer`.
|
||||
|
||||
Reading and writing data require a URL. The following snippet searches typical
|
||||
Reading and writing data require a URL. The following snippet searches typical
|
||||
document folders for a specified filename:
|
||||
|
||||
```ts
|
||||
@ -112,85 +102,6 @@ function get_url_for_filename(filename: string): string {
|
||||
}
|
||||
```
|
||||
|
||||
### App Configuration
|
||||
|
||||
Due to privacy concerns, apps must request file access. There are special APIs
|
||||
for accessing data and are subject to change in future platform versions.
|
||||
|
||||
<details>
|
||||
<summary><b>Technical Details</b> (click to show)</summary>
|
||||
|
||||
**iOS**
|
||||
|
||||
The following key/value pairs must be added to `Info.plist`:
|
||||
|
||||
```xml title="App_Resources/iOS/Info.plist (add highlighted lines)"
|
||||
<dict>
|
||||
<!-- highlight-start -->
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<!-- highlight-end -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Android**
|
||||
|
||||
Android security has evolved over the years. In newer Android versions, the
|
||||
following workarounds were required:
|
||||
|
||||
- `READ_EXTERNAL_STORAGE` and `WRITE_EXTERNAL_STORAGE` allow apps to access
|
||||
files outside of the app scope. These are required for scoped storage access.
|
||||
|
||||
When the demo was last tested, this option was enabled by default.
|
||||
|
||||
- `android:requestLegacyExternalStorage="true"` enabled legacy behavior in some
|
||||
older releases.
|
||||
|
||||
The manifest is saved to `App_Resources/Android/src/main/AndroidManifest.xml`:
|
||||
|
||||
```xml title="App_Resources/Android/src/main/AndroidManifest.xml (add highlighted lines)"
|
||||
<application
|
||||
<!-- highlight-next-line -->
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:name="com.tns.NativeScriptApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
android:hardwareAccelerated="true">
|
||||
```
|
||||
|
||||
- Permissions must be explicitly requested.
|
||||
|
||||
`@nativescript-community/perms` is a community module for managing permissions:
|
||||
|
||||
```ts title="App script or component"
|
||||
import { request } from '@nativescript-community/perms';
|
||||
import { File } from '@nativescript/core/file-system';
|
||||
```
|
||||
|
||||
Storage access must be requested before writing data:
|
||||
|
||||
```ts title="App script or component (before writing file)"
|
||||
/* request permissions */
|
||||
const res = await request('storage');
|
||||
```
|
||||
|
||||
The external paths can be resolved using the low-level APIs:
|
||||
|
||||
```ts title="App script or component (writing to downloads folder)"
|
||||
/* find Downloads folder */
|
||||
const dl_dir = android.os.Environment.DIRECTORY_DOWNLOADS;
|
||||
const dl = android.os.Environment.getExternalStoragePublicDirectory(dl_dir).getAbsolutePath();
|
||||
/* write to file */
|
||||
File.fromPath(dl + "/SheetJSNS.xls").writeSync(data);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Reading Local Files
|
||||
|
||||
`getFileAccess().readBufferAsync` can read data into an `ArrayBuffer` object.
|
||||
@ -265,7 +176,7 @@ import { read } from 'xlsx';
|
||||
const temp: string = path.join(knownFolders.temp().path, "pres.xlsx");
|
||||
|
||||
/* download file */
|
||||
const file = await getFile("https://docs.sheetjs.com/pres.xlsx", temp)
|
||||
const file = await getFile("https://sheetjs.com/pres.xlsx", temp)
|
||||
|
||||
/* get data */
|
||||
const ab: ArrayBuffer = await getFileAccess().readBufferAsync(file.path);
|
||||
@ -281,58 +192,54 @@ const wb = read(ab);
|
||||
0) Disable telemetry:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns usage-reporting disable
|
||||
npx -y -p nativescript ns error-reporting disable
|
||||
npx -p nativescript ns usage-reporting disable
|
||||
npx -p nativescript ns error-reporting disable
|
||||
```
|
||||
|
||||
1) Follow the official Environment Setup instructions[^8].
|
||||
|
||||
:::caution pass
|
||||
|
||||
In previous test runs, NativeScript did not support the latest Android API.
|
||||
The error message from `npx -y -p nativescript ns doctor android` clearly stated
|
||||
supported versions:
|
||||
When the demo was last tested, the latest version of the Android API was 34.
|
||||
NativeScript did not support that API level. The exact error message from
|
||||
`npx -p nativescript ns doctor ios` clearly stated supported versions:
|
||||
|
||||
<pre>
|
||||
<span {...r}>✖</span> No compatible version of the Android SDK Build-tools are installed on your system. You can install any version in the following range: '>=23 <=33'.
|
||||
</pre>
|
||||
(x is red, body text is yellow)
|
||||
```
|
||||
✖ No compatible version of the Android SDK Build-tools are installed on your system. You can install any version in the following range: '>=23 <=33'.
|
||||
```
|
||||
|
||||
If NativeScript does not properly support the latest API level, an older API
|
||||
version should be installed using Android Studio.
|
||||
|
||||
In a previous test run, the following packages were required:
|
||||
The SDK Platform `Android 13.0 ("Tiramisu")` was compatible with NativeScript.
|
||||
Until NativeScript properly supports API level 34, "Tiramisu" must be used.
|
||||
This requires installing the following packages from Android Studio:
|
||||
|
||||
- `Android 13.0 ("Tiramisu")` API Level `33`
|
||||
- `Android SDK Build-Tools` Version `33.0.2`
|
||||
|
||||
It is recommended to install the SDK Platform and corresponding Android SDK
|
||||
Build-Tools for the latest supported API level.
|
||||
|
||||
:::
|
||||
|
||||
2) Test the local system configuration for Android development:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns doctor android
|
||||
npx -p nativescript ns doctor android
|
||||
```
|
||||
|
||||
In the last macOS test, the following output was displayed:
|
||||
|
||||
<details open>
|
||||
<summary><b>Expected output</b> (click to hide)</summary>
|
||||
<details open><summary><b>Expected output</b> (click to hide)</summary>
|
||||
|
||||
<pre>
|
||||
<span {...g}>✔</span> Getting environment information
|
||||
|
||||
<b>No issues were detected.</b>
|
||||
<span {...g}>✔</span> Your ANDROID_HOME environment variable is set and points to correct directory.
|
||||
<span {...g}>✔</span> Your adb from the Android SDK is correctly installed.
|
||||
<span {...g}>✔</span> The Android SDK is installed.
|
||||
<span {...g}>✔</span> A compatible Android SDK for compilation is found.
|
||||
<span {...g}>✔</span> Javac is installed and is configured properly.
|
||||
<span {...g}>✔</span> The Java Development Kit (JDK) is installed and is configured properly.
|
||||
<span {...g}>✔</span> Getting NativeScript components versions information...
|
||||
<span {...g}>✔</span> Component nativescript has 8.9.2 version and is up to date.
|
||||
<span {...g}>✔</span> Getting environment information{'\n'}
|
||||
{'\n'}
|
||||
<b>No issues were detected.</b>{'\n'}
|
||||
<span {...g}>✔</span> Your ANDROID_HOME environment variable is set and points to correct directory.{'\n'}
|
||||
<span {...g}>✔</span> Your adb from the Android SDK is correctly installed.{'\n'}
|
||||
<span {...g}>✔</span> The Android SDK is installed.{'\n'}
|
||||
<span {...g}>✔</span> A compatible Android SDK for compilation is found.{'\n'}
|
||||
<span {...g}>✔</span> Javac is installed and is configured properly.{'\n'}
|
||||
<span {...g}>✔</span> The Java Development Kit (JDK) is installed and is configured properly.{'\n'}
|
||||
<span {...g}>✔</span> Getting NativeScript components versions information...{'\n'}
|
||||
<span {...g}>✔</span> Component nativescript has 8.6.1 version and is up to date.
|
||||
</pre>
|
||||
|
||||
</details>
|
||||
@ -340,28 +247,28 @@ In the last macOS test, the following output was displayed:
|
||||
3) Test the local system configuration for iOS development (macOS only):
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns doctor ios
|
||||
npx -p nativescript ns doctor ios
|
||||
```
|
||||
|
||||
In the last macOS test, the following output was displayed:
|
||||
|
||||
<details open>
|
||||
<summary><b>Expected output</b> (click to hide)</summary>
|
||||
<details open><summary><b>Expected output</b> (click to hide)</summary>
|
||||
|
||||
<pre>
|
||||
<span {...g}>✔</span> Getting environment information
|
||||
|
||||
<b>No issues were detected.</b>
|
||||
<span {...g}>✔</span> Xcode is installed and is configured properly.
|
||||
<span {...g}>✔</span> xcodeproj is installed and is configured properly.
|
||||
<span {...g}>✔</span> CocoaPods are installed.
|
||||
<span {...g}>✔</span> CocoaPods update is not required.
|
||||
<span {...g}>✔</span> CocoaPods are configured properly.
|
||||
<span {...g}>✔</span> Your current CocoaPods version is newer than 1.0.0.
|
||||
<span {...g}>✔</span> Python installed and configured correctly.
|
||||
<span {...g}>✔</span> Xcode version 16.3.0 satisfies minimum required version 10.
|
||||
<span {...g}>✔</span> Getting NativeScript components versions information...
|
||||
<span {...g}>✔</span> Component nativescript has 8.9.2 version and is up to date.
|
||||
<span {...g}>✔</span> Getting environment information{'\n'}
|
||||
{'\n'}
|
||||
No issues were detected.{'\n'}
|
||||
<span {...g}>✔</span> Xcode is installed and is configured properly.{'\n'}
|
||||
<span {...g}>✔</span> xcodeproj is installed and is configured properly.{'\n'}
|
||||
<span {...g}>✔</span> CocoaPods are installed.{'\n'}
|
||||
<span {...g}>✔</span> CocoaPods update is not required.{'\n'}
|
||||
<span {...g}>✔</span> CocoaPods are configured properly.{'\n'}
|
||||
<span {...g}>✔</span> Your current CocoaPods version is newer than 1.0.0.{'\n'}
|
||||
<span {...g}>✔</span> Python installed and configured correctly.{'\n'}
|
||||
<span {...g}>✔</span> The Python 'six' package is found.{'\n'}
|
||||
<span {...g}>✔</span> Xcode version 15.0.1 satisfies minimum required version 10.{'\n'}
|
||||
<span {...g}>✔</span> Getting NativeScript components versions information...{'\n'}
|
||||
<span {...g}>✔</span> Component nativescript has 8.6.1 version and is up to date.
|
||||
</pre>
|
||||
|
||||
</details>
|
||||
@ -371,21 +278,20 @@ In the last macOS test, the following output was displayed:
|
||||
4) Create a skeleton NativeScript + Angular app:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns create SheetJSNS --ng
|
||||
npx -p nativescript ns create SheetJSNS --ng
|
||||
```
|
||||
|
||||
5) Launch the app in the Android simulator to verify the app:
|
||||
5) Launch the app in the android simulator to verify the app:
|
||||
|
||||
```bash
|
||||
cd SheetJSNS
|
||||
npx -y -p nativescript ns run android
|
||||
npx -p nativescript ns run android
|
||||
```
|
||||
|
||||
(this may take a while)
|
||||
|
||||
Once the simulator launches and the test app is displayed, end the script by
|
||||
selecting the terminal and pressing <kbd>CTRL</kbd>+<kbd>C</kbd>. On Windows, if
|
||||
prompted to `Terminate batch job`, type <kbd>Y</kbd> and press Enter.
|
||||
selecting the terminal and entering the key sequence `CTRL + C`
|
||||
|
||||
:::note pass
|
||||
|
||||
@ -397,22 +303,11 @@ Emulator start failed with: No emulator image available for device identifier 'u
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
6) From the project folder, install the library:
|
||||
|
||||
In the most recent test, the build failed with an exception:
|
||||
|
||||
```
|
||||
WARNING: A restricted method in java.lang.System has been called
|
||||
WARNING: java.lang.System::load has been called by net.rubygrapefruit.platform.internal.NativeLibraryLoader in an unnamed module (file:/Users/sheetjs/.gradle/wrapper/dists/gradle-8.7-bin/bhs2wmbdwecv87pi65oeuq5iu/gradle-8.7/lib/native-platform-0.22-milestone-25.jar)
|
||||
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
|
||||
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
|
||||
```
|
||||
|
||||
**The NativeScript Gradle version is incompatible with Java 24!**
|
||||
|
||||
It is strongly recommended to roll back to Java 21.
|
||||
|
||||
:::
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
### Add SheetJS
|
||||
|
||||
@ -422,34 +317,29 @@ The goal of this section is to display the SheetJS library version number.
|
||||
|
||||
:::
|
||||
|
||||
6) From the project folder, install the SheetJS NodeJS module:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
7) Edit `src/app/item/items.component.ts` so that the component imports the
|
||||
SheetJS version string and adds it to a `version` variable in the component:
|
||||
|
||||
```ts title="src/app/item/items.component.ts (add highlighted lines)"
|
||||
```ts title="src/app/item/items.component.ts"
|
||||
// highlight-next-line
|
||||
import { version } from 'xlsx';
|
||||
import { Component, NO_ERRORS_SCHEMA, inject } from '@angular/core'
|
||||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
// ...
|
||||
|
||||
export class ItemsComponent {
|
||||
export class ItemsComponent implements OnInit {
|
||||
items: Array<Item>
|
||||
// highlight-next-line
|
||||
version = `SheetJS - ${version}`;
|
||||
itemService = inject(ItemService)
|
||||
page = inject(Page)
|
||||
|
||||
constructor(private itemService: ItemService) {}
|
||||
// ...
|
||||
```
|
||||
|
||||
8) Edit the template `src/app/item/items.component.html` to reference `version`
|
||||
in the title of the action bar:
|
||||
|
||||
```xml title="src/app/item/items.component.html (edit highlighted line)"
|
||||
```xml title="src/app/item/items.component.html"
|
||||
<!-- highlight-next-line -->
|
||||
<ActionBar [title]="version"></ActionBar>
|
||||
|
||||
@ -457,10 +347,10 @@ in the title of the action bar:
|
||||
<!-- ... -->
|
||||
```
|
||||
|
||||
9) End the script and relaunch the app in the Android simulator:
|
||||
9) Relaunch the app in the Android simulator:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run android
|
||||
npx -p nativescript ns run android
|
||||
```
|
||||
|
||||
The title bar should show the version.
|
||||
@ -471,7 +361,7 @@ The title bar should show the version.
|
||||
|
||||
10) Add the Import and Export buttons to the template:
|
||||
|
||||
```xml title="src/app/item/items.component.html (add highlighted lines)"
|
||||
```xml title="src/app/item/items.component.html"
|
||||
<ActionBar [title]="version"></ActionBar>
|
||||
|
||||
<!-- highlight-start -->
|
||||
@ -481,7 +371,7 @@ The title bar should show the version.
|
||||
<Button text="Export File" (tap)="export()" style="padding: 10px"></Button>
|
||||
</StackLayout>
|
||||
<!-- highlight-end -->
|
||||
<ListView [items]="itemService.items()">
|
||||
<ListView [items]="items">
|
||||
<!-- ... -->
|
||||
</ListView>
|
||||
<!-- highlight-next-line -->
|
||||
@ -490,15 +380,15 @@ The title bar should show the version.
|
||||
|
||||
11) Add the `import` and `export` methods in the component script:
|
||||
|
||||
```ts title="src/app/item/items.component.ts (add highlighted lines)"
|
||||
```ts title="src/app/item/items.component.ts"
|
||||
// highlight-start
|
||||
import { version, utils, read, write } from 'xlsx';
|
||||
import { Dialogs, getFileAccess } from '@nativescript/core';
|
||||
import { Folder, knownFolders, path } from '@nativescript/core/file-system';
|
||||
// highlight-end
|
||||
import { Component, NO_ERRORS_SCHEMA, inject } from '@angular/core'
|
||||
import { NativeScriptCommonModule, NativeScriptRouterModule } from '@nativescript/angular'
|
||||
import { Page } from '@nativescript/core'
|
||||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
import { Item } from './item'
|
||||
import { ItemService } from './item.service'
|
||||
|
||||
// highlight-start
|
||||
@ -508,12 +398,19 @@ function get_url_for_filename(filename: string): string {
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
// ...
|
||||
@Component({
|
||||
selector: 'ns-items',
|
||||
templateUrl: './items.component.html',
|
||||
})
|
||||
export class ItemsComponent implements OnInit {
|
||||
items: Array<Item>
|
||||
version: string = `SheetJS - ${version}`;
|
||||
|
||||
export class ItemsComponent {
|
||||
version = `SheetJS - ${version}`;
|
||||
itemService = inject(ItemService)
|
||||
page = inject(Page)
|
||||
constructor(private itemService: ItemService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.items = this.itemService.getItems()
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
/* Import button */
|
||||
@ -524,23 +421,16 @@ export class ItemsComponent {
|
||||
async export() {
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
12) End the script and relaunch the app in the Android simulator:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run android
|
||||
```
|
||||
|
||||
Two buttons should appear just below the header:
|
||||
12) Restart the app process. Two buttons should show up at the top:
|
||||
|
||||

|
||||
|
||||
13) Implement import and export by adding the highlighted lines:
|
||||
|
||||
```ts title="src/app/item/items.component.ts (add highlighted lines)"
|
||||
```ts title="src/app/item/items.component.ts"
|
||||
/* Import button */
|
||||
async import() {
|
||||
// highlight-start
|
||||
@ -560,7 +450,7 @@ Two buttons should appear just below the header:
|
||||
const ws = wb.Sheets[wsname];
|
||||
|
||||
/* update table */
|
||||
this.itemService.items.set(utils.sheet_to_json(ws));
|
||||
this.items = utils.sheet_to_json<Item>(ws);
|
||||
} catch(e) { await Dialogs.alert(e.message); }
|
||||
// highlight-end
|
||||
}
|
||||
@ -573,7 +463,7 @@ Two buttons should appear just below the header:
|
||||
|
||||
try {
|
||||
/* create worksheet from data */
|
||||
const ws = utils.json_to_sheet(this.itemService.items());
|
||||
const ws = utils.json_to_sheet(this.items);
|
||||
|
||||
/* create workbook from worksheet */
|
||||
const wb = utils.book_new();
|
||||
@ -595,57 +485,36 @@ Two buttons should appear just below the header:
|
||||
14) Launch the app in the Android Simulator:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run android
|
||||
```
|
||||
npx -p nativescript ns run android
|
||||
````
|
||||
|
||||
If the app does not automatically launch, manually open the `SheetJSNS` app.
|
||||
|
||||
15) Tap "Export File". A dialog will print where the file was written. Typically
|
||||
the URL is `/data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls`
|
||||
|
||||
16) Pull the file from the simulator. The following commands should be run in a
|
||||
new terminal or PowerShell window:
|
||||
16) Pull the file from the simulator:
|
||||
|
||||
```bash
|
||||
adb root
|
||||
adb pull /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls SheetJSNS.xls
|
||||
```
|
||||
|
||||
If the emulator cannot be rooted, the following workaround should be used:
|
||||
If the emulator cannot be rooted:
|
||||
|
||||
```bash
|
||||
adb shell "run-as org.nativescript.SheetJSNS cat /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls" > SheetJSNS.xls
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
In the most recent `win11-x64` test, the generated file was corrupt. This is a
|
||||
known issue with Windows redirects. The solution is to generate a Base64-encoded
|
||||
string and decode using PowerShell:
|
||||
|
||||
```bash
|
||||
adb shell "run-as org.nativescript.SheetJSNS base64 /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls" > SheetJSNS.xls.b64
|
||||
$b64 = Get-Content -Path .\SheetJSNS.xls.b64 -Raw
|
||||
$bytes = [Convert]::FromBase64String($b64)
|
||||
[System.IO.File]::WriteAllBytes("SheetJSNS.xls", $bytes)
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
17) Open `SheetJSNS.xls` with a spreadsheet editor.
|
||||
|
||||
After the header row, insert a row and make the following assignments:
|
||||
After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:
|
||||
|
||||
- Set cell `A2` to `0`
|
||||
- Set cell `B2` to `SheetJS` (type `'SheetJS` in the formula bar)
|
||||
- Set cell `C2` to `Library` (type `'Library` in the formula bar)
|
||||
|
||||
After making the changes, the worksheet should look like the following:
|
||||
|
||||
```text
|
||||
```
|
||||
id | name | role
|
||||
# highlight-next-line
|
||||
0 | SheetJS | Library
|
||||
1 | Ter Stegen | Goalkeeper
|
||||
3 | Piqué | Defender
|
||||
...
|
||||
```
|
||||
|
||||
@ -655,65 +524,34 @@ id | name | role
|
||||
adb push SheetJSNS.xls /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls
|
||||
```
|
||||
|
||||
If the emulator cannot be rooted, raw byte operations work in macOS and Linux:
|
||||
If the emulator cannot be rooted:
|
||||
|
||||
```bash
|
||||
dd if=SheetJSNS.xls | adb shell "run-as org.nativescript.SheetJSNS dd of=/data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls"
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
In the most recent `win11-x64` test, neither workaround worked. The solution is
|
||||
to generate a Base64-encoded string and decode in `adb`. After closing Excel and
|
||||
saving the `SheetJSNS.xls` file, run the following commands:
|
||||
|
||||
```bash
|
||||
$bytes = [IO.File]::ReadAllBytes(".\SheetJSNS.xls")
|
||||
$b64 = [Convert]::ToBase64String($bytes)
|
||||
echo $b64 | adb shell "run-as org.nativescript.SheetJSNS base64 -d | run-as org.nativescript.SheetJSNS dd of=/data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls"
|
||||
```
|
||||
|
||||
:::
|
||||
```
|
||||
|
||||
19) Tap "Import File". A dialog will print the path of the file that was read.
|
||||
The first item in the list will change.
|
||||
|
||||

|
||||
|
||||
### iOS
|
||||
|
||||
:::danger pass
|
||||
|
||||
**iOS testing can only be performed on Apple hardware running macOS!**
|
||||
|
||||
Xcode and iOS simulators are not available on Windows or Linux.
|
||||
|
||||
Scroll down to ["Fetching Files"](#android-device) for Android device testing.
|
||||
|
||||
:::
|
||||
|
||||
20) Launch the app in the iOS Simulator:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run ios
|
||||
npx -p nativescript ns run ios
|
||||
```
|
||||
|
||||
21) Tap "Export File". A dialog will print where the file was written.
|
||||
|
||||
22) Open the file with a spreadsheet editor.
|
||||
|
||||
After the header row, insert a row and make the following assignments:
|
||||
After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:
|
||||
|
||||
- Set cell `A2` to `0`
|
||||
- Set cell `B2` to `SheetJS` (type `'SheetJS` in the formula bar)
|
||||
- Set cell `C2` to `Library` (type `'Library` in the formula bar)
|
||||
|
||||
After making the changes, the worksheet should look like the following:
|
||||
|
||||
```text
|
||||
```
|
||||
id | name | role
|
||||
# highlight-next-line
|
||||
0 | SheetJS | Library
|
||||
1 | Ter Stegen | Goalkeeper
|
||||
3 | Piqué | Defender
|
||||
...
|
||||
```
|
||||
|
||||
@ -726,199 +564,86 @@ The first item in the list will change:
|
||||
|
||||
### Fetching Files
|
||||
|
||||
25) Replace `item.service.ts` with the following:
|
||||
25) In `src/app/item/items.component.ts`, make `ngOnInit` asynchronous:
|
||||
|
||||
```ts title="src/app/item/items.component.ts"
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.items = await this.itemService.getItems()
|
||||
}
|
||||
```
|
||||
|
||||
26) Replace `item.service.ts` with the following:
|
||||
|
||||
```ts title="src/app/item/item.service.ts"
|
||||
import { read, utils } from 'xlsx';
|
||||
import { Injectable, signal, effect } from '@angular/core'
|
||||
import { knownFolders, path, getFileAccess } from '@nativescript/core';
|
||||
import { getFile } from '@nativescript/core/http';
|
||||
import { Item } from './item'
|
||||
import { Injectable } from '@angular/core'
|
||||
|
||||
import { knownFolders, path, getFileAccess } from '@nativescript/core'
|
||||
import { getFile } from '@nativescript/core/http';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
import { Item } from './item'
|
||||
interface IPresident { Name: string; Index: number };
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ItemService {
|
||||
items = signal<Item[]>([]);
|
||||
constructor() { effect(() => { (async() => {
|
||||
/* fetch https://docs.sheetjs.com/pres.xlsx */
|
||||
private items: Array<Item>;
|
||||
|
||||
async getItems(): Promise<Array<Item>> {
|
||||
/* fetch https://sheetjs.com/pres.xlsx */
|
||||
const temp: string = path.join(knownFolders.temp().path, "pres.xlsx");
|
||||
const ab = await getFile("https://docs.sheetjs.com/pres.xlsx", temp)
|
||||
const ab = await getFile("https://sheetjs.com/pres.xlsx", temp)
|
||||
/* read the temporary file */
|
||||
const wb = read(await getFileAccess().readBufferAsync(ab.path));
|
||||
/* translate the first worksheet to the required Item type */
|
||||
const data = utils.sheet_to_json<IPresident>(wb.Sheets[wb.SheetNames[0]]);
|
||||
/* update state */
|
||||
this.items.set(data.map((pres, id) => ({id, name: pres.Name, role: ""+pres.Index} as Item)));
|
||||
})(); }); }
|
||||
return this.items = data.map((pres, id) => ({id, name: pres.Name, role: ""+pres.Index} as Item));
|
||||
}
|
||||
|
||||
getItem(id: number): Item {
|
||||
return this.items().find((item) => item.id === id)
|
||||
return this.items.filter((item) => item.id === id)[0]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
26) End the script and relaunch the app in the Android simulator:
|
||||
27) Relaunch the app in the Android simulator:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run android
|
||||
```
|
||||
npx -p nativescript ns run android
|
||||
````
|
||||
|
||||
The app should show Presidential data.
|
||||
|
||||
### Android Device
|
||||
|
||||
27) Connect an Android device using a USB cable.
|
||||
28) Connect an Android device using a USB cable.
|
||||
|
||||
If the device asks to allow USB debugging, tap "Allow".
|
||||
|
||||
28) Close any Android / iOS emulators.
|
||||
29) Close any Android / iOS emulators.
|
||||
|
||||
29) Enable "Legacy External Storage" in the Android app. The manifest is stored
|
||||
at `App_Resources/Android/src/main/AndroidManifest.xml`:
|
||||
|
||||
```xml title="App_Resources/Android/src/main/AndroidManifest.xml (add highlighted line)"
|
||||
<application
|
||||
<!-- highlight-next-line -->
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:name="com.tns.NativeScriptApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
android:hardwareAccelerated="true">
|
||||
```
|
||||
|
||||
30) Install the `@nativescript-community/perms` dependency:
|
||||
30) Build APK and run on device:
|
||||
|
||||
```bash
|
||||
npm i --save @nativescript-community/perms
|
||||
```
|
||||
|
||||
31) Add the highlighted lines to `items.component.ts`:
|
||||
|
||||
- Import `File` from NativeScript core and `request` from the new dependency:
|
||||
|
||||
```ts title="items.component.ts (add highlighted lines)"
|
||||
import { Dialogs, getFileAccess, Utils } from '@nativescript/core';
|
||||
// highlight-start
|
||||
import { request } from '@nativescript-community/perms';
|
||||
import { Folder, knownFolders, path, File } from '@nativescript/core/file-system';
|
||||
// highlight-end
|
||||
import { Component, OnInit } from '@angular/core'
|
||||
// ...
|
||||
```
|
||||
|
||||
- Add a new write operation to the `export` method:
|
||||
|
||||
```ts title="items.component.ts (add highlighted lines)"
|
||||
/* attempt to save Uint8Array to file */
|
||||
await getFileAccess().writeBufferAsync(url, global.isAndroid ? (Array.from(u8) as any) : u8);
|
||||
await Dialogs.alert(`Wrote to SheetJSNS.xls at ${url}`);
|
||||
|
||||
/* highlight-start */
|
||||
if(global.isAndroid) {
|
||||
/* request permissions */
|
||||
const res = await request('storage');
|
||||
/* write to Downloads folder */
|
||||
const dl = android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
|
||||
File.fromPath(dl + "/SheetJSNS.xls").writeSync(Array.from(u8));
|
||||
}
|
||||
/* highlight-end */
|
||||
} catch(e) { await Dialogs.alert(e.message); }
|
||||
```
|
||||
|
||||
32) Build APK and run on device:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run android
|
||||
npx -p nativescript ns run android
|
||||
```
|
||||
|
||||
If the Android emulators are closed and an Android device is connected, the last
|
||||
command will build an APK and install on the device.
|
||||
|
||||
<details open>
|
||||
<summary><b>Android Device Testing</b> (click to hide)</summary>
|
||||
|
||||
When the app launches, if the SheetJS library is loaded and if the device is
|
||||
connected to the Internet, a list of Presidents should be displayed.
|
||||
|
||||
Tap "Export File". The app will show an alert. Tap "OK".
|
||||
|
||||
Switch to the "Files" app and open the "Downloads" folder. There should be a new
|
||||
file named `SheetJSNS.xls`.
|
||||
|
||||
</details>
|
||||
|
||||
### iOS Device
|
||||
|
||||
33) Connect an iOS device using a USB cable
|
||||
31) Connect an iOS device using a USB cable
|
||||
|
||||
34) Close any Android / iOS emulators.
|
||||
32) Close any Android / iOS emulators.
|
||||
|
||||
35) Enable developer code signing certificates:
|
||||
33) Enable developer code signing certificates[^9]
|
||||
|
||||
Open `platforms/ios/SheetJSNS.xcodeproj/project.xcworkspace` in Xcode. Select
|
||||
the "Project Navigator" and select `SheetJSNS`. In the main view, select the
|
||||
`SheetJSNS` target. Click "Signing & Capabilities". Under "Signing", select a
|
||||
team in the dropdown menu.
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested, Xcode repeatedly crashed.
|
||||
|
||||
The issue was resolved by cleaning the project:
|
||||
34) Run on device:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns platform clean ios
|
||||
npx -p nativescript ns run ios
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
36) Add the following key/value pairs to `Info.plist`:
|
||||
|
||||
```xml title="App_Resources/iOS/Info.plist (add highlighted lines)"
|
||||
<dict>
|
||||
<!-- highlight-start -->
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<!-- highlight-end -->
|
||||
```
|
||||
|
||||
37) Run on device:
|
||||
|
||||
```bash
|
||||
npx -y -p nativescript ns run ios
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
If this is the first time testing an app on a device, the certificate must be
|
||||
trusted on the device:
|
||||
|
||||
Under "Settings" > "General" > "VPN & Device Management", there should be a
|
||||
"Apple Development" certificate in the "DEVELOPER APP" section. Select the
|
||||
certificate and confirm that "SheetJSNS" is listed under "APPS". Tap "Trust ..."
|
||||
and tap "Trust" in the popup.
|
||||
|
||||
:::
|
||||
|
||||
<details open>
|
||||
<summary><b>iOS Device Testing</b> (click to hide)</summary>
|
||||
|
||||
When the app launches, if the SheetJS library is loaded and if the device is
|
||||
connected to the Internet, a list of Presidents should be displayed.
|
||||
|
||||
Tap "Export File". The app will show an alert. Tap "OK".
|
||||
|
||||
Switch to the "Files" app and repeatedly tap "<". In the "Browse" window, tap
|
||||
"On My iPhone". There should be a new folder named "SheetJSNS". Tap the folder
|
||||
and look for the file named `SheetJSNS.xls`.
|
||||
|
||||
</details>
|
||||
|
||||
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^2]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^3]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
|
||||
@ -927,3 +652,4 @@ and look for the file named `SheetJSNS.xls`.
|
||||
[^6]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
|
||||
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^8]: See ["Local setup"](https://docs.nativescript.org/setup/#local-setup) in the NativeScript documentation. For Windows and Linux, follow the "Android" instructions. For macOS, follow both the iOS and Android instructions.
|
||||
[^9]: The [Flutter documentation](https://docs.flutter.dev/get-started/install/macos?tab=ios15#enable-developer-code-signing-certificates) covers the instructions in more detail. The correct workspace is `platforms/ios/SheetJSNS.xcodeproj/project.xcworkspace`
|
||||
@ -41,20 +41,10 @@ The ["Demo"](#demo) creates an app that looks like the screenshots below:
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
**Real Devices**
|
||||
|
||||
| OS | Device | Quasar | Date |
|
||||
|:-----------|:--------------------|:---------|:-----------|
|
||||
| Android 34 | NVIDIA Shield | `2.18.1` | 2025-04-17 |
|
||||
| iOS 15.1 | iPad Pro | `2.18.1` | 2025-04-17 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | Quasar | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:---------|:-------------|:-----------|
|
||||
| Android 35 | Pixel 9 Pro XL | `2.18.1` | `darwin-arm` | 2025-04-17 |
|
||||
| iOS 18.2 | iPhone 16 Pro Max | `2.18.1` | `darwin-arm` | 2025-04-17 |
|
||||
| Android 36 | Pixel 9 Pro XL | `2.18.1` | `win11-x64` | 2025-04-17 |
|
||||
| OS | Type | Device | Quasar | Date |
|
||||
|:-----------|:-----|:--------------------|:---------|:-----------|
|
||||
| Android 34 | Sim | Pixel 3a | `2.14.1` | 2023-12-04 |
|
||||
| iOS 17.0.1 | Sim | iPhone SE (3rd gen) | `2.14.1` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
@ -77,7 +67,7 @@ cd ..
|
||||
|
||||
### Reading data
|
||||
|
||||
The `QFile`[^1] component presents an API reminiscent of File Input elements:
|
||||
The QFile[^1] component presents an API reminiscent of File Input elements:
|
||||
|
||||
```html
|
||||
<q-file label="Load File" filled label-color="orange" @input="updateFile"/>
|
||||
@ -167,32 +157,13 @@ window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
|
||||
The demo draws from the ViteJS example. Familiarity with VueJS and TypeScript
|
||||
is assumed.
|
||||
|
||||
### Platform Setup
|
||||
|
||||
0) Ensure all of the dependencies are installed. Install the CLI globally:
|
||||
|
||||
```bash
|
||||
npm i -g @quasar/cli cordova
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In some systems, the command must be run as the root user:
|
||||
|
||||
```bash
|
||||
sudo npm i -g @quasar/cli cordova
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
Quasar requires Java 17
|
||||
|
||||
</details>
|
||||
|
||||
### Base Project
|
||||
(you may need to run `sudo npm i -g` if there are permission issues)
|
||||
|
||||
1) Create a new app:
|
||||
|
||||
@ -204,17 +175,19 @@ npm init quasar
|
||||
|
||||
When prompted:
|
||||
|
||||
- "What would you like to build?": `App with Quasar CLI, let's go!`
|
||||
- "What would you like to build?": `App with Quasar CLI`
|
||||
- "Project folder": `SheetJSQuasar`
|
||||
- "Pick Quasar version": `Quasar v2 (Vue 3 | latest and greatest)`
|
||||
- "Pick script type": `Typescript`
|
||||
- "Pick Quasar App CLI variant": `Quasar App CLI with Vite`
|
||||
- "Package name": (press <kbd>Enter</kbd>, it will use the default `sheetjsquasar`)
|
||||
- "Package name": (just press enter, it will use the default `sheetjsquasar`
|
||||
- "Project product name": `SheetJSQuasar`
|
||||
- "Project description": `SheetJS + Quasar`
|
||||
- "Pick a Vue component style": `Composition API with <script setup>`
|
||||
- "Author": (just press enter, it will use your git config settings)
|
||||
- "Pick a Vue component style": `Composition API`
|
||||
- "Pick your CSS preprocessor": `None`
|
||||
- "Check the features needed for your project": Deselect everything (scroll down to each selected item and press <kbd>Space</kbd>)
|
||||
- "Install project dependencies": `Yes, use npm`
|
||||
- "Check the features needed for your project": Deselect everything
|
||||
- "Install project dependencies": `No`
|
||||
|
||||
2) Install dependencies:
|
||||
|
||||
@ -229,105 +202,18 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
3) Set up Cordova:
|
||||
|
||||
```bash
|
||||
npx cordova telemetry off
|
||||
npx quasar mode add cordova
|
||||
npx quasar mode remove capacitor
|
||||
quasar mode add cordova
|
||||
```
|
||||
|
||||
When prompted, enter the app id `org.sheetjs.quasar`.
|
||||
|
||||
4) Add the Dialog plugin to the `plugins` array in `quasar.config.ts`:
|
||||
|
||||
```js title="quasar.config.ts (edit highlighted line)"
|
||||
// Quasar plugins
|
||||
// highlight-next-line
|
||||
plugins: ['Dialog']
|
||||
```
|
||||
|
||||
5) Download [`IndexPage.vue`](pathname:///quasar/IndexPage.vue) and replace the
|
||||
existing page script `src/pages/IndexPage.vue`:
|
||||
|
||||
```bash
|
||||
curl -L -o src/pages/IndexPage.vue https://docs.sheetjs.com/quasar/IndexPage.vue
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
6) Create the Android project:
|
||||
It will create a new `src-cordova` folder. Continue in that folder:
|
||||
|
||||
```bash
|
||||
cd src-cordova
|
||||
npx cordova platform add android
|
||||
npx cordova plugin add cordova-plugin-wkwebview-engine
|
||||
npx cordova plugin add cordova-plugin-file
|
||||
cd ..
|
||||
```
|
||||
|
||||
7) Start the simulator:
|
||||
|
||||
```bash
|
||||
npx quasar dev -m cordova -T android
|
||||
```
|
||||
|
||||
If prompted to select an external IP, press <kbd>Enter</kbd>.
|
||||
|
||||
:::caution pass
|
||||
|
||||
If the app is blank or not refreshing, delete the app and close the simulator,
|
||||
then restart the development process.
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
In some test runs, the command failed with a Gradle error
|
||||
|
||||
```
|
||||
Could not find an installed version of Gradle either in Android Studio,
|
||||
or on your system to install the gradle wrapper. Please include gradle
|
||||
in your path, or install Android Studio
|
||||
```
|
||||
|
||||
[Gradle](https://gradle.org/) (the complete version) must be extracted and the
|
||||
`bin` folder must be added to the user PATH variable. After adding to PATH,
|
||||
launch a new PowerShell or Command Prompt window and run the command.
|
||||
|
||||
:::
|
||||
|
||||
To test that reading works:
|
||||
|
||||
- Download https://docs.sheetjs.com/pres.numbers
|
||||
- Open the Downloads folder in Finder or Explorer
|
||||
- Click and drag `pres.numbers` from the window into the simulator.
|
||||
- Tap "Load File", tap the `≡` icon, tap "Downloads" and select `pres.numbers`.
|
||||
|
||||
To test that writing works:
|
||||
|
||||
- Tap "Save File". You will see a popup with a location.
|
||||
- Pull the file from the simulator and verify the contents:
|
||||
|
||||
```bash
|
||||
adb exec-out run-as org.sheetjs.quasar cat files/files/SheetJSQuasar.xlsx > /tmp/SheetJSQuasar.xlsx
|
||||
npx xlsx-cli /tmp/SheetJSQuasar.xlsx
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell file redirects will corrupt binary data. In Windows, commands must be
|
||||
run from a Command Prompt session.
|
||||
|
||||
:::
|
||||
|
||||
### iOS
|
||||
|
||||
8) Create the iOS project:
|
||||
|
||||
```bash
|
||||
cd src-cordova
|
||||
npx cordova platform add ios
|
||||
npx cordova plugin add cordova-plugin-wkwebview-engine
|
||||
npx cordova plugin add cordova-plugin-file
|
||||
cd ..
|
||||
cordova platform add ios
|
||||
cordova plugin add cordova-plugin-wkwebview-engine
|
||||
cordova plugin add cordova-plugin-file
|
||||
```
|
||||
|
||||
:::note pass
|
||||
@ -335,14 +221,20 @@ cd ..
|
||||
If there is an error `Could not load API for iOS project`, it needs to be reset:
|
||||
|
||||
```bash
|
||||
npx cordova platform rm ios
|
||||
npx cordova platform add ios
|
||||
npx cordova plugin add cordova-plugin-file
|
||||
cordova platform rm ios
|
||||
cordova platform add ios
|
||||
cordova plugin add cordova-plugin-file
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
9) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
Return to the project directory:
|
||||
|
||||
```bash
|
||||
cd ..
|
||||
```
|
||||
|
||||
11) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
The following lines must be added to `src-cordova/platforms/ios/SheetJSQuasar/SheetJSQuasar-Info.plist`:
|
||||
|
||||
```xml title="src-cordova/platforms/ios/SheetJSQuasar/SheetJSQuasar-Info.plist (add to file)"
|
||||
@ -360,14 +252,12 @@ The following lines must be added to `src-cordova/platforms/ios/SheetJSQuasar/Sh
|
||||
(The root element of the document is `plist` and it contains one `dict` child)
|
||||
|
||||
|
||||
10) Start the development server:
|
||||
5) Start the development server:
|
||||
|
||||
```bash
|
||||
npx quasar dev -m cordova -T ios
|
||||
quasar dev -m ios
|
||||
```
|
||||
|
||||
If prompted to select an external IP, press <kbd>Enter</kbd>.
|
||||
|
||||
:::caution pass
|
||||
|
||||
If the app is blank or not refreshing, delete the app and close the simulator,
|
||||
@ -375,6 +265,52 @@ then restart the development process.
|
||||
|
||||
:::
|
||||
|
||||
6) Add the Dialog plugin to `quasar.config.js`:
|
||||
|
||||
```js title="quasar.config.js"
|
||||
framework: {
|
||||
config: {},
|
||||
// ...
|
||||
// Quasar plugins
|
||||
// highlight-next-line
|
||||
plugins: ['Dialog']
|
||||
},
|
||||
```
|
||||
|
||||
7) In the template section of `src/pages/IndexPage.vue`, replace the example
|
||||
with a Table, Save button and Load file picker component:
|
||||
|
||||
```html title="src/pages/IndexPage.vue"
|
||||
<template>
|
||||
<q-page class="row items-center justify-evenly">
|
||||
<!-- highlight-start -->
|
||||
<q-table :rows="todos" />
|
||||
<q-btn-group>
|
||||
<q-file label="Load File" filled label-color="orange" @input="updateFile"/>
|
||||
<q-btn label="Save File" @click="saveFile" />
|
||||
</q-btn-group>
|
||||
<!-- highlight-end -->
|
||||
</q-page>
|
||||
</template>
|
||||
```
|
||||
|
||||
This uses two functions that should be added to the component script:
|
||||
|
||||
```ts title="src/pages/IndexPage.vue"
|
||||
const meta = ref<Meta>({
|
||||
totalCount: 1200
|
||||
});
|
||||
// highlight-start
|
||||
function saveFile() {
|
||||
}
|
||||
async function updateFile(v: Event) {
|
||||
}
|
||||
return { todos, meta, saveFile, updateFile };
|
||||
// highlight-end
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The app should now show two buttons at the bottom:
|
||||
|
||||

|
||||
@ -386,9 +322,41 @@ then restart the development process.
|
||||
|
||||
:::
|
||||
|
||||
8) Wire up the `updateFile` function:
|
||||
|
||||
```ts title="src/pages/IndexPage.vue"
|
||||
import { defineComponent, ref } from 'vue';
|
||||
// highlight-start
|
||||
import { read, write, utils } from 'xlsx';
|
||||
import { useQuasar } from 'quasar';
|
||||
// highlight-end
|
||||
|
||||
export default defineComponent({
|
||||
// ...
|
||||
// highlight-start
|
||||
const $q = useQuasar();
|
||||
function dialogerr(e: Error) { $q.dialog({title: "Error!", message: e.message || String(e)}); }
|
||||
// highlight-end
|
||||
function saveFile() {
|
||||
}
|
||||
async function updateFile(v: Event) {
|
||||
// highlight-start
|
||||
try {
|
||||
const files = (v.target as HTMLInputElement).files;
|
||||
if(!files || files.length == 0) return;
|
||||
|
||||
const wb = read(await files[0].arrayBuffer());
|
||||
|
||||
const data = utils.sheet_to_json<any>(wb.Sheets[wb.SheetNames[0]]);
|
||||
todos.value = data.map(row => ({id: row.Index, content: row.Name}));
|
||||
} catch(e) { dialogerr(e); }
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
To test that reading works:
|
||||
|
||||
- Download https://docs.sheetjs.com/pres.numbers
|
||||
- Download <https://sheetjs.com/pres.numbers>
|
||||
- In the simulator, click the Home icon to return to the home screen
|
||||
- Click on the "Files" icon
|
||||
- Click and drag `pres.numbers` from a Finder window into the simulator.
|
||||
@ -403,11 +371,46 @@ To test that reading works:
|
||||
|
||||
Once selected, the screen should refresh with new contents.
|
||||
|
||||
9) Wire up the `saveFile` function:
|
||||
|
||||
```ts title="src/pages/IndexPage.vue"
|
||||
function saveFile() {
|
||||
// highlight-start
|
||||
/* generate workbook from state */
|
||||
const ws = utils.json_to_sheet(todos.value);
|
||||
const wb = utils.book_new();
|
||||
utils.book_append_sheet(wb, ws, "SheetJSQuasar");
|
||||
const u8: Uint8Array = write(wb, {bookType: "xlsx", type: "buffer"});
|
||||
const dir: string = $q.cordova.file.documentsDirectory || $q.cordova.file.externalApplicationStorageDirectory;
|
||||
|
||||
/* save to file */
|
||||
window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
|
||||
try {
|
||||
fs.root.getFile("SheetJSQuasar.xlsx", {create: true}, entry => {
|
||||
const msg = `File stored at ${dir} ${entry.fullPath}`;
|
||||
entry.createWriter(writer => {
|
||||
try {
|
||||
const data = new Blob([u8], {type: "application/vnd.ms-excel"});
|
||||
writer.onwriteend = () => {
|
||||
try {
|
||||
$q.dialog({title: "Success!", message: msg});
|
||||
} catch(e) { dialogerr(e); }
|
||||
};
|
||||
writer.onerror = dialogerr;
|
||||
writer.write(data);
|
||||
} catch(e) { dialogerr(e); }
|
||||
}, dialogerr);
|
||||
}, dialogerr);
|
||||
} catch(e) { dialogerr(e) }
|
||||
}, dialogerr);
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
The page should revert to the old contents.
|
||||
|
||||
To test that writing works:
|
||||
|
||||
- Close the app in the simulator and re-launch the app.
|
||||
|
||||
- Click "Save File". You will see a popup with a location:
|
||||
|
||||

|
||||
@ -452,81 +455,38 @@ id,content
|
||||
46,Joseph Biden
|
||||
```
|
||||
|
||||
### Android Device
|
||||
**Android**
|
||||
|
||||
11) Close all open emulators and simulators.
|
||||
|
||||
12) Disconnect any iOS or Android devices connected to the computer.
|
||||
|
||||
13) Connect the Android device to the computer.
|
||||
|
||||
14) Start the dev process:
|
||||
10) Create the Android project:
|
||||
|
||||
```bash
|
||||
npx quasar dev -m cordova -T android
|
||||
cd src-cordova
|
||||
cordova platform add android
|
||||
cd ..
|
||||
```
|
||||
|
||||
If prompted to select an external IP, press <kbd>Enter</kbd>.
|
||||
|
||||
15) Test the application:
|
||||
|
||||
- Press the Home button (or swipe up with one finger) and switch to Browser.
|
||||
- Download https://docs.sheetjs.com/pres.numbers
|
||||
- Press the Home button (or swipe up with one finger) and select the `SheetJSQuasar` app
|
||||
- Tap the "Load" button, then select "Choose File" and select the downloaded `pres.numbers`
|
||||
|
||||
The table will update with new data.
|
||||
|
||||
:::warning pass
|
||||
|
||||
The "Save File" process will write files. However, Android 30+ requires special
|
||||
methods ("Storage Access Framework") that are not implemented in Quasar.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
### iOS Device
|
||||
|
||||
16) Close all open emulators and simulators.
|
||||
|
||||
17) Disconnect any iOS or Android devices connected to the computer.
|
||||
|
||||
18) Connect the iOS device to the computer.
|
||||
|
||||
19) Open the Xcode project:
|
||||
11) Start the simulator:
|
||||
|
||||
```bash
|
||||
open src-cordova/platforms/ios/SheetJSQuasar.xcodeproj
|
||||
quasar dev -m android
|
||||
```
|
||||
|
||||
Select "SheetJSQuasar" in the Navigator. In the main pane, select "Signing &
|
||||
Capabilities" and ensure a Team is selected. Save and close the project.
|
||||
To test that reading works:
|
||||
|
||||
20) Start the dev process:
|
||||
- Click and drag `pres.numbers` from a Finder window into the simulator.
|
||||
- Tap "Load", tap the `≡` icon, tap "Downloads" and select `pres.numbers`.
|
||||
|
||||
To test that writing works:
|
||||
|
||||
- Tap "Save File". You will see a popup with a location.
|
||||
|
||||
- Pull the file from the simulator and verify the contents:
|
||||
|
||||
```bash
|
||||
npx quasar dev -m cordova -T ios
|
||||
adb exec-out run-as org.sheetjs.quasar cat files/files/SheetJSQuasar.xlsx > /tmp/SheetJSQuasar.xlsx
|
||||
npx xlsx-cli /tmp/SheetJSQuasar.xlsx
|
||||
```
|
||||
|
||||
If prompted to select an external IP, press <kbd>Enter</kbd>.
|
||||
|
||||
11) Test the application:
|
||||
|
||||
- Press the Home button (or swipe up with one finger) and switch to Safari.
|
||||
- Download https://docs.sheetjs.com/pres.numbers
|
||||
- Press the Home button (or swipe up with one finger) and select the `SheetJSQuasar` app
|
||||
- Tap the "Load" button, then select "Choose File" and select the downloaded `pres.numbers`
|
||||
|
||||
The table will update with new data.
|
||||
|
||||
- Tap "Save File"
|
||||
- Press the Home button (or swipe up with one finger) and switch to Files.
|
||||
- Tap `<` until the main "Browse" window is displayed, then select "On My iPhone"
|
||||
- Look for the "SheetJSQuasar" folder and tap `SheetJSQuasar.xlsx`.
|
||||
|
||||
If Numbers is installed on the device, it will display the contents of the new file.
|
||||
|
||||
|
||||
[^1]: See ["File Picker"](https://quasar.dev/vue-components/file-picker) in the Quasar documentation.
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See ["SheetJS Data Model"](/docs/csf/) for more details on workbooks, worksheets, and other concepts.
|
||||
|
||||
@ -49,54 +49,42 @@ The [CapacitorJS demo](/docs/demos/mobile/capacitor) covers CapacitorJS apps.
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
**Real Devices**
|
||||
| OS | Type | Device | Date |
|
||||
|:-----------|:-----|:--------------------|:-----------|
|
||||
| Android 34 | Sim | Pixel 3a | 2023-12-04 |
|
||||
| iOS 17.0.1 | Sim | iPhone SE (3rd gen) | 2023-12-04 |
|
||||
|
||||
| OS | Device | Config | Date |
|
||||
|:-----------|:--------------------|:-------|:-----------|
|
||||
| Android 34 | NVIDIA Shield | B | 2025-03-30 |
|
||||
| iOS 15.6 | iPhone 13 Pro Max | B | 2025-03-30 |
|
||||
`ionic info` showed:
|
||||
|
||||
**Simulators**
|
||||
- Ionic: `@ionic/angular 7.5.7`, `@ionic/angular-toolkit 9.0.0`
|
||||
- Cordova: `cordova-lib@12.0.1`, `android 12.0.1, ios 7.0.1`
|
||||
|
||||
| OS | Device | Config | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:-------|:-------------|:-----------|
|
||||
| Android 34 | Pixel 3a | B | `darwin-arm` | 2025-03-30 |
|
||||
| iOS 18.2 | iPhone SE (3rd gen) | B | `darwin-arm` | 2025-03-30 |
|
||||
| Android 36 | Pixel 9 Pro XL | A | `win11-x64` | 2025-04-17 |
|
||||
|
||||
<details>
|
||||
<summary><b>Configurations</b> (click to show)</summary>
|
||||
|
||||
Configuration A:
|
||||
|
||||
- Ionic: `@ionic/angular 8.5.2`, `@ionic/angular-toolkit 12.1.1`
|
||||
- Cordova: `cordova-lib@12.0.2`, `android 14.0.0`
|
||||
- File Integration: `@awesome-cordova-plugins/file` version `6.16.0`
|
||||
|
||||
Configuration B:
|
||||
|
||||
- Ionic: `@ionic/angular 8.5.2`, `@ionic/angular-toolkit 12.1.1`
|
||||
- Cordova: `cordova-lib@12.0.2`, `android 14.0.0, ios 7.1.1`
|
||||
- File Integration: `@awesome-cordova-plugins/file` version `6.16.0`
|
||||
|
||||
</details>
|
||||
The file integration uses `@awesome-cordova-plugins/file` version `6.4.0`.
|
||||
|
||||
:::
|
||||
|
||||
:::danger Telemetry
|
||||
:::warning Telemetry
|
||||
|
||||
Before starting this demo, manually disable telemetry:
|
||||
Before starting this demo, manually disable telemetry. On Linux and MacOS:
|
||||
|
||||
```bash
|
||||
npx -y @ionic/cli config set -g telemetry false
|
||||
npx -y @capacitor/cli telemetry off
|
||||
rm -rf ~/.ionic/
|
||||
mkdir ~/.ionic
|
||||
cat <<EOF > ~/.ionic/config.json
|
||||
{
|
||||
"version": "6.20.1",
|
||||
"telemetry": false,
|
||||
"npmClient": "npm"
|
||||
}
|
||||
EOF
|
||||
npx @capacitor/cli telemetry off
|
||||
```
|
||||
|
||||
To verify telemetry was disabled:
|
||||
|
||||
```bash
|
||||
npx -y @ionic/cli config get -g telemetry
|
||||
npx -y @capacitor/cli telemetry
|
||||
npx @ionic/cli config get -g telemetry
|
||||
npx @capacitor/cli telemetry
|
||||
```
|
||||
|
||||
:::
|
||||
@ -144,18 +132,7 @@ simplifies iteration over the array of arrays:
|
||||
|
||||
### File Operations
|
||||
|
||||
The `cordova-plugin-file` plugin reads and writes files on devices.
|
||||
|
||||
:::caution pass
|
||||
|
||||
For Android 30+, due to scoped storage rules, the standard file module writes
|
||||
private files that cannot be accessed from the Files app.
|
||||
|
||||
A Storage Access Framework plugin must be used to write external files.
|
||||
|
||||
:::
|
||||
|
||||
`@awesome-cordova-plugins/file` is a wrapper designed for Ionic + Angular apps.
|
||||
`@awesome-cordova-plugins/file` reads and writes files on devices.
|
||||
|
||||
:::info pass
|
||||
|
||||
@ -173,7 +150,7 @@ These objects can be parsed with the SheetJS `read` method[^4]. The SheetJS
|
||||
`sheet_to_json` method[^5] with the option `header: 1` generates an array of
|
||||
arrays which can be assigned to the page state:
|
||||
|
||||
```ts title="Read file from device and update state"
|
||||
```ts
|
||||
/* read a workbook file */
|
||||
const ab: ArrayBuffer = await this.file.readAsArrayBuffer(url, filename);
|
||||
/* parse */
|
||||
@ -194,7 +171,7 @@ worksheet object. The `book_new` and `book_append_sheet` helpers[^7] generate a
|
||||
workbook object. The SheetJS `write` method[^8] with the option `type: "array"`
|
||||
will generate an `ArrayBuffer`, from which a `Blob` can be created:
|
||||
|
||||
```ts title="Export state data to XLSX workbook and write to device"
|
||||
```ts
|
||||
/* generate worksheet */
|
||||
const ws: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet(this.data);
|
||||
|
||||
@ -216,7 +193,7 @@ this.file.writeFile(url, filename, blob, {replace: true});
|
||||
|
||||
The app in this demo will display data in a table.
|
||||
|
||||
On load, a [test file](https://docs.sheetjs.com/pres.numbers) will be processed.
|
||||
On load, a [test file](https://sheetjs.com/pres.numbers) will be processed.
|
||||
|
||||
When a document is selected with the file picker, it will be processed and the
|
||||
table will refresh to show the contents.
|
||||
@ -230,38 +207,16 @@ known location. After writing, an alert will display the location of the file.
|
||||
|
||||
### Platform Setup
|
||||
|
||||
0) Disable telemetry:
|
||||
|
||||
```bash
|
||||
npx -y @ionic/cli config set -g telemetry false
|
||||
npx -y @capacitor/cli telemetry off
|
||||
```
|
||||
0) Disable telemetry as noted in the warning.
|
||||
|
||||
1) Follow the official instructions for iOS and Android development[^9].
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
Ionic requires Java 17.
|
||||
|
||||
</details>
|
||||
|
||||
2) Install required global dependencies:
|
||||
|
||||
```bash
|
||||
npm i -g cordova cordova-res @angular/cli native-run @ionic/cli
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In some systems, the command must be run as the root user:
|
||||
|
||||
```bash
|
||||
sudo npm i -g cordova cordova-res @angular/cli native-run @ionic/cli
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Base Project
|
||||
|
||||
3) Create a new project:
|
||||
@ -272,15 +227,15 @@ ionic start SheetJSIonic blank --type angular --cordova --quiet --no-git --no-li
|
||||
|
||||
When asked to select `NgModules` or `Standalone Components`, select `NgModules`
|
||||
|
||||
If a prompt asks to confirm Cordova use, enter <kbd>Y</kbd> to continue.
|
||||
If a prompt asks to confirm Cordova use, enter `Yes` to continue.
|
||||
|
||||
If a prompt asks to create an Ionic account, enter <kbd>N</kbd> to opt out.
|
||||
If a prompt asks about creating an Ionic account, enter `N` to opt out.
|
||||
|
||||
:::caution pass
|
||||
|
||||
Due to conflicts in the dependency tree, the command failed in some test runs.
|
||||
Due to conflicts in the dependency tree, the command failed in the last test.
|
||||
|
||||
If the package installation fails, forcefully install all modules:
|
||||
The fix is to force install all modules:
|
||||
|
||||
```bash
|
||||
cd SheetJSIonic
|
||||
@ -296,9 +251,28 @@ cd ..
|
||||
```bash
|
||||
cd SheetJSIonic
|
||||
ionic cordova plugin add cordova-plugin-file
|
||||
ionic cordova platform add ios --confirm
|
||||
ionic cordova platform add android --confirm
|
||||
npm i --save @awesome-cordova-plugins/core @awesome-cordova-plugins/file @ionic/cordova-builders
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
If `cordova-plugin-file` is added before the platforms, installation may fail:
|
||||
|
||||
```
|
||||
CordovaError: Could not load API for ios project
|
||||
```
|
||||
|
||||
This can be resolved by removing and reinstalling the `ios` platform:
|
||||
|
||||
```bash
|
||||
ionic cordova platform rm ios
|
||||
ionic cordova platform add ios --confirm
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
If the `npm i` step fails due to `rxjs` resolution, add the highlighted lines
|
||||
@ -326,9 +300,9 @@ After adding the lines, the `npm i` command will succeed.
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
6) Add the `@awesome-cordova-plugins/file` plugin to `src/app/app.module.ts`:
|
||||
6) Add `@awesome-cordova-plugins/file` to the module. Differences highlighted below:
|
||||
|
||||
```ts title="src/app/app.module.ts (add highlighted lines)"
|
||||
```ts title="src/app/app.module.ts"
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
@ -352,167 +326,9 @@ export class AppModule {}
|
||||
curl -o src/app/home/home.page.ts -L https://docs.sheetjs.com/ionic/home.page.ts
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'L'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -o src/app/home/home.page.ts -L https://docs.sheetjs.com/ionic/home.page.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Android
|
||||
|
||||
8) Add the Android platform to the project:
|
||||
|
||||
```bash
|
||||
ionic cordova platform add android --confirm
|
||||
npm i --save cordova-android
|
||||
```
|
||||
|
||||
9) Enable file reading and writing in the Android app.
|
||||
|
||||
Edit `platforms/android/app/src/main/AndroidManifest.xml` and add the following
|
||||
two lines before the `application` tag:
|
||||
|
||||
```xml title="platforms/android/app/src/main/AndroidManifest.xml (add to file)"
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
```
|
||||
|
||||
In the `application` tag, add the attribute `android:requestLegacyExternalStorage="true"`.
|
||||
|
||||
10) Build the app and start the emulator:
|
||||
|
||||
```bash
|
||||
ionic cordova emulate android
|
||||
```
|
||||
|
||||
If prompted to share pseudonymous usage data with Google, type <kbd>N</kbd> and
|
||||
press <kbd>Enter</kbd> to opt out.
|
||||
|
||||
If prompted to share anonymous usage data with Cordova, type <kbd>N</kbd>.
|
||||
|
||||
When the app is loaded, a list of Presidents should be displayed. This list is
|
||||
dynamically generated by fetching and parsing a test file.
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs, `cordova build android --emulator` step failed with error:
|
||||
|
||||
```
|
||||
Could not find or parse valid build output file
|
||||
```
|
||||
|
||||
This was resolved by forcefully installing `cordova-android`:
|
||||
|
||||
```bash
|
||||
npm i --save cordova-android
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs, Ionic could not find the emulator:
|
||||
|
||||
```
|
||||
ERR_NO_TARGET: No target devices/emulators available.
|
||||
```
|
||||
|
||||
The target emulator can be found by running
|
||||
|
||||
```bash
|
||||
avdmanager list avd
|
||||
```
|
||||
|
||||
In a test run, the output showed a Pixel 3a with the following details:
|
||||
|
||||
```text
|
||||
// highlight-next-line
|
||||
Name: Pixel_3a_API_34
|
||||
Device: pixel_3a (Google)
|
||||
Path: /Users/SheetJS/.android/avd/Pixel_3a_API_34.avd
|
||||
```
|
||||
|
||||
The Ionic command accepts a `--target` flag. Pass the emulator name:
|
||||
|
||||
```bash
|
||||
ionic cordova emulate android --target=Pixel_3a_API_34
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some tests, the build failed with a Gradle error:
|
||||
|
||||
```
|
||||
Could not find an installed version of Gradle either in Android Studio,
|
||||
or on your system to install the gradle wrapper. Please include gradle
|
||||
in your path or install Android Studio
|
||||
```
|
||||
|
||||
On macOS, this issue was resolved by installing Gradle with Homebrew manager:
|
||||
|
||||
```bash
|
||||
brew install gradle
|
||||
```
|
||||
|
||||
In Windows, Gradle must be installed manually[^11]
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
|
||||
When the demo was last tested on Android, reading files worked as expected.
|
||||
However, the generated files were not externally visible from the Files app.
|
||||
|
||||
**This is a known bug with Android SDK 33 and the underlying file plugins!**
|
||||
|
||||
:::
|
||||
|
||||
### iOS
|
||||
|
||||
:::danger pass
|
||||
|
||||
**iOS testing can only be performed on Apple hardware running macOS!**
|
||||
|
||||
Xcode and iOS simulators are not available on Windows or Linux.
|
||||
|
||||
:::
|
||||
|
||||
11) Add the iOS platform to the project:
|
||||
|
||||
```bash
|
||||
ionic cordova platform add ios --confirm
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
If `cordova-plugin-file` is added before the platforms, installation may fail:
|
||||
|
||||
```
|
||||
CordovaError: Could not load API for ios project
|
||||
```
|
||||
|
||||
This can be resolved by removing and reinstalling the `ios` platform:
|
||||
|
||||
```bash
|
||||
ionic cordova platform rm ios
|
||||
ionic cordova platform add ios --confirm
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
12) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
8) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
Add the following lines to `platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist`:
|
||||
|
||||
```xml title="platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist (add to file)"
|
||||
@ -529,7 +345,7 @@ Add the following lines to `platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist`:
|
||||
|
||||
(The root element of the document is `plist` and it contains one `dict` child)
|
||||
|
||||
13) Build the app and start the simulator
|
||||
9) Build the app and start the simulator
|
||||
|
||||
```bash
|
||||
ionic cordova emulate ios
|
||||
@ -598,86 +414,71 @@ ng add @ionic/cordova-builders
|
||||
|
||||
:::
|
||||
|
||||
### iOS Device
|
||||
### Android
|
||||
|
||||
14) Connect an iOS device to the computer and "Trust" the device if prompted.
|
||||
10) Enable file reading and writing in the Android app.
|
||||
|
||||
15) Enable code signing for the project:
|
||||
Edit `platforms/android/app/src/main/AndroidManifest.xml` and add the following
|
||||
two lines before the `application` tag:
|
||||
|
||||
Open the `SheetJSIonic.xcodeproj` project in Xcode:
|
||||
```xml title="platforms/android/app/src/main/AndroidManifest.xml (add to file)"
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
```
|
||||
|
||||
In the `application` tag, add the attribute `android:requestLegacyExternalStorage="true"`.
|
||||
|
||||
11) Build the app and start the emulator
|
||||
|
||||
```bash
|
||||
open platforms/ios/SheetJSIonic.xcodeproj
|
||||
ionic cordova emulate android
|
||||
```
|
||||
|
||||
Select the "SheetJSIonic" project in the "Project navigator" side panel.
|
||||
When the app is loaded, a list of Presidents should be displayed. This list is
|
||||
dynamically generated by fetching and parsing a test file.
|
||||
|
||||
In the main panel, select "Signing & Capabilities".
|
||||
:::caution pass
|
||||
|
||||
In the "Team" dropdown, select a certificate.
|
||||
In some test runs, `cordova build android --emulator` step failed with error:
|
||||
|
||||
In the "Bundle Identifier" text box, enter `com.sheetjs.SheetJSIonic`
|
||||
```
|
||||
Could not find or parse valid build output file
|
||||
```
|
||||
|
||||
16) Launch the app on the device:
|
||||
This was resolved by forcefully installing `cordova-android`:
|
||||
|
||||
```bash
|
||||
ionic cordova run ios --device --verbose
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
In the most recent test, the `native-run ios` command failed with
|
||||
|
||||
```
|
||||
[native-run] ERR_UNKNOWN: Path 'platforms/ios/build/device/SheetJSIonic.ipa' not found
|
||||
```
|
||||
|
||||
Inspecting `platforms/ios/build/`, the actual folder name was:
|
||||
|
||||
```bash
|
||||
% ls platforms/ios/build
|
||||
#highlight-next-line
|
||||
Debug-iphoneos
|
||||
```
|
||||
|
||||
To force `native-run` to use the device, the name must be found by inspecting
|
||||
the output of `native-run ios --list`:
|
||||
|
||||
```bash
|
||||
% native-run ios --list
|
||||
|
||||
Connected Devices:
|
||||
|
||||
Name API Target ID
|
||||
---------------------------------------------
|
||||
SheetJS iOS 15.6 12345678-90ABCDEF12345678
|
||||
|
||||
```
|
||||
|
||||
`native-run` accepts a `--device` flag. Pass the device name:
|
||||
|
||||
|
||||
```bash
|
||||
native-run ios --app platforms/ios/build/Debug-iphoneos/SheetJSIonic.ipa --device SheetJS
|
||||
npm i --save cordova-android
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
17) Test the app.
|
||||
:::caution pass
|
||||
|
||||
The app will fetch a file and display the contents in a table.
|
||||
In some tests, the build failed with a Gradle error:
|
||||
|
||||
Tap "Export Data" to create a file. To find the file, switch to the "Files" app
|
||||
and browse "On My iPhone" > "SheetJSIonic". There should be a new spreadsheet
|
||||
named "SheetJSIonic".
|
||||
```
|
||||
Could not find an installed version of Gradle either in Android Studio,
|
||||
or on your system to install the gradle wrapper. Please include gradle
|
||||
in your path or install Android Studio
|
||||
```
|
||||
|
||||
Switch to the "Numbers" app and open that file. Tap "EDIT" to make changes.
|
||||
Change cell A7 to "SheetJS Dev" and cell B7 to 47. Tap "Done" and close the app.
|
||||
On macOS, this issue was resolved by installing gradle with Homebrew manager:
|
||||
|
||||
Switch back to "SheetJSIonic" and tap "Import Data". Tap "Choose Files" in the
|
||||
popup. Tap "Browse" in the bottom of the sheet. Navigate to "On My iPhone" >
|
||||
"SheetJSIonic" and tap the new "SheetJSIonic" spreadsheet. The screen will show
|
||||
the file with the new line.
|
||||
```bash
|
||||
brew install gradle
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::warning pass
|
||||
|
||||
When the demo was last tested on Android, reading files worked as expected.
|
||||
However, the generated files were not externally visible from the Files app.
|
||||
|
||||
**This is a known bug with Android SDK 33 and the underlying file plugins!**
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays)
|
||||
[^2]: See [`ion-grid`](https://ionicframework.com/docs/api/grid) in the Ionic documentation.
|
||||
@ -687,6 +488,4 @@ the file with the new line.
|
||||
[^6]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
|
||||
[^7]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
|
||||
[^8]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
[^9]: See ["Developing for iOS"](https://ionic-docs-o31kiyk8l-ionic1.vercel.app/docs/v6/developing/ios) and ["Developing for Android"](https://ionic-docs-o31kiyk8l-ionic1.vercel.app/docs/v6/developing/android). The Ionic team removed these pages from the official docs site and recommend the `vercel.app` docs site.
|
||||
[^10]: See the [JDK Archive](https://jdk.java.net/archive/) for Java 17 JDK download links.
|
||||
[^11]: See ["Installing manually"](https://gradle.org/install/#manually) in the Gradle documentation.
|
||||
[^9]: See ["Developing for iOS"](https://ionicframework.com/docs/v6/developing/ios) and ["Developing for Android"](https://ionicframework.com/docs/v6/developing/android) in the v6 Ionic framework documentation.
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Storing Sheets with CapacitorJS
|
||||
sidebar_label: CapacitorJS
|
||||
title: CapacitorJS
|
||||
pagination_prev: demos/static/index
|
||||
pagination_next: demos/desktop/index
|
||||
sidebar_position: 5
|
||||
@ -11,17 +10,10 @@ sidebar_custom_props:
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[CapacitorJS](https://capacitorjs.com/) is a mobile app runtime for building iOS
|
||||
and Android apps.
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from any component or script in the app.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses CapacitorJS and SheetJS to process data and export spreadsheets.
|
||||
We'll explore how to load SheetJS in an CapacitorJS app and use APIs and plugins
|
||||
to extract data from, and write data to, spreadsheet files on the device.
|
||||
|
||||
The ["Demo"](#demo) creates an app that looks like the screenshots below:
|
||||
The "Complete Example" creates an app that looks like the screenshots below:
|
||||
|
||||
<table><thead><tr>
|
||||
<th><a href="#demo">iOS</a></th>
|
||||
@ -40,62 +32,40 @@ The ["Demo"](#demo) creates an app that looks like the screenshots below:
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
**Real Devices**
|
||||
|
||||
| OS | Device | CapacitorJS + FS | Date |
|
||||
|:-----------|:--------------------|:------------------|:-----------|
|
||||
| Android 30 | NVIDIA Shield | `6.2.0` / `6.0.3` | 2025-01-19 |
|
||||
| iOS 15.1 | iPad Pro | `6.2.0` / `6.0.3` | 2025-01-19 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | CapacitorJS + FS | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:------------------|:-------------|:-----------|
|
||||
| Android 34 | Pixel 3a | `7.1.0` / `7.0.0` | `darwin-arm` | 2025-03-30 |
|
||||
| iOS 18.2 | iPhone 16 Pro Max | `7.1.0` / `7.0.0` | `darwin-arm` | 2025-03-30 |
|
||||
| Android 35 | Pixel 9 Pro XL | `7.3.0` / `7.1.1` | `win11-x64` | 2025-06-08 |
|
||||
| Android 36 | Pixel 9 | `7.4.1` / `7.1.2` | `linux-x64` | 2025-07-06 |
|
||||
| OS | Type | Device | CapacitorJS + FS | Date |
|
||||
|:-----------|:-----|:--------------------|:------------------|:-----------|
|
||||
| Android 34 | Sim | Pixel 3a | `5.5.1` / `5.1.4` | 2023-12-04 |
|
||||
| iOS 17.0.1 | Sim | iPhone 15 Pro Max | `5.5.1` / `5.1.4` | 2023-12-04 |
|
||||
| Android 29 | Real | NVIDIA Shield | `5.5.1` / `5.1.4` | 2023-12-04 |
|
||||
| iOS 15.1 | Real | iPad Pro | `5.5.1` / `5.1.4` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
:::danger Telemetry
|
||||
:::warning Telemetry
|
||||
|
||||
Before starting this demo, manually disable telemetry. On Linux and MacOS:
|
||||
|
||||
```bash
|
||||
npx -y @capacitor/cli telemetry off
|
||||
npx @capacitor/cli telemetry off
|
||||
```
|
||||
|
||||
To verify telemetry was disabled:
|
||||
|
||||
```bash
|
||||
npx -y @capacitor/cli telemetry
|
||||
npx @capacitor/cli telemetry
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from any component or script in the app.
|
||||
|
||||
This demo uses [SvelteJS](/docs/demos/frontend/svelte), but the same principles
|
||||
apply to other frameworks.
|
||||
This example uses Svelte, but the same principles apply to other frameworks.
|
||||
|
||||
#### Reading data
|
||||
|
||||
The standard [HTML5 File Input](/docs/demos/local/file#file-api) API works as
|
||||
expected in CapacitorJS.
|
||||
The standard HTML5 File Input element logic works in CapacitorJS:
|
||||
|
||||
Apps will typically include an `input type="file"` element. When the element is
|
||||
activated, CapacitorJS will show a file picker. After the user selects a file,
|
||||
the element will receive a `change` event.
|
||||
|
||||
The following example parses the selected file using the SheetJS `read`[^1]
|
||||
method, generates an HTML table from the first sheet using `sheet_to_html`[^2],
|
||||
and displays the table by setting the `innerHTML` attribute of a `div` element:
|
||||
|
||||
```html title="Sample component for data import"
|
||||
```html
|
||||
<script>
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
@ -121,39 +91,28 @@ async function importFile(evt) {
|
||||
|
||||
#### Writing data
|
||||
|
||||
Starting from a SheetJS workbook object[^3], the `write` method with the option
|
||||
`type: "base64"`[^4] will generate Base64-encoded files.
|
||||
`@capacitor/filesystem` can write Base64 strings:
|
||||
|
||||
The `@capacitor/filesystem` plugin can write Base64 strings to the device.
|
||||
|
||||
The following example uses the SheetJS `table_to_book` method[^5] to create a
|
||||
workbook object from an HTML table. The workbook object is exported to the XLSX
|
||||
format and written to the device.
|
||||
|
||||
```html title="Sample component for data export"
|
||||
```html
|
||||
<script>
|
||||
import { Filesystem, Directory } from '@capacitor/filesystem';
|
||||
import { utils, write } from 'xlsx';
|
||||
import { utils, writeXLSX } from 'xlsx';
|
||||
|
||||
let html = "";
|
||||
let tbl;
|
||||
|
||||
/* get state data and export to XLSX */
|
||||
async function exportFile() {
|
||||
/* generate workbook object from HTML table */
|
||||
const elt = tbl.getElementsByTagName("TABLE")[0];
|
||||
const wb = utils.table_to_book(elt);
|
||||
|
||||
/* generate Base64 string for Capacitor */
|
||||
// highlight-start
|
||||
/* export to XLSX encoded in a Base64 string */
|
||||
const data = write(wb, { bookType: "xlsx", type: "base64" });
|
||||
|
||||
/* attempt to write to the device */
|
||||
const data = writeXLSX(wb, { type: "base64" });
|
||||
await Filesystem.writeFile({
|
||||
data,
|
||||
path: "SheetJSCap.xlsx",
|
||||
directory: Directory.Documents
|
||||
});
|
||||
}); // write file
|
||||
// highlight-end
|
||||
}
|
||||
|
||||
@ -165,45 +124,11 @@ async function exportFile() {
|
||||
</main>
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
`Filesystem.writeFile` cannot overwrite existing files. Production apps should
|
||||
attempt to delete the file before writing:
|
||||
|
||||
```js
|
||||
/* attempt to delete file first */
|
||||
try {
|
||||
await Filesystem.deleteFile({
|
||||
path: "SheetJSCap.xlsx",
|
||||
directory: Directory.Documents
|
||||
});
|
||||
} catch(e) {}
|
||||
/* attempt to write to the device */
|
||||
await Filesystem.writeFile({
|
||||
data,
|
||||
path: "SheetJSCap.xlsx",
|
||||
directory: Directory.Documents
|
||||
});
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Demo
|
||||
|
||||
The app in this demo will display data in a table.
|
||||
|
||||
When the app is launched, a [test file](https://docs.sheetjs.com/pres.numbers)
|
||||
will be fetched and processed.
|
||||
|
||||
When a document is selected with the file picker, it will be processed and the
|
||||
table will refresh to show the contents.
|
||||
|
||||
"Export XLSX" will attempt to export the table data to `SheetJSCap.xlsx` in the
|
||||
app Documents folder. An alert will display the location of the file.
|
||||
|
||||
### Base Project
|
||||
|
||||
0) Follow the official "Environment Setup"[^6] instructions to set up Android
|
||||
0) Follow the official "Environment Setup"[^1] instructions to set up Android
|
||||
and iOS targets
|
||||
|
||||
:::caution pass
|
||||
@ -212,29 +137,22 @@ iOS development is only supported on macOS.
|
||||
|
||||
:::
|
||||
|
||||
<details open>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
<details><summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
For Android development, CapacitorJS requires a Java version compatible with the
|
||||
expected Gradle version:
|
||||
|
||||
| CapacitorJS | Gradle | Java |
|
||||
|:------------|:---------|:-----|
|
||||
| `6.2.0` | `8.2.1` | 20 |
|
||||
| `7.4.1` | `8.11.1` | 21 |
|
||||
CapacitorJS requires Java 17.
|
||||
|
||||
</details>
|
||||
|
||||
1) Disable telemetry.
|
||||
|
||||
```bash
|
||||
npx -y @capacitor/cli telemetry off
|
||||
npx @capacitor/cli telemetry off
|
||||
```
|
||||
|
||||
Verify that telemetry is disabled by running
|
||||
|
||||
```bash
|
||||
npx -y @capacitor/cli telemetry
|
||||
npx @capacitor/cli telemetry
|
||||
```
|
||||
|
||||
(it should print `Telemetry is off`)
|
||||
@ -253,22 +171,6 @@ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
|
||||
npm i --save @capacitor/core @capacitor/cli @capacitor/filesystem`}
|
||||
</CodeBlock>
|
||||
|
||||
:::warning pass
|
||||
|
||||
When this demo was last tested, parts of the official project required ViteJS 6
|
||||
and other parts required ViteJS 7. The install failed.
|
||||
|
||||
**This is a bug in the Svelte integration!**
|
||||
|
||||
Until the bug is fixed, the `--force` option should be used:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz --force
|
||||
npm i --save @capacitor/core @capacitor/cli @capacitor/filesystem --force`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
4) Create CapacitorJS structure:
|
||||
|
||||
```bash
|
||||
@ -278,14 +180,14 @@ npm run build
|
||||
|
||||
:::note pass
|
||||
|
||||
If prompted to create an Ionic account, Press <kbd>N</kbd> to opt out.
|
||||
If prompted to create an Ionic account, type `N` and press Enter.
|
||||
|
||||
:::
|
||||
|
||||
5) Download [`src/App.svelte`](pathname:///cap/App.svelte) and replace:
|
||||
|
||||
```bash
|
||||
curl -o src/App.svelte https://docs.sheetjs.com/cap/App.svelte
|
||||
curl -o src/App.svelte -L https://docs.sheetjs.com/cap/App.svelte
|
||||
```
|
||||
|
||||
### Android
|
||||
@ -297,36 +199,6 @@ npm i --save @capacitor/android
|
||||
npx cap add android
|
||||
```
|
||||
|
||||
:::warning pass
|
||||
|
||||
When this demo was last tested, parts of the official project required ViteJS 6
|
||||
and other parts required ViteJS 7. The install failed.
|
||||
|
||||
**This is a bug in the Svelte integration!**
|
||||
|
||||
Until the bug is fixed, the `--force` option should be used:
|
||||
|
||||
```bash
|
||||
npm i --save @capacitor/android --force
|
||||
npx cap add android
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
If the wrong Java version is installed, the last command will fail with a
|
||||
message that references a "class file major version"
|
||||
|
||||
```
|
||||
> BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 67
|
||||
```
|
||||
|
||||
The correct Java version must be installed. When this demo was last tested, Java
|
||||
20 was compatible with CapacitorJS Android projects.
|
||||
|
||||
:::
|
||||
|
||||
7) Enable file reading and writing in the Android app.
|
||||
|
||||
Add the highlighted lines to `android/app/src/main/AndroidManifest.xml` after
|
||||
@ -343,9 +215,7 @@ the `Permissions` comment:
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
```
|
||||
|
||||
8) Start the Android simulator through Android Studio.
|
||||
|
||||
9) Run the app in the simulator:
|
||||
8) Run the app in the simulator
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -353,66 +223,27 @@ npx cap sync
|
||||
npx cap run android
|
||||
```
|
||||
|
||||
The app should look like the screenshot at the top of the page.
|
||||
9) Test the app
|
||||
|
||||
10) Test the export functionality.
|
||||
Open the app and observe that presidents are listed in the table.
|
||||
|
||||
Touch "Export XLSX". If the emulator asks for permission, tap "Allow". A popup
|
||||
will show the exported path.
|
||||
Touch "Export XLSX" and the emulator will ask for permission:
|
||||
|
||||

|
||||
Tap "Allow" and a popup will be displayed with a path.
|
||||
|
||||
Open the "Files" app in the simulator, tap the `≡` icon and tap "Documents". Tap
|
||||
the "Documents" folder to find `SheetJSCap.xlsx`.
|
||||
|
||||
<details open>
|
||||
<summary><b>Downloading the generated file</b> (click to hide)</summary>
|
||||
|
||||
The file location can be found by searching for `SheetJSCap.xlsx`:
|
||||
|
||||
```bash
|
||||
adb exec-out find / -name SheetJSCap.xlsx
|
||||
```
|
||||
|
||||
There may be a number of error messages that start with `find:`. There will be
|
||||
at least one line starting with `/`:
|
||||
|
||||
```text
|
||||
/data/media/0/Documents/SheetJSCap.xlsx
|
||||
/storage/emulated/0/Documents/SheetJSCap.xlsx
|
||||
```
|
||||
|
||||
The `/storage` path can be pulled using `adb pull`:
|
||||
|
||||
```bash
|
||||
adb pull "/storage/emulated/0/Documents/SheetJSCap.xlsx" SheetJSCap.xlsx
|
||||
```
|
||||
|
||||
`SheetJSCap.xlsx` can be opened with a spreadsheet editor such as Excel.
|
||||
|
||||
</details>
|
||||
|
||||
11) Test the import functionality.
|
||||
|
||||
Edit `SheetJSCap.xlsx`, setting cell `A7` to `SheetJS Dev` and setting cell `B7`
|
||||
to `47`. Save the file.
|
||||
|
||||
Click and drag the file into the Android emulator window. The file will be
|
||||
uploaded to a Downloads folder in the emulator.
|
||||
|
||||
Switch back to the app and tap "Choose File". In the selector, tap `≡`, select
|
||||
"Downloads" and tap `SheetJSCap.xlsx`. The table will refresh with the new row.
|
||||
To see the generated file, switch to the "Files" app in the simulator, tap the
|
||||
`≡` icon and tap "Documents". Tap "Documents" folder to find `SheetJSCap.xlsx`.
|
||||
|
||||
### iOS
|
||||
|
||||
12) Create iOS app.
|
||||
10) Create iOS app
|
||||
|
||||
```bash
|
||||
npm i --save @capacitor/ios
|
||||
npx cap add ios
|
||||
```
|
||||
|
||||
13) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
11) Enable file sharing and make the documents folder visible in the iOS app.
|
||||
The following lines must be added to `ios/App/App/Info.plist`:
|
||||
|
||||
```xml title="ios/App/App/Info.plist (add to file)"
|
||||
@ -429,7 +260,7 @@ The following lines must be added to `ios/App/App/Info.plist`:
|
||||
|
||||
(The root element of the document is `plist` and it contains one `dict` child)
|
||||
|
||||
14) Run the app in the simulator.
|
||||
12) Run the app in the simulator
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -437,66 +268,26 @@ npx cap sync
|
||||
npx cap run ios
|
||||
```
|
||||
|
||||
If prompted to select a target device, select "iPhone 16 Pro Max (simulator)".
|
||||
If prompted to select a target device, select "iPhone 15 Pro Max (simulator)".
|
||||
|
||||
The app should look like the screenshot at the top of the page.
|
||||
13) Test the app
|
||||
|
||||
15) Test the export functionality.
|
||||
Open the app and observe that presidents are listed in the table.
|
||||
|
||||
Touch "Export XLSX" and a popup will be displayed.
|
||||
|
||||

|
||||
|
||||
To see the generated file, switch to the "Files" app in the simulator and look
|
||||
for `SheetJSCap.xlsx` in "On My iPhone" > "`sheetjs-cap`"
|
||||
|
||||
<details open>
|
||||
<summary><b>Downloading the generated file</b> (click to hide)</summary>
|
||||
|
||||
The app files are available in the filesystem in `~/Library/Developer`. Open a
|
||||
terminal and run the following command to find the file:
|
||||
|
||||
```bash
|
||||
find ~/Library/Developer -name SheetJSCap.xlsx
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
16) Test the import functionality.
|
||||
|
||||
Edit `SheetJSCap.xlsx`, setting cell `A7` to `SheetJS Dev` and setting cell `B7`
|
||||
to `47`. Save the file.
|
||||
|
||||
Click and drag the file into the iOS simulator window. The simulator will show a
|
||||
picker for saving the file. Select the `sheetjs-cap` folder and tap "Save". If
|
||||
prompted to "Replace Existing Items?", tap "Replace".
|
||||
|
||||
Switch back to the app and tap "Choose File". Tap "Choose File" in the popup.
|
||||
In the picker, tap "Recents" and select the newest `SheetJSCap` file. The table
|
||||
will refresh with the new data.
|
||||
|
||||
### Android Device
|
||||
|
||||
17) Connect an Android device using a USB cable.
|
||||
14) Connect an Android device using a USB cable.
|
||||
|
||||
If the device asks to allow USB debugging, tap "Allow".
|
||||
|
||||
18) Confirm the device is detected by `adb`.
|
||||
15) Close any Android / iOS emulators.
|
||||
|
||||
```bash
|
||||
adb devices
|
||||
```
|
||||
|
||||
If the device is detected, the command will list the device:
|
||||
|
||||
```text title="Expected output"
|
||||
List of devices attached
|
||||
1234567890 device
|
||||
```
|
||||
|
||||
19) Close any Android / iOS emulators.
|
||||
|
||||
20) Build APK and run on device:
|
||||
16) Build APK and run on device:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -507,13 +298,6 @@ npx cap run android
|
||||
If the Android emulators are closed and an Android device is connected, the last
|
||||
command will build an APK and install on the device.
|
||||
|
||||
:::note pass
|
||||
|
||||
In some tests, the last command asked for a target device. Select the Android
|
||||
device in the list and press <kbd>Enter</kbd>
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
For real devices running API level 29 or below, the following line must be added
|
||||
@ -535,19 +319,13 @@ to the `application` open tag in `android/app/src/main/AndroidManifest.xml`:
|
||||
|
||||
### iOS Device
|
||||
|
||||
21) Connect an iOS device using a USB cable
|
||||
17) Connect an iOS device using a USB cable
|
||||
|
||||
If prompted to "Trust This Computer", tap "Trust" and enter the device passcode.
|
||||
18) Close any Android / iOS emulators.
|
||||
|
||||
22) Close any Android / iOS emulators.
|
||||
19) Enable developer code signing certificates[^2]
|
||||
|
||||
23) Enable developer code signing certificates.
|
||||
|
||||
Open `ios/App/App.xcworkspace` in Xcode. Select the "Project Navigator" and
|
||||
select the "App" project. In the main view, select "Signing & Capabilities".
|
||||
Under "Signing", select a team in the dropdown menu.
|
||||
|
||||
24) Run on device:
|
||||
19) Run on device:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@ -557,38 +335,5 @@ npx cap run ios
|
||||
|
||||
When prompted to select a target device, select the real device in the list.
|
||||
|
||||
:::info pass
|
||||
|
||||
In some test runs, the build failed with a provisioning error:
|
||||
|
||||
```
|
||||
error: Provisioning profile "iOS Team Provisioning Profile: com.sheetjs.cap" doesn't include the currently selected device "SheetJS Test Device" (identifier 12345678-9ABCDEF012345678). (in target 'App' from project 'App')
|
||||
```
|
||||
|
||||
This error was resolved by manually selecting the device as the primary target
|
||||
in the Xcode workspace.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some tests, the app failed to launch with a "Untrusted Developer" error.
|
||||
|
||||
Switch to the Settings app and select General > VPN & Device Management. There
|
||||
will be a new item in the "DEVELOPER APP" section. Tap the line and verify that
|
||||
`sheetjs-cap` is listed in the screen. Tap "Trust" and tap "Trust" in the popup.
|
||||
|
||||
After trusting the certificate, re-run the app:
|
||||
|
||||
```bash
|
||||
npx cap run ios
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^2]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
|
||||
[^3]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^4]: See [the "base64" type in "Writing Files"](/docs/api/write-options#output-type)
|
||||
[^5]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
|
||||
[^6]: See ["Environment Setup"](https://capacitorjs.com/docs/getting-started/environment-setup) in the CapacitorJS documentation.
|
||||
[^1]: See ["Environment Setup"](https://capacitorjs.com/docs/getting-started/environment-setup) in the CapacitorJS documentation.
|
||||
[^2]: The [Flutter documentation](https://docs.flutter.dev/get-started/install/macos?tab=ios15#enable-developer-code-signing-certificates) covers the instructions in more detail. The correct workspace is `ios/App/App.xcworkspace`
|
||||
@ -10,13 +10,8 @@ sidebar_custom_props:
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
export const r = {style: {color:"red"}};
|
||||
export const g = {style: {color:"green"}};
|
||||
|
||||
Dart[^1] + Flutter[^2] is a popular cross-platform app framework. JavaScript
|
||||
code can be run through [embedded engines](/docs/demos/engines).
|
||||
|
||||
@ -46,24 +41,16 @@ The "Demo" creates an app that looks like the screenshots below:
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
**Real Devices**
|
||||
|
||||
| OS | Device | Dart | Flutter | Date |
|
||||
|:-----------|:------------------|:--------|:---------|:-----------|
|
||||
| Android 34 | NVIDIA Shield | `3.7.2` | `3.29.2` | 2025-03-31 |
|
||||
| iOS 15.6 | iPhone 13 Pro Max | `3.7.2` | `3.29.2` | 2025-03-31 |
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | Dart | Flutter | Dev Platform | Date |
|
||||
|:-----------|:------------------|:--------|:---------|:-------------|:-----------|
|
||||
| Android 35 | Pixel 9 Pro XL | `3.7.2` | `3.29.2` | `darwin-x64` | 2025-03-31 |
|
||||
| iOS 18.3 | iPhone 16 Pro Max | `3.7.2` | `3.29.2` | `darwin-x64` | 2025-03-31 |
|
||||
| Android 36 | Pixel 9 Pro XL | `3.7.2` | `3.29.3` | `win11-x64` | 2054-04-28 |
|
||||
| OS | Type | Device | Dart | Flutter | Date |
|
||||
|:-----------|:-----|:------------------|:--------|:---------|:-----------|
|
||||
| Android 34 | Sim | Pixel 3a | `3.2.2` | `3.16.2` | 2023-12-04 |
|
||||
| iOS 17.0.1 | Sim | iPhone 15 Pro Max | `3.2.2` | `3.16.2` | 2023-12-04 |
|
||||
| Android 29 | Real | NVIDIA Shield | `3.2.2` | `3.16.2` | 2023-12-04 |
|
||||
| iOS 15.1 | Real | iPad Pro | `3.2.2` | `3.16.2` | 2023-12-04 |
|
||||
|
||||
:::
|
||||
|
||||
:::danger Telemetry
|
||||
:::warning Telemetry
|
||||
|
||||
Before starting this demo, manually disable telemetry. On MacOS:
|
||||
|
||||
@ -225,42 +212,22 @@ class SheetJSFlutterState extends State<SheetJSFlutter> {
|
||||
|
||||
Run `flutter doctor` and confirm the following items are checked:
|
||||
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="linux" label="Linux">
|
||||
|
||||
<pre>
|
||||
<span {...g}>[✓]</span> Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
||||
</pre>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS" default>
|
||||
|
||||
<pre>
|
||||
<span {...g}>[✓]</span> Android toolchain - develop for Android devices (Android SDK version 36.0.0)
|
||||
<span {...g}>[✓]</span> Xcode - develop for iOS and macOS (Xcode 16.2)
|
||||
</pre>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
<pre>
|
||||
<span {...g}>[✓]</span> Android toolchain - develop for Android devices (Android SDK version 35.0.1)
|
||||
</pre>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
```
|
||||
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
||||
[✓] Xcode - develop for iOS and macOS (Xcode 15.0.1)
|
||||
[✓] Android Studio (version 2022.3)
|
||||
```
|
||||
|
||||
(the actual version numbers may differ)
|
||||
|
||||
<details open>
|
||||
<summary><b>Installation Notes</b> (click to hide)</summary>
|
||||
<details open><summary><b>Installation Notes</b> (click to hide)</summary>
|
||||
|
||||
:::note pass
|
||||
|
||||
On first run, there may be a warning with "Android toolchain":
|
||||
|
||||
```
|
||||
[!] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
|
||||
[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
|
||||
! Some Android licenses not accepted. To resolve this, run: flutter doctor
|
||||
--android-licenses
|
||||
```
|
||||
@ -294,11 +261,6 @@ In local testing, there were issues with the Android toolchain:
|
||||
error: Android sdkmanager not found. Update to the latest Android SDK and ensure that the cmdline-tools are installed to resolve this.
|
||||
```
|
||||
|
||||
Android Studio does not install `Android SDK Command-Line Tools` by default. It
|
||||
must be installed manually.
|
||||
|
||||
Assuming the command-line tools are installed
|
||||
|
||||
This was fixed by switching to Java 20, installing `Android SDK 33`, and rolling
|
||||
back to `Android SDK Command-Line Tools (revision: 10.0)`
|
||||
|
||||
@ -316,90 +278,28 @@ If Google Chrome is not installed, `flutter doctor` will show an issue:
|
||||
|
||||
If Chromium is installed, the environment variable should be manually assigned:
|
||||
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="linux" label="Linux">
|
||||
|
||||
The `CHROME_EXECUTABLE` environment variable should be set to the path to the
|
||||
`chrome` binary. This path differs between distributions and package managers.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS">
|
||||
|
||||
```bash
|
||||
export CHROME_EXECUTABLE=/Applications/Chromium.app/Contents/MacOS/Chromium
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
Type `env` in the search bar and select "Edit the system environment variables".
|
||||
|
||||
In the new window, click the "Environment Variables..." button.
|
||||
|
||||
In the new window, look for the "System variables" section and click "New..."
|
||||
|
||||
Set the "Variable name" to `CHROME_EXECUTABLE` and the value to the path to the
|
||||
program. When this demo was last tested, Chromium was installed for the local
|
||||
user at `C:\Users\USERNAME\AppData\Local\Chromium\Application\chrome.exe` .
|
||||
|
||||
Click "OK" in each window (3 windows) and restart your computer.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
|
||||
List all available emulators:
|
||||
|
||||
```bash
|
||||
flutter emulators
|
||||
```
|
||||
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="linux" label="Linux">
|
||||
|
||||
There should be at least one `android` emulator:
|
||||
Run `flutter emulators` and check for both `ios` and `android` emulators:
|
||||
|
||||
```
|
||||
Id • Name • Manufacturer • Platform
|
||||
|
||||
Pixel_3a_API_35 • Pixel 3a API 35 • Google • android
|
||||
apple_ios_simulator • iOS Simulator • Apple • ios
|
||||
Pixel_3a_API_34 • Pixel 3a API 34 • Google • android
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="macos" label="macOS">
|
||||
|
||||
There should be at least one `android` emulator and one `ios` simulator:
|
||||
|
||||
```
|
||||
Id • Name • Manufacturer • Platform
|
||||
|
||||
apple_ios_simulator • iOS Simulator • Apple • ios
|
||||
Pixel_9_Pro_XL_API_35 • Pixel 9 Pro XL API 35 • Google • android
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
There should be at least one `android` emulator:
|
||||
|
||||
```
|
||||
Id • Name • Manufacturer • Platform
|
||||
|
||||
Pixel_9_Pro_XL • Pixel 9 Pro XL • Google • android
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
1) Disable telemetry. The following commands were confirmed to work:
|
||||
1) Disable telemetry.
|
||||
|
||||
```bash
|
||||
dart --disable-telemetry
|
||||
dart --disable-analytics
|
||||
flutter config --no-analytics
|
||||
flutter config --disable-telemetry
|
||||
```
|
||||
|
||||
### Base Project
|
||||
@ -413,8 +313,7 @@ cd sheetjs_flutter
|
||||
|
||||
3) Start the Android emulator.
|
||||
|
||||
<details open>
|
||||
<summary><b>Details</b> (click to hide)</summary>
|
||||
<details open><summary><b>Details</b> (click to hide)</summary>
|
||||
|
||||
**Android Studio**
|
||||
|
||||
@ -429,59 +328,28 @@ List the available emulators with `flutter emulators`:
|
||||
% flutter emulators
|
||||
2 available emulators:
|
||||
|
||||
Id • Name • Manufacturer • Platform
|
||||
|
||||
apple_ios_simulator • iOS Simulator • Apple • ios
|
||||
Pixel_9_Pro_XL_API_35 • Pixel 9 Pro XL API 35 • Google • android
|
||||
^^^^^^^^^^^^^^^^^^^^^--- the first column is the name for `emulator avd`
|
||||
apple_ios_simulator • iOS Simulator • Apple • ios
|
||||
Pixel_3a_API_34 • Pixel 3a API 34 • Google • android
|
||||
^^^^^^^^^^^^^^^--- the first column is the name
|
||||
```
|
||||
|
||||
The first column shows the name that should be passed to `emulator -avd`. In a
|
||||
previous test, the name was `Pixel_9_Pro_XL_API_35` and the launch command was:
|
||||
previous test, the name was `Pixel_3a_API_34` and the launch command was:
|
||||
|
||||
```bash
|
||||
emulator -avd Pixel_9_Pro_XL_API_35
|
||||
emulator -avd Pixel_3a_API_34
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
If `emulator` cannot be found, the folder must be added to the system path.
|
||||
|
||||
<Tabs groupId="os">
|
||||
<TabItem value="macos" label="macOS">
|
||||
|
||||
On macOS, `~/Library/Android/sdk/emulator/` is the typical location for the
|
||||
`emulator` binary:
|
||||
`emulator` binary. If it cannot be found, add the folder to `PATH`:
|
||||
|
||||
```bash
|
||||
export PATH="$PATH":~/Library/Android/sdk/emulator
|
||||
emulator -avd Pixel_9_Pro_XL_API_35
|
||||
emulator -avd Pixel_3a_API_34
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
|
||||
The Android SDK folder can be found in the SDK manager in Android Studio. It is
|
||||
typically `%LOCALAPPDATA%\Android\Sdk`.
|
||||
|
||||
If it is not assigned, create a User environment variable named `ANDROID_HOME`
|
||||
with the value set to the Android SDK folder.
|
||||
|
||||
---
|
||||
|
||||
There are three folders within the Android SDK folder that should be added to
|
||||
the User `PATH` environment variable. Each folder holds a different tool:
|
||||
|
||||
| Folder | Command-line Tool |
|
||||
|:------------------------------------------|:------------------|
|
||||
| `%ANDROID_HOME%\emulator` | `emulator` |
|
||||
| `%ANDROID_HOME%\cmdline-tools\latest\bin` | `avdmanager` |
|
||||
| `%ANDROID_HOME%\platform-tools` | `adb` |
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
:::
|
||||
|
||||
</details>
|
||||
@ -492,8 +360,7 @@ the User `PATH` environment variable. Each folder holds a different tool:
|
||||
flutter run
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>If emulator is not detected</b> (click to show)</summary>
|
||||
<details><summary><b>If emulator is not detected</b> (click to show)</summary>
|
||||
|
||||
In some test runs, `flutter run` did not automatically detect the emulator.
|
||||
|
||||
@ -534,32 +401,6 @@ Once the app loads, stop the terminal process and close the simulator.
|
||||
flutter pub add http csv flutter_js
|
||||
```
|
||||
|
||||
:::info pass
|
||||
|
||||
The command may fail in Windows with the following message:
|
||||
|
||||
<pre {...r}>
|
||||
Building with plugins requires symlink support.
|
||||
|
||||
Please enable Developer Mode in your system settings. Run
|
||||
{` `}start ms-settings:developers
|
||||
to open settings.
|
||||
</pre>
|
||||
|
||||
As stated, "Developer Mode" must be enabled:
|
||||
|
||||
1) Run `start ms-settings:developers`
|
||||
|
||||
2) In the panel, enable "Developer Mode" and click "Yes" in the popup.
|
||||
|
||||
3) Reinstall dependencies:
|
||||
|
||||
```bash
|
||||
flutter pub add http csv flutter_js
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
6) Open `pubspec.yaml` with a text editor. Search for the line that starts with
|
||||
`flutter:` (no whitespace) and add the highlighted lines:
|
||||
|
||||
@ -583,50 +424,12 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
|
||||
cd ..`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
mkdir -p scripts
|
||||
cd scripts
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl.exe -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
|
||||
cd ..`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
8) Download [`main.dart`](pathname:///flutter/main.dart) to `lib/main.dart`:
|
||||
|
||||
```bash
|
||||
curl -L -o lib/main.dart https://docs.sheetjs.com/flutter/main.dart
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
PowerShell `curl` is incompatible with the official `curl` program. The command
|
||||
may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'L'.
|
||||
```
|
||||
|
||||
`curl.exe` must be used instead:
|
||||
|
||||
```bash
|
||||
curl.exe -L -o lib/main.dart https://docs.sheetjs.com/flutter/main.dart
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Android
|
||||
|
||||
9) Start the Android emulator using the same instructions as Step 3.
|
||||
@ -637,53 +440,12 @@ curl.exe -L -o lib/main.dart https://docs.sheetjs.com/flutter/main.dart
|
||||
flutter run
|
||||
```
|
||||
|
||||
The app fetches https://docs.sheetjs.com/pres.numbers, parses, converts data to
|
||||
an array of arrays, and presents the data in a Flutter `Table` widget.
|
||||
|
||||
<details>
|
||||
<summary><b>If emulator is not detected</b> (click to show)</summary>
|
||||
|
||||
In some test runs, `flutter run` did not automatically detect the emulator.
|
||||
|
||||
Run `flutter -v -d sheetjs run` and the command will fail. Inspect the output:
|
||||
|
||||
```text title="Command output"
|
||||
// highlight-next-line
|
||||
[ +6 ms] No supported devices found with name or id matching 'sheetjs'.
|
||||
[ ] The following devices were found:
|
||||
...
|
||||
// highlight-next-line
|
||||
[ +26 ms] sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 13 (API 33) (emulator)
|
||||
[ ] macOS (desktop) • macos • darwin-arm64 • macOS 13.5.1 22G90 darwin-arm64
|
||||
...
|
||||
```
|
||||
|
||||
Search the output for `sheetjs`. After that line, search for the emulator list.
|
||||
One of the lines will correspond to the running emulator:
|
||||
|
||||
```
|
||||
[ +26 ms] sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 13 (API 33) (emulator)
|
||||
^^^^^^^^^^^^^--- the second column is the name
|
||||
```
|
||||
|
||||
The second column is the device name. Assuming the name is `emulator-5554`, run:
|
||||
|
||||
```bash
|
||||
flutter -v -d emulator-5554 run
|
||||
```
|
||||
|
||||
</details>
|
||||
The app fetches <https://sheetjs.com/pres.numbers>, parses, converts data to an
|
||||
array of arrays, and presents the data in a Flutter `Table` widget.
|
||||
|
||||
:::caution pass
|
||||
|
||||
In some test runs on low-power devices, it took 20 seconds for the app to fetch
|
||||
and display data!
|
||||
|
||||
:::
|
||||
|
||||
:::info Troubleshooting
|
||||
|
||||
In some demo runs, the build failed with an Android SDK error:
|
||||
When the demo was last run, there was a build error:
|
||||
|
||||
```
|
||||
│ The plugin flutter_js requires a higher Android SDK version. │
|
||||
@ -709,57 +471,6 @@ Searching for `minSdkVersion` should reveal the following line:
|
||||
minSdkVersion 21
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
In some demo runs, the build failed with an Android NDK error:
|
||||
|
||||
```
|
||||
Your project is configured with Android NDK 26.3.11579264, but the following plugin(s) depend on a different Android NDK version:
|
||||
- flutter_js requires Android NDK 27.0.12077973
|
||||
Fix this issue by using the highest Android NDK version (they are backward compatible).
|
||||
Add the following to /.../android/app/build.gradle.kts:
|
||||
|
||||
android {
|
||||
ndkVersion = "27.0.12077973"
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This was fixed by editing `android/app/build.gradle.kts`.
|
||||
|
||||
Searching for `ndkVersion` should reveal the following line:
|
||||
|
||||
```text title="android\app\build.gradle.kts"
|
||||
ndkVersion = flutter.ndkVersion
|
||||
```
|
||||
|
||||
`flutter.ndkVersion` should be replaced with `27.0.12077973`:
|
||||
|
||||
```text title="android\app\build.gradle.kts"
|
||||
ndkVersion = "27.0.12077973"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
In some demo runs, the build failed with an Android namespace error:
|
||||
|
||||
```
|
||||
A problem occurred configuring project ':flutter_js'.
|
||||
> Could not create an instance of type com.android.build.api.variant.impl.LibraryVariantBuilderImpl.
|
||||
> Namespace not specified. Specify a namespace in the module's build file: /Users/sheetjs/.pub-cache/hosted/pub.dev/flutter_js-0.8.2/android/build.gradle. See https://d.android.com/r/tools/upgrade-assistant/set-namespace for information about setting the namespace.
|
||||
```
|
||||
|
||||
This affects `flutter_js` version `0.8.2`.
|
||||
|
||||
The file (`flutter_js-0.8.2/android/build.gradle`) should be manually edited. In
|
||||
the `android` block, add a `namespace` field:
|
||||
|
||||
```text title="cached flutter_js android/build.gradle (add highlighted line)"
|
||||
android {
|
||||
// highlight-next-line
|
||||
namespace "io.abner.flutter_js"
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
11) Close the Android emulator.
|
||||
@ -774,8 +485,8 @@ android {
|
||||
flutter run
|
||||
```
|
||||
|
||||
The app fetches https://docs.sheetjs.com/pres.numbers, parses, converts data to
|
||||
an array of arrays, and presents the data in a Flutter `Table` widget.
|
||||
The app fetches <https://sheetjs.com/pres.numbers>, parses, converts data to an
|
||||
array of arrays, and presents the data in a Flutter `Table` widget.
|
||||
|
||||
### Android Device
|
||||
|
||||
@ -792,7 +503,7 @@ flutter devices
|
||||
The list should include the device:
|
||||
|
||||
```
|
||||
SheetJS (mobile) • 726272627262726272 • android-arm64 • Android 11 (API 30)
|
||||
SheetJS (mobile) • 1234567890 • android-arm64 • Android 10 (API 29)
|
||||
^^^^^^^--- the first column is the name
|
||||
```
|
||||
|
||||
@ -802,37 +513,13 @@ The list should include the device:
|
||||
flutter build apk --release
|
||||
```
|
||||
|
||||
:::info Troubleshooting
|
||||
|
||||
In some demo runs, the build failed with an Android resource error:
|
||||
|
||||
```
|
||||
Execution failed for task ':flutter_js:verifyReleaseResources'.
|
||||
> A failure occurred while executing com.android.build.gradle.tasks.VerifyLibraryResourcesTask$Action
|
||||
> Android resource linking failed
|
||||
ERROR: /private/tmp/sheetjs_flutter/build/flutter_js/intermediates/merged_res/release/mergeReleaseResources/values/values.xml:194: AAPT: error: resource android:attr/lStar not found.
|
||||
```
|
||||
|
||||
The file (`flutter_js-0.8.2/android/build.gradle`) should be manually edited. In
|
||||
the `android` block, force the `compileSdkVersion` to be `31`:
|
||||
|
||||
```text title="cached flutter_js android/build.gradle (add highlighted line)"
|
||||
android {
|
||||
// highlight-next-line
|
||||
compileSdkVersion 31
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
17) Install on the Android device:
|
||||
|
||||
```bash
|
||||
flutter install
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
The script may ask for a device:
|
||||
The script will ask for a device:
|
||||
|
||||
```
|
||||
[1]: SheetJS (1234567890)
|
||||
@ -844,9 +531,7 @@ Please choose one (or "q" to quit):
|
||||
|
||||
Select the number corresponding to the device.
|
||||
|
||||
:::
|
||||
|
||||
18) Launch the installed `sheetjs_flutter` app on the device.
|
||||
18) Launch the installed `sheetjs_flutter` app on the device
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -869,7 +554,7 @@ flutter devices
|
||||
The list should include the device:
|
||||
|
||||
```
|
||||
SheetPad (mobile) • 00000000-0000000000000000 • ios • iOS 15.1 19B74
|
||||
SheetPad (mobile) • 00000000-0000000000000000 • ios • iOS 15.1 19B74
|
||||
^^^^^^^^--- the first column is the name
|
||||
```
|
||||
|
||||
@ -879,67 +564,8 @@ The list should include the device:
|
||||
flutter run -d SheetPad
|
||||
```
|
||||
|
||||
In debug mode, "Flutter tools" will attempt to connect to the running app. The
|
||||
device will ask for permission:
|
||||
|
||||
> "Sheetjs Flutter" would like to find and connect to devices on your local network.
|
||||
|
||||
Tap "OK" to continue.
|
||||
|
||||
:::info pass
|
||||
|
||||
In some test runs, the app requested for local network access:
|
||||
|
||||
> "Sheetjs Flutter" would like to find and connect to devices on your local network.
|
||||
|
||||
Local network access is not required for the demo. Select "Don't Allow".
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
When this demo was last tested, the build failed with an error:
|
||||
|
||||
```text
|
||||
Could not build the precompiled application for the device.
|
||||
Error (Xcode): No profiles for 'com.example.sheetjsFlutter' were found: Xcode couldn't find any iOS App Development provisioning profiles matching 'com.example.sheetjsFlutter'. Automatic signing is disabled and unable to generate a profile. To enable automatic signing, pass -allowProvisioningUpdates to xcodebuild.
|
||||
```
|
||||
|
||||
The message includes a hint:
|
||||
|
||||
```
|
||||
Verify that the Bundle Identifier in your project is your signing id in Xcode
|
||||
open ios/Runner.xcworkspace
|
||||
```
|
||||
|
||||
Open the workspace and select the "Runner" project in the Navigator. In the main
|
||||
pane, select "Signing & Capabilities" and ensure a Team is selected. From
|
||||
the menu bar, select "Product" > "Run" to run the app.
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
If there is an "Untrusted Developer" error, the certificate must be trusted on
|
||||
the device. The following steps were verified in iOS 15.1:
|
||||
|
||||
1) Open the "Settings" app on the device
|
||||
|
||||
In the "APPS FROM DEVELOPER" section of the new screen, "Sheetjs Flutter" should
|
||||
be displayed. If it is missing, tap the "<" button near the top of the screen
|
||||
and select a different certificate from the list.
|
||||
|
||||
2) Select "General" > "VPN & Device Management".
|
||||
|
||||
3) In the "DEVELOPER APP" section, tap the certificate that is "Not Trusted".
|
||||
|
||||
4) After confirming "Sheetjs Flutter" is in the list, tap the "Trust" button and
|
||||
tap "Trust" in the popup.
|
||||
|
||||
:::
|
||||
|
||||
[^1]: https://dart.dev/ is the official site for the Dart Programming Language.
|
||||
[^2]: https://flutter.dev/ is the official site for the Flutter Framework.
|
||||
[^1]: <https://dart.dev/> is the official site for the Dart Programming Language.
|
||||
[^2]: <https://flutter.dev/> is the official site for the Flutter Framework.
|
||||
[^3]: [The `flutter_js` package](https://pub.dev/packages/flutter_js) is hosted on the Dart package repository.
|
||||
[^4]: See [the dedicated "Swift + JavaScriptCore" demo](/docs/demos/engines/jsc) for more details.
|
||||
[^5]: See [the dedicated "C + QuickJS" demo](/docs/demos/engines/quickjs) for more details.
|
||||
|
||||
@ -1,402 +0,0 @@
|
||||
---
|
||||
title: Sheets at Native Speed with Lynx
|
||||
sidebar_label: Lynx
|
||||
description: Build data-intensive mobile apps with Lynx. Seamlessly integrate spreadsheets into your app using SheetJS. Securely process and generate Excel files in the field.
|
||||
pagination_prev: demos/static/index
|
||||
pagination_next: demos/desktop/index
|
||||
sidebar_position: 7
|
||||
sidebar_custom_props:
|
||||
summary: React + Native Rendering
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
export const r = {style: {color:"red"}};
|
||||
export const g = {style: {color:"green"}};
|
||||
export const y = {style: {color:"gold"}};
|
||||
export const gr = {style: {color:"gray"}};
|
||||
|
||||
[Lynx](https://lynxjs.org/) is a modern cross-platform framework. It builds iOS,
|
||||
Android and Web apps that use JavaScript for describing layouts and events.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
:::caution Lynx support is considered experimental.
|
||||
|
||||
Lynx is relatively new and does not currently have a deep community.
|
||||
|
||||
Any issues should be reported to the Lynx project for further diagnosis.
|
||||
|
||||
:::
|
||||
|
||||
This demo uses React (using [ReactLynx](https://lynxjs.org/react)) and SheetJS
|
||||
to process and generate spreadsheets. We'll explore how to load SheetJS in Lynx
|
||||
apps in the following scenarios:
|
||||
|
||||
- ["Fetching Remote Data"](#fetching-remote-data) uses the built-in `fetch` to download
|
||||
and parse remote workbook files.
|
||||
|
||||
The "Fetching Remote Data" example creates an app that looks like the screenshots below:
|
||||
|
||||
<table><thead><tr>
|
||||
<th><a href="#demo">iOS</a></th>
|
||||
<th><a href="#demo">Android</a></th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||

|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
:::caution pass
|
||||
|
||||
**Before testing this demo, follow the official React Lynx Guide!**[^1]
|
||||
|
||||
Follow the instructions for iOS (requires macOS) and for Android. They will
|
||||
cover installation and system configuration. You should be able to build and run
|
||||
a sample app in the Android and the iOS (if applicable) simulators.
|
||||
|
||||
:::
|
||||
|
||||
:::danger pass
|
||||
|
||||
**Lynx development requires an Apple Silicon-powered Macintosh!**
|
||||
|
||||
[X64 is currently unsupported.](https://github.com/lynx-family/lynx/issues/219)
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from any component or script in the app.
|
||||
|
||||
### Internal State
|
||||
|
||||
For simplicity, this demo uses an "Array of Arrays"[^2] as the internal state.
|
||||
|
||||
|
||||
<table>
|
||||
<thead><tr><th>Spreadsheet</th><th>Array of Arrays</th></tr></thead>
|
||||
<tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
```js
|
||||
[
|
||||
["Name", "Index"],
|
||||
["Bill Clinton", 42],
|
||||
["GeorgeW Bush", 43],
|
||||
["Barack Obama", 44],
|
||||
["Donald Trump", 45],
|
||||
["Joseph Biden", 46]
|
||||
]
|
||||
```
|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
Each array represents a row in the table.
|
||||
|
||||
This demo also keeps track of the column widths as a single array of numbers.
|
||||
The widths are used by the display component.
|
||||
|
||||
```ts title="State variables"
|
||||
const [data, setData] = useState<any[]>([
|
||||
"SheetJS".split(""),
|
||||
[5, 4, 3, 3, 7, 9, 5],
|
||||
[8, 6, 7, 5, 3, 0, 9]
|
||||
]);
|
||||
const [widths, setWidths] = useState<number[]>(Array.from({ length: 7 }, () => 20));
|
||||
```
|
||||
|
||||
#### Updating State
|
||||
|
||||
Starting from a SheetJS worksheet object, `sheet_to_json`[^3] with the `header`
|
||||
option can generate an array of arrays:
|
||||
|
||||
```js title="Updating state from a workbook"
|
||||
/* assuming `wb` is a SheetJS workbook */
|
||||
function update_state(wb) {
|
||||
/* convert first worksheet to AOA */
|
||||
const wsname = wb.SheetNames[0];
|
||||
const ws = wb.Sheets[wsname];
|
||||
const data = utils.sheet_to_json(ws, {header:1});
|
||||
|
||||
/* update state */
|
||||
setData(data);
|
||||
|
||||
/* update column widths */
|
||||
setWidths(make_width(data));
|
||||
}
|
||||
```
|
||||
|
||||
_Calculating Column Widths_
|
||||
|
||||
Column widths can be calculated by walking each column and calculating the max
|
||||
data width. Using the array of arrays:
|
||||
|
||||
```js title="Calculating column widths"
|
||||
/* this function takes an array of arrays and generates widths */
|
||||
function make_width(aoa) {
|
||||
/* walk each row */
|
||||
aoa.forEach((r) => {
|
||||
/* walk each column */
|
||||
r.forEach((c, C) => {
|
||||
/* update column width based on the length of the cell contents */
|
||||
res[C] = Math.max(res[C]||60, String(c).length * 10);
|
||||
});
|
||||
});
|
||||
/* use a default value for columns with no data */
|
||||
for(let C = 0; C < res.length; ++C) if(!res[C]) res[C] = 60;
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
### Displaying Data
|
||||
|
||||
Lynx does not ship with a component for displaying tabular data.
|
||||
|
||||
The demo uses Lynx `<view/>` and `<text/>` elements to display tabular data:
|
||||
|
||||
```tsx title="Example JSX for displaying data"
|
||||
{/* Table container */}
|
||||
<view className='Table'>
|
||||
{/* Map through each row in the data array */}
|
||||
{data.map((row, rowIndex) => (
|
||||
<view key={`row-${rowIndex}`} className="Row">
|
||||
{/* Map through each cell in the current row */}
|
||||
{Array.isArray(row) && row.map((cell, cellIndex) => (
|
||||
{/* Cell with dynamic width based on content */}
|
||||
<view
|
||||
key={`cell-${rowIndex}-${cellIndex}`} className="Cell"
|
||||
style={{ width: `${widths[cellIndex]}px` }}>
|
||||
{/* Display cell content as text */}
|
||||
<text>{String(cell)}</text>
|
||||
</view>
|
||||
))}
|
||||
</view>
|
||||
))}
|
||||
</view>
|
||||
```
|
||||
|
||||
## Fetching Remote Data
|
||||
|
||||
This snippet downloads and parses https://docs.sheetjs.com/pres.xlsx:
|
||||
|
||||
```js
|
||||
/* fetch data into an ArrayBuffer */
|
||||
const ab = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
/* parse data */
|
||||
const wb = XLSX.read(ab);
|
||||
```
|
||||
|
||||
### Fetch Demo
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
**Simulators**
|
||||
|
||||
| OS | Device | Lynx | LynxExplorer | Dev Platform | Date |
|
||||
|:-----------|:--------------------|:---------|:-------------|:-------------|:-----------|
|
||||
| Android 35 | Pixel 3a | `0.8.6` | `3.2.0-rc.1` | `darwin-arm` | 2025-03-26 |
|
||||
| iOS 18.3 | iPhone 16 Pro | `0.8.6` | `3.2.0-rc.1` | `darwin-arm` | 2025-03-26 |
|
||||
|
||||
:::
|
||||
|
||||
:::danger Real Devices
|
||||
|
||||
When this demo was last tested, there was no simple standalone guide for running
|
||||
Lynx apps on real devices.
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
First install Lynx by following the Guide![^1].
|
||||
|
||||
Make sure you can run a basic test app on your simulator before continuing!
|
||||
|
||||
:::
|
||||
|
||||
0) Install Lynx dependencies
|
||||
|
||||
1) Create project:
|
||||
|
||||
```bash
|
||||
npm create rspeedy@0.8.6 -- -d SheetJSLynxFetch -t react-ts --tools biome
|
||||
```
|
||||
|
||||
2) Install shared dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
cd SheetJSLynxFetch
|
||||
curl -o ./src/assets/SheetJS-logo.png https://docs.sheetjs.com/logo.png
|
||||
npm i
|
||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Download [`App.tsx`](pathname:///lynx/App.tsx) into the `src` folder:
|
||||
|
||||
```bash
|
||||
curl -o ./src/App.tsx https://docs.sheetjs.com/lynx/App.tsx
|
||||
```
|
||||
|
||||
4) Download [`App.css`](pathname:///lynx/App.css) into the `src` folder:
|
||||
|
||||
```bash
|
||||
curl -o ./src/App.css https://docs.sheetjs.com/lynx/App.css
|
||||
```
|
||||
<a name="step5"></a>
|
||||
|
||||
5) Start the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Keep the window open.
|
||||
|
||||
#### Android
|
||||
|
||||
6) Start the Android emulator:
|
||||
|
||||
<Tabs>
|
||||
<TabItem name="Android Studio" value="Android Studio">
|
||||
|
||||
In Android Studio, click "More actions" > "Virtual Device Manager". Look for the
|
||||
emulated device in the list and click the ▶ button to play.
|
||||
|
||||
</TabItem>
|
||||
<TabItem name="Android Studio" value="Command Line">
|
||||
|
||||
List the available emulators with `emulator -list-avds`:
|
||||
|
||||
```
|
||||
shjs@sheetjs SheetJSLynxFetch % emulator -list-avds
|
||||
Medium_Phone_API_35
|
||||
^^^^^^^^^^^^^^^^^^^--- emulator name
|
||||
```
|
||||
|
||||
The emulator name should be passed to `emulator -avd`. In a previous test, the
|
||||
name was `Medium_Phone_API_35` and the launch command was:
|
||||
|
||||
```bash
|
||||
emulator -avd Medium_Phone_API_35
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
On macOS, `~/Library/Android/sdk/emulator/` is the typical location
|
||||
for the `emulator` binary. If it cannot be found, add the folder to `PATH`:
|
||||
|
||||
```bash
|
||||
export PATH="$PATH":~/Library/Android/sdk/emulator
|
||||
emulator -avd Medium_Phone_API_35
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
7) Download the LynxExplorer[^4] APK.
|
||||
|
||||
The latest test used [`LynxExplorer-noasan-release.apk` for version `3.2.0-rc.1`](https://github.com/lynx-family/lynx/releases/download/3.2.0-rc.1/LynxExplorer-noasan-release.apk).
|
||||
|
||||
8) Drag and drop the APK into the Android emulator window.
|
||||
|
||||
The emulator will install LynxExplorer.
|
||||
|
||||
9) In the terminal window from [step 5](#step5), copy the HTTP link. It will be
|
||||
printed below the QR code, as shown in the following screenshot:
|
||||
|
||||

|
||||
|
||||
10) In the emulator, open the "LynxExplorer" app.
|
||||
|
||||
11) In the **Enter Card URL** input field, paste the link. Tap **Go**.
|
||||
|
||||
The view will refresh. The app should look like the "Before" screenshot:
|
||||
|
||||
<table><thead><tr>
|
||||
<th>Before</th>
|
||||
<th>After</th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||

|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
12) Tap "Import data from a spreadsheet" and verify that the app shows new data.
|
||||
The app should look like the "After" screenshot.
|
||||
|
||||
|
||||
**iOS Testing**
|
||||
|
||||
:::danger pass
|
||||
|
||||
**iOS testing can only be performed on Apple hardware running macOS!**
|
||||
|
||||
Xcode and iOS simulators are not available on Windows or Linux.
|
||||
|
||||
:::
|
||||
|
||||
13) Download the LynxExplorer[^4] app tarball.
|
||||
|
||||
The latest test used [`LynxExplorer-arm64.app.tar.gz` for version `3.2.0-rc.1`](https://github.com/lynx-family/lynx/releases/download/3.2.0-rc.1/LynxExplorer-arm64.app.tar.gz).
|
||||
|
||||
14) Open `LynxExplorer-arm64.app.tar.gz` using Finder.
|
||||
|
||||
The tarball contains an app named `LynxExplorer-arm64` .
|
||||
|
||||
15) Launch the iOS Simulator.
|
||||
|
||||
16) Click and drag `LynxExplorer-arm64` into the Simulator window.
|
||||
|
||||
The simulator will install the "LynxExplorer" app.
|
||||
|
||||
17) Copy the HTTP link from the terminal window in [step 5](#step5).
|
||||
|
||||

|
||||
|
||||
18) Tap the "LynxExplorer" icon in the simulator to launch the app.
|
||||
|
||||
19) Tap the **Enter Card URL** input field and paste the link. Tap **Go**.
|
||||
|
||||
The view will refresh. The app should look like the "Before" screenshot:
|
||||
|
||||
<table><thead><tr>
|
||||
<th>Before</th>
|
||||
<th>After</th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||

|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
20) Tap "Import data from a spreadsheet" and verify that the app shows new data. The app should look like the "After" screenshot.
|
||||
|
||||
[^1]: Follow ["Quick Start"](https://lynxjs.org/guide/start/quick-start.html) in
|
||||
the Lynx documentation and select the appropriate "Lynx Explorer sandbox"
|
||||
[^2]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays)
|
||||
[^3]: See ["Array Output" in "Utility Functions"](/docs/api/utilities/array#array-output)
|
||||
[^4]: See ["LynxExplorer sandbox"](https://github.com/lynx-family/lynx/releases/latest/)
|
||||
@ -1,28 +1,18 @@
|
||||
---
|
||||
title: Tables on Tablets and Mobile Devices
|
||||
sidebar_label: iOS and Android Apps
|
||||
title: iOS and Android Apps
|
||||
pagination_prev: demos/static/index
|
||||
pagination_next: demos/desktop/index
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
import EngineData from '/data/mobile.js'
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
Many mobile app frameworks mix JavaScript / CSS / HTML5 concepts with native
|
||||
extensions and libraries to create a hybrid development experience. Developers
|
||||
well-versed in web technologies can now build actual mobile applications that
|
||||
run on iOS and Android!
|
||||
|
||||
The demos in this section showcase a number of mobile frameworks. In each case,
|
||||
we will build a sample app that loads SheetJS library scripts and processes
|
||||
on-device and remote spreadsheet files.
|
||||
|
||||
:::danger pass
|
||||
:::warning pass
|
||||
|
||||
**The ecosystem has broken backwards-compatibility many times!**
|
||||
|
||||
@ -34,6 +24,10 @@ MacOS is required for the iOS demos. The Android demos were tested on MacOS.
|
||||
|
||||
:::
|
||||
|
||||
The ["JavaScript Engines"](/docs/demos/engines) section includes samples for JS
|
||||
engines used in the mobile app frameworks. SheetJS libraries have been tested
|
||||
in the relevant engines and should "just work" with some caveats.
|
||||
|
||||
Demos for common tools are included in separate pages. Each demo section will
|
||||
mention test dates and platform versions.
|
||||
|
||||
@ -46,14 +40,6 @@ mention test dates and platform versions.
|
||||
</li>);
|
||||
})}</ul>
|
||||
|
||||
:::info pass
|
||||
|
||||
The ["JavaScript Engines"](/docs/demos/engines) section includes samples for JS
|
||||
engines used in the mobile app frameworks. SheetJS libraries have been tested
|
||||
in the relevant engines.
|
||||
|
||||
:::
|
||||
|
||||
:::note Recommendation
|
||||
|
||||
React Native is extremely popular and is the recommended choice for greenfield
|
||||
@ -61,9 +47,9 @@ projects that can use community modules. However, its "lean core" approach
|
||||
forces developers to learn iOS/Android programming or use community modules to
|
||||
provide basic app features.
|
||||
|
||||
The original Web View framework was PhoneGap/Cordova. The modern frameworks are
|
||||
built atop Cordova. Cordova is waning in popularity but it has a deep library of
|
||||
community modules to solve many problems.
|
||||
The original Web View framework was PhoneGap/Cordova. The modern frameworks
|
||||
are built atop Cordova. Cordova is waning in popularity but it has a deep
|
||||
library of community modules to solve many problems.
|
||||
|
||||
Before creating a new app, it is important to identify what features the app
|
||||
should support and investigate community modules. If there are popular modules
|
||||
@ -71,16 +57,3 @@ for features that must be included, or for teams that are comfortable with
|
||||
native app development, React Native is the obvious choice.
|
||||
|
||||
:::
|
||||
|
||||
### Platforms
|
||||
|
||||
The following frameworks have been tested:
|
||||
|
||||
<EngineData/>
|
||||
|
||||
:::info pass
|
||||
|
||||
When this table was last updated, it was not possible to build an iOS app from
|
||||
Linux or Windows. Android tooling runs on MacOS, Linux and Windows.
|
||||
|
||||
:::
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Electrified Sheets with Electron
|
||||
sidebar_label: Electron
|
||||
title: Electron
|
||||
pagination_prev: demos/mobile/index
|
||||
pagination_next: demos/cli/index
|
||||
sidebar_position: 1
|
||||
@ -11,14 +10,10 @@ sidebar_custom_props:
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
[Electron](https://www.electronjs.org/) is a modern toolkit for building desktop
|
||||
apps. Electron apps use the same technologies powering Chromium and NodeJS.
|
||||
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
|
||||
from the main or the renderer thread.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
The ["Complete Example"](#complete-example) section covers a complete desktop
|
||||
app to read and write workbooks. The app will look like the screenshots below:
|
||||
The "Complete Example" creates an app that looks like the screenshots below:
|
||||
|
||||
<table><thead><tr>
|
||||
<th><a href="#complete-example">Windows</a></th>
|
||||
@ -40,53 +35,8 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from the main or the renderer thread.
|
||||
|
||||
The SheetJS `readFile` and `writeFile` methods will use the Electron `fs` module
|
||||
where available.
|
||||
|
||||
<details>
|
||||
<summary><b>Renderer Configuration</b> (click to show)</summary>
|
||||
|
||||
Electron 9 and later require the preference `nodeIntegration: true` in order to
|
||||
`require('xlsx')` in the renderer process.
|
||||
|
||||
Electron 12 and later also require `worldSafeExecuteJavascript: true` and
|
||||
`contextIsolation: true`.
|
||||
|
||||
</details>
|
||||
|
||||
:::caution pass
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be loaded from the renderer thread using a standard `SCRIPT` tag, but it is
|
||||
strongly discouraged.
|
||||
|
||||
[Issue 3314](https://git.sheetjs.com/sheetjs/sheetjs/issues/3314) in the SheetJS
|
||||
CE issue tracker describes the required HTML configuration.
|
||||
|
||||
<details>
|
||||
<summary><b>HTML Configuration</b> (click to show)</summary>
|
||||
|
||||
The following CSP directives should be specified in the `HEAD` block:
|
||||
|
||||
```html
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.sheetjs.com"
|
||||
/>
|
||||
<meta
|
||||
http-equiv="X-Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.sheetjs.com"
|
||||
/>
|
||||
```
|
||||
|
||||
Without these settings, newer Electron versions will not run the scripts.
|
||||
|
||||
</details>
|
||||
|
||||
:::
|
||||
Electron presents a `fs` module. The `require('xlsx')` call loads the CommonJS
|
||||
module, so `XLSX.readFile` and `XLSX.writeFile` work in the renderer thread.
|
||||
|
||||
### Reading Files
|
||||
|
||||
@ -238,12 +188,12 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Electron | Date |
|
||||
|:---------------|:-------------|:---------|:-----------|
|
||||
| macOS 15.3 | `darwin-x64` | `35.1.2` | 2025-03-31 |
|
||||
| macOS 14.5 | `darwin-arm` | `35.1.2` | 2025-08-30 |
|
||||
| Windows 11 | `win11-x64` | `33.2.1` | 2025-02-11 |
|
||||
| Windows 11 | `win11-arm` | `33.2.1` | 2025-02-23 |
|
||||
| Linux (Ubuntu) | `linux-x64` | `35.1.2` | 2025-07-06 |
|
||||
| Linux (Debian) | `linux-arm` | `33.2.1` | 2025-02-16 |
|
||||
| macOS 14.4 | `darwin-x64` | `29.1.4` | 2024-03-15 |
|
||||
| macOS 14.1.2 | `darwin-arm` | `27.1.3` | 2023-12-01 |
|
||||
| Windows 10 | `win10-x64` | `28.2.0` | 2024-03-04 |
|
||||
| Windows 11 | `win11-arm` | `27.1.3` | 2023-12-01 |
|
||||
| Linux (HoloOS) | `linux-x64` | `29.1.4` | 2024-03-21 |
|
||||
| Linux (Debian) | `linux-arm` | `27.1.3` | 2023-12-01 |
|
||||
|
||||
:::
|
||||
|
||||
@ -281,26 +231,6 @@ curl -LO https://docs.sheetjs.com/electron/index.html
|
||||
curl -LO https://docs.sheetjs.com/electron/index.js
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'LO'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/package.json
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/main.js
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/index.html
|
||||
curl.exe -LO https://docs.sheetjs.com/electron/index.js
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
|
||||
2) Install dependencies:
|
||||
|
||||
```bash
|
||||
@ -313,29 +243,7 @@ npm install
|
||||
npx -y electron .
|
||||
```
|
||||
|
||||
The app will run.
|
||||
|
||||
:::info pass
|
||||
|
||||
In some Linux deployments, the command will fail with a sandbox error:
|
||||
|
||||
```
|
||||
The SUID sandbox helper binary was found, but is not configured correctly. Rather than run without sandboxing I'm aborting now.
|
||||
```
|
||||
|
||||
The workaround is to temporarily disable the sandbox restriction:
|
||||
|
||||
```bash
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
```
|
||||
|
||||
After testing, the restriction can be re-enabled with a similar command:
|
||||
|
||||
```bash
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=1
|
||||
```
|
||||
|
||||
:::
|
||||
The app will show.
|
||||
|
||||
4) To build a standalone app, run the builder:
|
||||
|
||||
@ -360,18 +268,15 @@ The program will run on ARM64 Windows.
|
||||
|
||||
### Testing
|
||||
|
||||
5) Download [the test file `pres.numbers`](https://docs.sheetjs.com/pres.numbers)
|
||||
5) Download [the test file `pres.numbers`](https://sheetjs.com/pres.numbers)
|
||||
|
||||
6) Launch the generated application:
|
||||
|
||||
| Architecture | Command |
|
||||
|:-------------|:--------------------------------------------------------------|
|
||||
| `darwin-x64` |`open ./out/sheetjs-electron-darwin-x64/sheetjs-electron.app` |
|
||||
| `darwin-arm` |`open ./out/sheetjs-electron-darwin-arm64/sheetjs-electron.app`|
|
||||
| `win11-x64` |`.\out\sheetjs-electron-win32-x64\sheetjs-electron.exe` |
|
||||
| `win11-arm` |`.\out\sheetjs-electron-win32-x64\sheetjs-electron.exe` |
|
||||
| `linux-x64` |`./out/sheetjs-electron-linux-x64/sheetjs-electron` |
|
||||
| `linux-arm` |`./out/sheetjs-electron-linux-arm64/sheetjs-electron` |
|
||||
| `darwin-x64` | `open ./out/sheetjs-electron-darwin-x64/sheetjs-electron.app` |
|
||||
| `win10-x64` | `.\out\sheetjs-electron-win32-x64\sheetjs-electron.exe` |
|
||||
| `linux-x64` | `./out/sheetjs-electron-linux-x64/sheetjs-electron` |
|
||||
|
||||
#### Electron API
|
||||
|
||||
@ -413,8 +318,8 @@ and select `pres.numbers`.
|
||||
|
||||
## Electron Breaking Changes
|
||||
|
||||
The first version of this demo used Electron `1.7.5`. The current demo includes
|
||||
the required changes for Electron `35.1.2`.
|
||||
The first version of this demo used Electron 1.7.5. The current demo includes
|
||||
the required changes for Electron 28.2.0.
|
||||
|
||||
There are no Electron-specific workarounds in the library, but Electron broke
|
||||
backwards compatibility multiple times. A summary of changes is noted below.
|
||||
@ -428,7 +333,6 @@ methods have been renamed:
|
||||
|:-----------------|:---------------------|
|
||||
| `showOpenDialog` | `showOpenDialogSync` |
|
||||
| `showSaveDialog` | `showSaveDialogSync` |
|
||||
|
||||
**This change was not properly documented!**
|
||||
|
||||
Electron 9 and later require the preference `nodeIntegration: true` in order to
|
||||
@ -442,4 +346,4 @@ Electron 14 and later must use `@electron/remote` instead of `remote`. An
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See ["Makers"](https://www.electronforge.io/config/makers) in the Electron Forge documentation. On Linux, the demo generates `rpm` and `deb` distributables. On Arch Linux and the Steam Deck, `sudo pacman -Syu rpm-tools dpkg fakeroot` installed required packages. On Debian and Ubuntu, `sudo apt-get install rpm` sufficed.
|
||||
[^1]: See ["Makers"](https://www.electronforge.io/config/makers) in the Electron Forge documentation. On Linux, the demo generates `rpm` and `deb` distributables. On Arch Linux and the Steam Deck, `sudo pacman -Syu rpm-tools dpkg fakeroot` installed required packages.
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
title: Sheets in NW.js
|
||||
sidebar_label: NW.js
|
||||
title: NW.js
|
||||
pagination_prev: demos/mobile/index
|
||||
pagination_next: demos/cli/index
|
||||
sidebar_position: 2
|
||||
@ -13,14 +12,10 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[NW.js](https://nwjs.io/), formerly `node-webkit`, is a modern toolkit for
|
||||
building desktop apps using web technologies.
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be referenced in a `SCRIPT` tag from the entry point HTML page.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
The ["Complete Example"](#complete-example) section covers a complete desktop
|
||||
app to read and write workbooks. The app will look like the screenshots below:
|
||||
The "Complete Example" creates an app that looks like the screenshots below:
|
||||
|
||||
<table><thead><tr>
|
||||
<th><a href="#complete-example">Windows</a></th>
|
||||
@ -42,9 +37,6 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be referenced in a `SCRIPT` tag from the entry point HTML page.
|
||||
|
||||
NW.js provides solutions for reading and writing files.
|
||||
|
||||
### Reading Files
|
||||
@ -119,14 +111,16 @@ input.click();
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | NW.js | Date | Notes |
|
||||
|:---------------|:-------------|:----------|:-----------|:---------------------|
|
||||
| macOS 15.3.2 | `darwin-x64` | `0.94.0` | 2025-03-31 | |
|
||||
| macOS 14.5 | `darwin-arm` | `0.94.0` | 2025-03-30 | |
|
||||
| Windows 11 | `win11-x64` | `0.100.0` | 2025-05-27 | |
|
||||
| Windows 11 | `win11-arm` | `0.94.0` | 2025-02-23 | |
|
||||
| Linux (Ubuntu) | `linux-x64` | `0.101.2` | 2025-07-06 | |
|
||||
| Linux (Debian) | `linux-arm` | `0.60.0` | 2025-02-16 | Unofficial build[^1] |
|
||||
| OS and Version | Architecture | NW.js | Date |
|
||||
|:---------------|:-------------|:---------|:-----------|
|
||||
| macOS 14.3.1 | `darwin-x64` | `0.85.0` | 2024-03-12 |
|
||||
| macOS 14.1.2 | `darwin-arm` | `0.82.0` | 2023-12-01 |
|
||||
| Windows 10 | `win10-x64` | `0.83.0` | 2024-03-04 |
|
||||
| Windows 11 | `win11-arm` | `0.82.0` | 2023-12-01 |
|
||||
| Linux (HoloOS) | `linux-x64` | `0.85.0` | 2024-03-12 |
|
||||
|
||||
There is no official Linux ARM64 release. The community release[^1] was tested
|
||||
and verified on 2023-09-27.
|
||||
|
||||
:::
|
||||
|
||||
@ -146,7 +140,7 @@ cd sheetjs-nwjs
|
||||
"version": "0.0.0",
|
||||
"main": "index.html",
|
||||
"dependencies": {
|
||||
"nw": "0.101.2",
|
||||
"nw": "0.85.0",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz"
|
||||
}
|
||||
}`}
|
||||
@ -167,22 +161,6 @@ In the terminal window, the download can be performed with:
|
||||
curl -LO https://docs.sheetjs.com/nwjs/index.html
|
||||
```
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'L'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
```bash
|
||||
curl.exe -LO https://docs.sheetjs.com/nwjs/index.html
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
3) Install dependencies:
|
||||
|
||||
```bash
|
||||
@ -195,60 +173,15 @@ npm i
|
||||
npx nw .
|
||||
```
|
||||
|
||||
On launch, the app will fetch and parse https://docs.sheetjs.com/pres.numbers .
|
||||
|
||||
Using the file input element, a file can be selected from the filesystem and the
|
||||
table will refresh with the contents of the selected file.
|
||||
|
||||
Click "Export Data!" and save the generated file to `SheetJSNWDemo.xlsx`. This
|
||||
file can be opened in Excel or another spreadsheet editor.
|
||||
|
||||
<details>
|
||||
<summary><b>Linux ARM64 support</b> (click to show)</summary>
|
||||
|
||||
NW.js does not officially support `linux-arm`. The official recommendation is to
|
||||
use a third-party pre-built version.
|
||||
|
||||
```bash
|
||||
curl -LO https://github.com/LeonardLaszlo/nw.js-armv7-binaries/releases/download/nw60-arm64_2022-01-08/nw60-arm64_2022-01-08.tar.gz
|
||||
tar -xzf nw60-arm64_2022-01-08.tar.gz
|
||||
cp usr/docker/dist/nwjs-chromium-ffmpeg-branding/nwjs-v0.60.1-linux-arm64.tar.gz
|
||||
tar -xzf nwjs-v0.60.1-linux-arm64.tar.gz
|
||||
./nwjs-v0.60.1-linux-arm64/nw .
|
||||
```
|
||||
|
||||
Unfortunately `nw-builder` will not be able to build a standalone program.
|
||||
|
||||
</details>
|
||||
The app will show and you should be able to verify reading and writing by using
|
||||
the file input element to select a spreadsheet and clicking the export button.
|
||||
|
||||
5) To build a standalone app, run the builder:
|
||||
|
||||
```bash
|
||||
npx -p nw-builder@4.13.14 nwbuild --mode=build --version=0.101.2 --glob=false --outDir=../out ./
|
||||
npx -p nw-builder nwbuild --mode=build --version=0.85.0 --glob=false --outDir=../out ./
|
||||
```
|
||||
|
||||
This will generate the standalone app in the `..\out\` folder.
|
||||
|
||||
:::caution pass
|
||||
|
||||
There is a regression in `nw-builder` versions `4.12.0` and `4.13.14`.
|
||||
|
||||
In local `win11-x64` testing, `4.11.6` generates the standalone application.
|
||||
|
||||
```bash
|
||||
npx -p nw-builder@4.11.6 nwbuild --mode=build --version=0.101.2 --glob=false --outDir=../out ./
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
6) Launch the generated application:
|
||||
|
||||
| Architecture | Command |
|
||||
|:-------------|:--------------------------------------------------------------|
|
||||
| `darwin-x64` | `open ../out/sheetjs-nwjs.app` |
|
||||
| `darwin-arm` | `open ../out/sheetjs-nwjs.app` |
|
||||
| `win11-x64` | `..\out\sheetjs-nwjs.exe` |
|
||||
| `win11-arm` | `..\out\sheetjs-nwjs.exe` |
|
||||
| `linux-x64` | `../out/sheetjs-nwjs` |
|
||||
|
||||
[^1]: The [`nw60-arm64_2022-01-08` release](https://github.com/LeonardLaszlo/nw.js-armv7-binaries/releases/tag/nw60-arm64_2022-01-08) included an ARM64 version of `nw`.
|
||||
@ -33,7 +33,7 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
<th><a href="#complete-example">Linux</a></th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
@ -54,21 +54,6 @@ platform provides many native features out of the box.
|
||||
|
||||
:::
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Wails | Date |
|
||||
|:---------------|:-------------|:----------|:-----------|
|
||||
| macOS 15.3.2 | `darwin-x64` | `v2.10.1` | 2025-03-31 |
|
||||
| macOS 14.5 | `darwin-arm` | `v2.10.1` | 2025-03-30 |
|
||||
| Windows 11 | `win11-x64` | `v2.10.1` | 2025-05-27 |
|
||||
| Windows 11 | `win11-arm` | `v2.10` | 2025-02-23 |
|
||||
| Linux (HoloOS) | `linux-x64` | `v2.10.2` | 2025-07-06 |
|
||||
| Linux (Debian) | `linux-arm` | `v2.10` | 2025-02-16 |
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
@ -306,10 +291,24 @@ async function exportFile(table_element) {
|
||||
|
||||
## Complete Example
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Wails | Date |
|
||||
|:---------------|:-------------|:---------|:-----------|
|
||||
| macOS 14.4 | `darwin-x64` | `v2.8.0` | 2024-03-15 |
|
||||
| macOS 14.1.2 | `darwin-arm` | `v2.6.0` | 2023-12-01 |
|
||||
| Windows 10 | `win10-x64` | `v2.8.0` | 2024-03-24 |
|
||||
| Windows 11 | `win11-arm` | `v2.6.0` | 2023-12-01 |
|
||||
| Linux (HoloOS) | `linux-x64` | `v2.8.0` | 2024-03-21 |
|
||||
| Linux (Debian) | `linux-arm` | `v2.6.0` | 2023-12-01 |
|
||||
|
||||
:::
|
||||
|
||||
0) Read the Wails "Getting Started" guide[^14] and install dependencies.
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
<details><summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
Wails will require:
|
||||
|
||||
@ -334,7 +333,7 @@ On macOS and Linux, the `PATH` environment variable must include `~/go/bin`. If
|
||||
`wails` cannot be found, run the following command in the terminal session:
|
||||
|
||||
```bash
|
||||
export PATH="$PATH":~/go/bin
|
||||
export PATH="$PATH:~/go/bin"
|
||||
```
|
||||
|
||||
:::
|
||||
@ -358,7 +357,7 @@ None of the optional packages are required for building and running this demo.
|
||||
On the Steam Deck (HoloOS), some dependencies must be reinstalled:
|
||||
|
||||
```bash
|
||||
sudo pacman -Syu base-devel gtk3 glib2 pango harfbuzz cairo gdk-pixbuf2 atk libsoup webkit2gtk
|
||||
sudo pacman -Syu base-devel gtk3 glib2 pango harfbuzz cairo gdk-pixbuf2 atk libsoup
|
||||
```
|
||||
|
||||
:::
|
||||
@ -400,15 +399,11 @@ wails build
|
||||
|
||||
It will print the path to the generated program (typically in `build/bin/`).
|
||||
|
||||
5) Run the generated application:
|
||||
|
||||
| Architecture | Command |
|
||||
|:-------------|:------------------------------------------|
|
||||
| `win11-x64` | `.\build\bin\sheetjs-wails.exe` |
|
||||
5) Run the generated application.
|
||||
|
||||
**Testing**
|
||||
|
||||
The program will download [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) and
|
||||
The program will download [`pres.xlsx`](https://sheetjs.com/pres.xlsx) and
|
||||
display the contents of the first worksheet in a table.
|
||||
|
||||
To test export features, click "Export XLSX". The app will ask for a file name
|
||||
|
||||
@ -9,16 +9,13 @@ sidebar_custom_props:
|
||||
summary: Webview + Rust Backend
|
||||
---
|
||||
|
||||
# Data Wrangling in Tauri Apps
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
export const c = {style: {color:"cyan"}};
|
||||
export const y = {style: {color:"gold"}};
|
||||
export const g = {style: {color:"green"}};
|
||||
export const B = {style: {fontWeight:"bold"}};
|
||||
|
||||
[Tauri](https://tauri.app/) is a modern toolkit for building desktop apps. Tauri
|
||||
apps leverage platform-native browser engines to build lightweight programs.
|
||||
|
||||
@ -38,7 +35,7 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
<th><a href="#complete-example">Linux</a></th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
@ -50,44 +47,21 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Tauri | Date |
|
||||
|:---------------|:-------------|:----------|:-----------|
|
||||
| macOS 15.3.2 | `darwin-x64` | `v1.6.0` | 2025-03-31 |
|
||||
| macOS 14.5 | `darwin-arm` | `v1.6.0` | 2025-03-30 |
|
||||
| Windows 11 | `win11-x64` | `v1.6.0` | 2025-05-27 |
|
||||
| Windows 11 | `win11-arm` | `v1.6.0` | 2025-02-23 |
|
||||
| Linux (HoloOS) | `linux-x64` | `v1.6.0` | 2025-07-06 |
|
||||
| Linux (Debian) | `linux-arm` | `v1.6.0` | 2025-05-27 |
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
installed and imported from JavaScript code.
|
||||
|
||||
:::info pass
|
||||
|
||||
The following explanation applies to Tauri 1.
|
||||
|
||||
The `allowlist` security model was abandoned in Tauri 2.
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
Tauri 1.x does not provide the equivalent of NodeJS `fs` module. The raw
|
||||
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.
|
||||
|
||||
:::
|
||||
|
||||
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 configuration file.
|
||||
the `tauri` section of the config.
|
||||
|
||||
- The `fs` entitlement[^2] enables reading and writing file data.
|
||||
|
||||
@ -236,7 +210,7 @@ function SheetJSImportKaioponent() {
|
||||
<table><tbody>{data.map((row) =>
|
||||
<tr>{row.map((cell) => <td>{cell}</td>)}</tr>
|
||||
)}</tbody></table>
|
||||
</> );
|
||||
</>);
|
||||
}
|
||||
```
|
||||
|
||||
@ -355,6 +329,7 @@ function SheetJSExportKaioponent() {
|
||||
|
||||
return ( <button type="button" onclick={save_callback}>Save Data</button> );
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -362,40 +337,52 @@ function SheetJSExportKaioponent() {
|
||||
|
||||
## Complete Example
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Tauri | Date |
|
||||
|:---------------|:-------------|:----------|:-----------|
|
||||
| macOS 14.4 | `darwin-x64` | `v1.5.11` | 2024-03-15 |
|
||||
| macOS 14.0 | `darwin-arm` | `v1.5.2` | 2023-10-18 |
|
||||
| Windows 10 | `win10-x64` | `v1.5.11` | 2024-03-24 |
|
||||
| Windows 11 | `win11-arm` | `v1.5.7` | 2023-12-01 |
|
||||
| Linux (HoloOS) | `linux-x64` | `v1.5.11` | 2024-03-21 |
|
||||
| Linux (Debian) | `linux-arm` | `v1.5.7` | 2023-12-01 |
|
||||
|
||||
:::
|
||||
|
||||
0) Read Tauri "Getting Started" guide and install prerequisites.[^16]
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
<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`)
|
||||
- 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@1 info
|
||||
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:
|
||||
|
||||
<pre>
|
||||
<span {...g}>[✔]</span> <span style={{...y.style,...B.style}}>Environment</span>
|
||||
{` `}<span {...c}>-</span> <span {...B}>OS</span>: Mac OS 14.5.0 arm64 (X64)
|
||||
{` `}<span {...g}>✔</span> <span {...B}>Xcode Command Line Tools</span>: installed
|
||||
{` `}<span {...g}>✔</span> <span {...B}>rustc</span>: 1.87.0 (17067e9ac 2025-05-09)
|
||||
{` `}<span {...g}>✔</span> <span {...B}>cargo</span>: 1.87.0 (99624be96 2025-05-06)
|
||||
{` `}<span {...g}>✔</span> <span {...B}>rustup</span>: 1.28.2 (e4f3ad6f8 2025-04-28)
|
||||
{` `}<span {...g}>✔</span> <span {...B}>Rust toolchain</span>: stable-aarch64-apple-darwin (default)
|
||||
{` `}<span {...c}>-</span> <span {...B}>node</span>: 20.18.0
|
||||
{` `}<span {...c}>-</span> <span {...B}>pnpm</span>: 9.12.3
|
||||
{` `}<span {...c}>-</span> <span {...B}>npm</span>: 10.8.2
|
||||
{` `}<span {...c}>-</span> <span {...B}>bun</span>: 1.2.14
|
||||
{` `}<span {...c}>-</span> <span {...B}>deno</span>: deno 2.3.3
|
||||
</pre>
|
||||
```
|
||||
[✔] Environment
|
||||
- OS: Mac OS 14.4.0 X64
|
||||
✔ Xcode Command Line Tools: installed
|
||||
✔ rustc: 1.76.0 (07dca489a 2024-02-04)
|
||||
✔ cargo: 1.76.0 (c84b36747 2024-01-18)
|
||||
✔ rustup: 1.27.0 (bbb9276d2 2024-03-08)
|
||||
✔ Rust toolchain: stable-x86_64-apple-darwin (default)
|
||||
- node: 20.11.1
|
||||
- npm: 10.2.4
|
||||
- bun: 1.0.31
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
@ -412,7 +399,7 @@ build step will correctly detect the platform architecture.
|
||||
<TabItem value="vuejs" label="VueJS">
|
||||
|
||||
```bash
|
||||
npm create tauri-app@3.x -- -m npm -t vue-ts SheetJSTauri -y
|
||||
npm create tauri-app@latest -- -m npm -t vue-ts SheetJSTauri -y
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -426,7 +413,7 @@ TypeScript template and manually wires Kaioken
|
||||
:::
|
||||
|
||||
```bash
|
||||
npm create tauri-app@3.x -- -m npm -t vanilla-ts SheetJSTauri -y
|
||||
npm create tauri-app@latest -- -m npm -t vanilla-ts SheetJSTauri -y
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -447,8 +434,6 @@ npm i --save-dev @tauri-apps/cli`}
|
||||
</TabItem>
|
||||
<TabItem value="kaioken" label="Kaioken" default>
|
||||
|
||||
Install the Kaioken dependencies:
|
||||
|
||||
```bash
|
||||
npm add kaioken --save
|
||||
npm add vite-plugin-kaioken -D --save
|
||||
@ -478,23 +463,14 @@ npm add vite-plugin-kaioken -D --save
|
||||
// highlight-end
|
||||
```
|
||||
|
||||
In the same file, look for `"title"` and change the value to `SheetJS x Tauri`:
|
||||
In the same file, look for the `"identifier"` key and replace the value with `com.sheetjs.tauri`:
|
||||
|
||||
```json title="src-tauri/tauri.conf.json (edit highlighted line)"
|
||||
"windows": [
|
||||
{
|
||||
// highlight-next-line
|
||||
"title": "SheetJS x Tauri",
|
||||
"width": 800,
|
||||
```
|
||||
|
||||
In the same file, look for `"identifier"` and change the value to `com.sheetjs.tauri`:
|
||||
|
||||
```json title="src-tauri/tauri.conf.json (edit highlighted line)"
|
||||
"targets": "all",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
// highlight-next-line
|
||||
"identifier": "com.sheetjs.tauri",
|
||||
"icon": [
|
||||
"longDescription": "",
|
||||
```
|
||||
|
||||
<Tabs groupId="framework">
|
||||
@ -517,27 +493,36 @@ curl -o src/App.vue https://docs.sheetjs.com/tauri/App.vue
|
||||
```ts title="vite.config.ts (add highlighted lines)"
|
||||
import { defineConfig } from "vite";
|
||||
// highlight-next-line
|
||||
import kaioken from "vite-plugin-kaioken";
|
||||
import kaioken from "vite-plugin-kaioken"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
// highlight-start
|
||||
esbuild: {
|
||||
jsxInject: `import * as kaioken from "kaioken"`,
|
||||
jsx: "transform",
|
||||
jsxFactory: "kaioken.createElement",
|
||||
jsxFragment: "kaioken.fragment",
|
||||
loader: "tsx",
|
||||
include: ["**/*.tsx", "**/*.ts", "**/*.jsx", "**/*.js"],
|
||||
},
|
||||
plugins: [kaioken()],
|
||||
// highlight-end
|
||||
```
|
||||
|
||||
- Edit `tsconfig.json`. In `compilerOptions` add the option `"jsx": "preserve"`:
|
||||
- Add the highlighted line to `tsconfig.json`:
|
||||
|
||||
```js title="tsconfig.json (add highlighted line)"
|
||||
{
|
||||
"compilerOptions": {
|
||||
// highlight-next-line
|
||||
"jsx": "preserve",
|
||||
"target": "ES2020",
|
||||
```
|
||||
|
||||
- Replace `index.html` with the following codeblock:
|
||||
|
||||
```html title="index.html (replace contents)"
|
||||
```html title="index.html"
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -574,7 +559,7 @@ table.center {
|
||||
|
||||
- Replace `src/main.ts` with the following codeblock:
|
||||
|
||||
```ts title="src/main.ts (replace contents)"
|
||||
```ts title="src/main.ts"
|
||||
import { mount } from "kaioken";
|
||||
import App from "./App";
|
||||
|
||||
@ -613,7 +598,7 @@ If the build fails, see ["Troubleshooting"](#troubleshooting) for more details.
|
||||
Depending on the version of Tauri, the command may be
|
||||
|
||||
```bash
|
||||
./src-tauri/target/release/sheetjstauri
|
||||
./src-tauri/target/release/SheetJSTauri
|
||||
```
|
||||
|
||||
or
|
||||
@ -625,7 +610,7 @@ or
|
||||
or
|
||||
|
||||
```bash
|
||||
./src-tauri/target/release/SheetJSTauri
|
||||
./src-tauri/target/release/sheetjstauri
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -640,11 +625,10 @@ or
|
||||
|
||||
The following features should be manually verified:
|
||||
|
||||
- When it is loaded, the app will download https://docs.sheetjs.com/pres.numbers
|
||||
- When it is loaded, the app will download <https://sheetjs.com/pres.numbers>
|
||||
and display the data in a table.
|
||||
- Clicking "Save Data" will show a save dialog. If there is no filename, type
|
||||
`SheetJSTauri.xlsb`. Click "Save". The app will write a file which can be
|
||||
opened in a spreadsheet editor.
|
||||
- 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.
|
||||
|
||||
@ -652,22 +636,6 @@ The following features should be manually verified:
|
||||
|
||||
:::note pass
|
||||
|
||||
In some tests, the build failed with the error message:
|
||||
|
||||
```
|
||||
Search string not found: "/supportedTSExtensions = .*(?=;)/"
|
||||
```
|
||||
|
||||
This is a known issue with `vue-tsc`. The dependency must be upgraded:
|
||||
|
||||
```bash
|
||||
npm install vue-tsc@latest
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
During the last Linux ARM64 test, the build failed to create an AppImage:
|
||||
|
||||
```
|
||||
@ -697,7 +665,7 @@ sudo pacman -S openssl
|
||||
|
||||
:::note pass
|
||||
|
||||
In some macOS tests, the build failed with the following error message:
|
||||
During the last macOS test, the build failed with the following error message:
|
||||
|
||||
```
|
||||
Error failed to bundle project: error running bundle_dmg.sh
|
||||
@ -722,14 +690,6 @@ select "Automation" in the body. Look for "Terminal", expand the section, and en
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
In some tests, the fonts did not match the screenshots.
|
||||
|
||||
**The Inter font static TTFs must be manually downloaded and installed.**[^17]
|
||||
|
||||
:::
|
||||
|
||||
[^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
|
||||
@ -745,5 +705,4 @@ In some tests, the fonts did not match the screenshots.
|
||||
[^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
|
||||
[^17]: Click "Get font" in the [Inter Google Fonts listing](https://fonts.google.com/specimen/Inter)
|
||||
[^16]: See ["Prerequisites"](https://tauri.app/v1/guides/getting-started/prerequisites) in the Tauri documentation
|
||||
@ -33,7 +33,7 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
<th><a href="#complete-example">Linux</a></th>
|
||||
</tr></thead><tbody><tr><td>
|
||||
|
||||

|
||||

|
||||
|
||||
</td><td>
|
||||
|
||||
@ -45,24 +45,6 @@ app to read and write workbooks. The app will look like the screenshots below:
|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Server | Client | Date |
|
||||
|:---------------|:-------------|:---------|:---------|:-----------|
|
||||
| macOS 15.3.2 | `darwin-x64` | `6.0.0` | `6.0.0` | 2025-03-31 |
|
||||
| macOS 14.5 | `darwin-arm` | `6.0.0` | `6.0.0` | 2025-03-30 |
|
||||
| Windows 11 | `win11-x64` | `6.1.0` | `6.1.0` | 2025-05-27 |
|
||||
| Windows 11 | `win11-arm` | `5.6.0` | `5.6.0` | 2025-02-23 |
|
||||
| Linux (HoloOS) | `linux-x64` | `6.1.0` | `6.1.0` | 2025-07-06 |
|
||||
| Linux (Debian) | `linux-arm` | `5.6.0` | `5.6.0` | 2025-02-16 |
|
||||
|
||||
NeutralinoJS on Windows on ARM generates X64 binaries that run using the X64
|
||||
compatibility layer. The binaries are not native ARM64 programs!
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
@ -204,14 +186,28 @@ const save_button_callback = async() => {
|
||||
|
||||
## Complete Example
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Architecture | Server | Client | Date |
|
||||
|:---------------|:-------------|:---------|:---------|:-----------|
|
||||
| macOS 14.4 | `darwin-x64` | `5.0.0` | `5.0.1` | 2024-03-15 |
|
||||
| macOS 14.0 | `darwin-arm` | `4.14.1` | `3.12.0` | 2023-10-18 |
|
||||
| Windows 10 | `win10-x64` | `5.1.0` | `5.1.0` | 2024-03-24 |
|
||||
| Windows 11 | `win11-arm` | `4.14.1` | `3.12.0` | 2023-12-01 |
|
||||
| Linux (HoloOS) | `linux-x64` | `5.0.0` | `5.0.1` | 2024-03-21 |
|
||||
| Linux (Debian) | `linux-arm` | `4.14.1` | `3.12.0` | 2023-12-01 |
|
||||
|
||||
:::
|
||||
|
||||
The app core state will be the HTML table. Reading files will add the table to
|
||||
the window. Writing files will parse the table into a spreadsheet.
|
||||
|
||||
<details>
|
||||
<summary><b>Installation Notes</b> (click to show)</summary>
|
||||
<details><summary><b>Installation Notes</b> (click to show)</summary>
|
||||
|
||||
NeutralinoJS uses `portable-file-dialogs`[^12] to show open and save dialogs. On
|
||||
Linux, a dialog box helper (Zenity or KDialog) must be installed.
|
||||
Linux, Zenity or KDialog are require.
|
||||
|
||||
The last Debian test was run on a system using LXDE. KDialog is supported but
|
||||
must be explicitly installed:
|
||||
@ -247,22 +243,6 @@ subdirectory in the `sheetjs-neu` folder:
|
||||
curl -L -o resources/js/xlsx.full.min.js https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
|
||||
</CodeBlock>
|
||||
|
||||
:::note pass
|
||||
|
||||
In PowerShell, the command may fail with a parameter error:
|
||||
|
||||
```
|
||||
Invoke-WebRequest : A parameter cannot be found that matches parameter name 'L'.
|
||||
```
|
||||
|
||||
`curl.exe` must be invoked directly:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl.exe -L -o resources/js/xlsx.full.min.js https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
3) Add the highlighted line to `neutralino.config.json` in `nativeAllowList`:
|
||||
|
||||
```json title="neutralino.config.json (add highlighted line)"
|
||||
@ -322,7 +302,7 @@ table {
|
||||
|
||||
6) Print the version number in the `showInfo` method of `resources/js/main.js`:
|
||||
|
||||
```js title="resources/js/main.js (add highlighted lines)"
|
||||
```js title="resources/js/main.js"
|
||||
function showInfo() {
|
||||
document.getElementById('info').innerHTML = `
|
||||
${NL_APPID} is running on port ${NL_PORT} inside ${NL_OS}
|
||||
@ -348,18 +328,14 @@ npx @neutralinojs/neu run
|
||||
|
||||
```js title="resources/js/main.js (add to end)"
|
||||
(async() => {
|
||||
const ab = await (await fetch("https://docs.sheetjs.com/pres.numbers")).arrayBuffer();
|
||||
const ab = await (await fetch("https://sheetjs.com/pres.numbers")).arrayBuffer();
|
||||
const wb = XLSX.read(ab);
|
||||
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||
document.getElementById('info').innerHTML = XLSX.utils.sheet_to_html(ws);
|
||||
})();
|
||||
```
|
||||
|
||||
9) Close the app. Run the app again:
|
||||
|
||||
```bash
|
||||
npx @neutralinojs/neu run
|
||||
```
|
||||
9) Close the app and relaunch the app with `npx @neutralinojs/neu run`
|
||||
|
||||
When the app loads, a table should show in the main screen.
|
||||
|
||||
@ -394,11 +370,7 @@ async function exportData() {
|
||||
}
|
||||
```
|
||||
|
||||
11) Close the app. Run the app again:
|
||||
|
||||
```bash
|
||||
npx @neutralinojs/neu run
|
||||
```
|
||||
11) Close the app and re-run with `npx @neutralinojs/neu run`
|
||||
|
||||
When the app loads, click the "Import File" button and select a spreadsheet to
|
||||
see the contents.
|
||||
@ -425,18 +397,8 @@ save as `SheetJSNeu` will not automatically add the `.xlsx` extension!
|
||||
npx @neutralinojs/neu build
|
||||
```
|
||||
|
||||
Platform-specific programs will be created in the `dist` folder:
|
||||
|
||||
| Platform | Path to binary |
|
||||
|:-------------|:---------------------------------------------|
|
||||
| `darwin-x64` | `./dist/sheetjs-neu/sheetjs-neu-mac_x64` |
|
||||
| `darwin-arm` | `./dist/sheetjs-neu/sheetjs-neu-mac_arm64` |
|
||||
| `win11-x64` | `.\dist\sheetjs-neu\sheetjs-neu-win_x64.exe` |
|
||||
| `win11-arm` | `.\dist\sheetjs-neu\sheetjs-neu-win_x64.exe` |
|
||||
| `linux-x64` | `./dist/sheetjs-neu/sheetjs-neu-linux_x64` |
|
||||
| `linux-arm` | `./dist/sheetjs-neu/sheetjs-neu-linux_arm64` |
|
||||
|
||||
Run the generated app and confirm that Presidential data is displayed.
|
||||
Platform-specific programs will be created in the `dist` folder. For example,
|
||||
the `darwin-arm` program will be `./dist/sheetjs-neu/sheetjs-neu-mac_arm64`
|
||||
|
||||
[^1]: See [`nativeAllowList`](https://neutralino.js.org/docs/configuration/neutralino.config.json#nativeallowlist-string) in the NeutralinoJS documentation
|
||||
[^2]: See [`os.showOpenDialog`](https://neutralino.js.org/docs/api/os#osshowopendialogtitle-options) in the NeutralinoJS documentation
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user