This commit is contained in:
SheetJS 2023-04-01 16:13:16 -04:00
parent af187640fa
commit 4f548c4646
6 changed files with 236 additions and 67 deletions

@ -7,15 +7,23 @@ sidebar_custom_props:
summary: JS + Web View
---
## CapacitorJS
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
from the main entrypoint or any script in the project.
:::note
The "Complete Example" creates an app that looks like the screenshots below:
This demo was tested on an Intel Mac on 2022 August 26 with Svelte.
<table><thead><tr>
<th><a href="#demo">iOS</a></th>
<th><a href="#demo">Android</a></th>
</tr></thead><tbody><tr><td>
The iOS simulator runs iOS 15.5 on an iPhone 13 Pro Max.
![iOS screenshot](pathname:///cap/ios.png)
:::
</td><td>
![Android screenshot](pathname:///cap/and.png)
</td></tr></tbody></table>
:::warning Telemetry
@ -33,7 +41,7 @@ npx @capacitor/cli telemetry
:::
### Integration Details
## Integration Details
This example uses Svelte, but the same principles apply to other frameworks.
@ -100,93 +108,150 @@ async function exportFile() {
</main>
```
### Demo
## Demo
<details><summary><b>Complete Example</b> (click to show)</summary>
:::note
0) Disable telemetry as noted in the warning.
This demo was tested on an Intel Mac on 2023 April 01 with Capacitor 4.7.3 and
`@capacitor/filesystem` 4.1.4
Follow the [React Native demo](#demo) to ensure iOS and Android sims are ready.
The iOS simulator runs iOS 16.2 on an iPhone 14 Pro Max.
The Android simulator runs Android 12.0 (S) API 31 on a Pixel 3.
1) Create a new Svelte project:
:::
### Base Project
1) Disable telemetry.
```bash
npx @capacitor/cli telemetry off
```
Verify that telemetry is disabled by running
```bash
npx @capacitor/cli telemetry
```
(it should print `Telemetry is off`)
2) Create a new Svelte project:
```bash
npm create vite@latest sheetjs-cap -- --template svelte
cd sheetjs-cap
```
2) Install dependencies:
3) Install dependencies:
```bash
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
npm i --save @capacitor/core @capacitor/cli @capacitor/ios @capacitor/filesystem
npm i --save @capacitor/core @capacitor/cli @capacitor/filesystem
```
3) Create CapacitorJS structure:
4) Create CapacitorJS structure:
```bash
npx cap init sheetjs-cap com.sheetjs.cap --web-dir=dist
npm run build
```
5) Download [`src/App.svelte`](pathname:///cap/App.svelte) and replace:
```bash
curl -o src/App.svelte -L https://docs.sheetjs.com/cap/App.svelte
```
### iOS
6) Follow the [React Native demo](/docs/demos/mobile/reactnative#demo) to
ensure the iOS simulator is ready.
7) Create iOS app
```bash
npm i --save @capacitor/ios
npx cap add ios
```
4) Replace the contents of `src/App.svelte` with the following:
8) 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`:
```html title="src/App.svelte"
<script>
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
import { onMount } from 'svelte';
import { read, utils, version, writeXLSX } from 'xlsx';
let html = "";
let tbl;
/* Fetch and update the state once */
onMount(async() => {
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
html = utils.sheet_to_html(ws); // generate HTML and update state
});
/* get state data and export to XLSX */
async function exportFile() {
const elt = tbl.getElementsByTagName("TABLE")[0];
const wb = utils.table_to_book(elt);
/* generate Base64 string for Capacitor */
const data = writeXLSX(wb, { type: "base64" });
/* write */
await Filesystem.writeFile({
path: "SheetJSCap.xlsx",
data,
directory: Directory.Documents
});
}
/* show file picker, read file, load table */
async function importFile(evt) {
const f = evt.target.files[0];
const wb = read(await f.arrayBuffer());
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
html = utils.sheet_to_html(ws); // generate HTML and update state
}
</script>
<main>
<h3>SheetJS × CapacitorJS { version }</h3>
<input type="file" on:change={importFile}/>
<button on:click={exportFile}>Export XLSX</button>
<div bind:this={tbl}>{@html html}</div>
</main>
```xml title="ios/App/App/Info.plist"
<plist version="1.0">
<dict>
<!-- highlight-start -->
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<!-- highlight-end -->
<key>CFBundleDevelopmentRegion</key>
```
5) Test the app:
(The root element of the document is `plist` and it contains one `dict` child)
9) Run the app in the simulator
```bash
npm run build; npx cap sync; npx cap run ios
npm run build
npx cap sync
npx cap run ios
```
There are 3 steps: build the Svelte app, sync with CapacitorJS, and run sim.
This sequence must be run every time to ensure changes are propagated.
10) Test the app
</details>
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`"
### Android
11) Follow the [React Native demo](/docs/demos/mobile/reactnative#demo) to
ensure the Android simulator is ready.
12) Create Android app
```bash
npm i --save @capacitor/android
npx cap add android
```
13) Enable file reading and writing in the Android app.
The following lines must be added to `android/app/src/main/AndroidManifest.xml`
after the `Permissions` comment:
```xml title="android/app/src/main/AndroidManifest.xml"
<!-- Permissions -->
<!-- highlight-start -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- highlight-end -->
<uses-permission android:name="android.permission.INTERNET" />
```
14) Run the app in the simulator
```bash
npm run build
npx cap sync
npx cap run android
```
15) Test the app
Open the app and observe that presidents are listed in the table.
Touch "Export XLSX" and the emulator will ask for permission:
Tap "Allow" and a popup will be displayed with a path.
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`.

@ -59,7 +59,7 @@ Writers will process the `Props` key of the options object:
```js
/* force the Author to be "SheetJS" */
XLSX.write(wb, {Props:{Author:"SheetJS"}});
XLSX.write(wb, { Props: { Author: "SheetJS" } });
```
## Workbook-Level Attributes
@ -92,6 +92,63 @@ Excel allows two sheet-scoped defined names to share the same name. However, a
sheet-scoped name cannot collide with a workbook-scope name. Workbook writers
may not enforce this constraint.
The following snippet creates a worksheet-level defined name `"Global"` and a
local defined name `"Local"` with distinct values for first and second sheets:
```js
/* ensure the workbook structure exists */
if(!wb.Workbook) wb.Workbook = {};
if(!wb.Workbook.Names) wb.Workbook.Names = [];
/* "Global" workbook-level -> Sheet1 A1:A2 */
wb.Workbook.Names.push({ Name: "Global", Ref: "Sheet1!$A$1:$A$2" });
/* "Local" scoped to the first worksheet -> Sheet1 B1:B2 */
wb.Workbook.Names.push({ Name: "Local", Ref: "Sheet1!$B$1:$B$2", Sheet: 0 });
/* "Local" scoped to the second worksheet -> Sheet1 C1:C2 */
wb.Workbook.Names.push({ Name: "Local", Ref: "Sheet1!$C$1:$C$2", Sheet: 1 });
```
<details><summary><b>Live Example</b> (click to show)</summary>
```jsx live
/* The live editor requires this function wrapper */
function DefinedNameExport() { return ( <button onClick={() => {
/* Create empty workbook */
var wb = XLSX.utils.book_new();
/* Create worksheet Sheet1 */
var ws1 = XLSX.utils.aoa_to_sheet([[1,2,3],[4,5,6],["Global",0],["Local",0]]);
XLSX.utils.book_append_sheet(wb, ws1, "Sheet1");
/* Create worksheet Sheet2 */
var ws2 = XLSX.utils.aoa_to_sheet([["Global",0],["Local",0]]);
XLSX.utils.book_append_sheet(wb, ws2, "Sheet2");
/* Create defined names */
if(!wb.Workbook) wb.Workbook = {};
if(!wb.Workbook.Names) wb.Workbook.Names = [];
/* "Global" workbook-level -> Sheet1 A1:A2 */
wb.Workbook.Names.push({ Name: "Global", Ref: "Sheet1!$A$1:$A$2" });
/* "Local" scoped to the first worksheet -> Sheet1 B1:B2 */
wb.Workbook.Names.push({ Name: "Local", Sheet: 0, Ref: "Sheet1!$B$1:$B$2" });
/* "Local" scoped to the second worksheet -> Sheet1 C1:C2 */
wb.Workbook.Names.push({ Name: "Local", Sheet: 1, Ref: "Sheet1!$C$1:$C$2" });
/* Create formulae */
ws1["B3"].f = "SUM(Global)"; // Sheet1 B3 =SUM(Global) 1 + 4 = 5
ws1["B4"].f = "SUM(Local)"; // Sheet1 B4 =SUM(Local) 2 + 5 = 7
ws2["B1"].f = "SUM(Global)"; // Sheet2 B1 =SUM(Global) 1 + 4 = 5
ws2["B2"].f = "SUM(Local)"; // Sheet2 B2 =SUM(Local) 3 + 6 = 9
/* Export to file (start a download) */
XLSX.writeFile(wb, "SheetJSDNExport.xlsx");
}}><b>Export XLSX!</b></button> ); }
```
</details>
### Workbook Views
`wb.Workbook.Views` is an array of workbook view objects which have the keys:

@ -1,4 +1,5 @@
{
"label": "Spreadsheet Features",
"collapsed": false,
"position": 7
}

@ -0,0 +1,46 @@
<script>
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
import { onMount } from 'svelte';
import { read, utils, version, writeXLSX } from 'xlsx';
let html = "";
let tbl;
/* Fetch and update the state once */
onMount(async() => {
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
html = utils.sheet_to_html(ws); // generate HTML and update state
});
/* get state data and export to XLSX */
async function exportFile() {
const elt = tbl.getElementsByTagName("TABLE")[0];
const wb = utils.table_to_book(elt);
/* generate Base64 string for Capacitor */
const data = writeXLSX(wb, { type: "base64" });
/* write */
await Filesystem.writeFile({
path: "SheetJSCap.xlsx",
data,
directory: Directory.Documents
});
alert(`Exported to ${Directory.Documents}/SheetJSCap.xlsx`);
}
/* show file picker, read file, load table */
async function importFile(evt) {
const f = evt.target.files[0];
const wb = read(await f.arrayBuffer());
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
html = utils.sheet_to_html(ws); // generate HTML and update state
}
</script>
<main>
<h3>SheetJS × CapacitorJS { version }</h3>
<input type="file" on:change={importFile}/>
<button on:click={exportFile}>Export XLSX</button>
<div bind:this={tbl}>{@html html}</div>
</main>

BIN
docz/static/cap/and.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
docz/static/cap/ios.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB