This commit is contained in:
SheetJS 2022-08-14 23:07:34 -04:00
parent 847a1c850a
commit c2433e1c83
14 changed files with 719 additions and 137 deletions

@ -524,7 +524,7 @@ async function openFile() {
There are two steps to writing files: obtaining a path and writing binary data:
```js
import { read } from 'xlsx';
import { write } from 'xlsx';
import { save } from '@tauri-apps/api/dialog';
import { writeBinaryFile } from '@tauri-apps/api/fs';

@ -3,6 +3,9 @@ sidebar_position: 19
title: iOS and Android Apps
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
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
@ -36,6 +39,629 @@ provide, usually there are third-party modules to provide needed functionality.
:::
macOS is required for the iOS demos. The Android demos were tested on macOS.
## React Native
:::note
This demo was tested on an Intel Mac on 2022 August 14 with RN `0.67.2`.
The iOS simulator runs iOS 15.5 on an iPhone 13.
The Android simulator runs Android 12 (S) Platform 31 on a Pixel 5.
:::
:::warning
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.
Since React Native internals change between releases, libraries may only work
with specific versions of React Native. Project documentation should be
consulted before picking a library.
:::
The following table lists tested file plugins. "OS" lists tested platforms
("A" for Android and "I" for iOS). "Copy" indicates whether an explicit copy
is needed (file picker copies to cache directory and file plugin reads cache).
| Filesystem Plugin | File Picker Plugin | OS | Copy |
|:---------------------------|:-------------------------------|:----:|:-----|
| `react-native-file-access` | `react-native-document-picker` | `AI` | |
| `react-native-blob-util` | `react-native-document-picker` | `AI` | YES |
| `rn-fetch-blob` | `react-native-document-picker` | `AI` | YES |
| `react-native-fs` | `react-native-document-picker` | `AI` | YES |
| `expo-file-system` | `expo-document-picker` | ` I` | YES |
### RN File Picker
The following libraries have been tested:
#### `react-native-document-picker`
<details open><summary><b>Selecting a file</b> (click to show)</summary>
When a copy is not needed:
```js
import { pickSingle } from 'react-native-document-picker';
const f = await pickSingle({allowMultiSelection: false, mode: "open" });
const path = f.uri; // this path can be read by RN file plugins
```
When a copy is needed:
```js
import { pickSingle } from 'react-native-document-picker';
const f = await pickSingle({allowMultiSelection: false, copyTo: "cachesDirectory", mode: "open" });
const path = f.fileCopyUri; // this path can be read by RN file plugins
```
</details>
#### `expo-document-picker`
<details><summary><b>Selecting a file</b> (click to show)</summary>
When using `DocumentPicker.getDocumentAsync`, enable `copyToCacheDirectory`:
```js
import * as DocumentPicker from 'expo-document-picker';
const result = await DocumentPicker.getDocumentAsync({
// highlight-next-line
copyToCacheDirectory: true,
type: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
});
const path = result.uri;
```
</details>
### RN File Plugins
The following libraries have been tested:
#### `react-native-blob-util` and `rn-fetch-blob`
:::note Historical Context
The `react-native-fetch-blob` project was archived in 2019. At the time, there
were a number of project forks. The maintainers blessed the `rn-fetch-blob`
fork as the spiritual successor.
`react-native-blob-util` is an active fork of `rn-fetch-blob`
On the day that this demo was tested (2022 August 14), both `rn-fetch-blob` and
`react-native-blob-util` worked with the tested iOS and Android SDK versions.
The APIs are identical for the purposes of working with files.
:::
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 open><summary><b>Reading and Writing snippets</b> (click to show)</summary>
The snippets use `rn-fetch-blob`. To use `react-native-blob-util`, change the
`import` statements to load the module.
_Reading Data_
```js
import * as XLSX from "xlsx";
import RNFetchBlob from 'rn-fetch-blob'; // or react-native-blob-util
const { readFile } = RNFetchBlob.fs;
const res = await readFile(path, 'ascii');
const wb = XLSX.read(new Uint8Array(res), {type:'buffer'});
```
:::caution
On iOS, URIs from `react-native-document-picker` must be massaged:
```js
import { pickSingle } from 'react-native-document-picker';
import RNFetchBlob from 'rn-fetch-blob'; // or react-native-blob-util
const { readFile, dirs: { DocumentDir } } = RNFetchBlob.fs;
const f = await pickSingle({
// highlight-start
// Instruct the document picker to copy file to Documents directory
copyTo: "documentDirectory",
// highlight-end
allowMultiSelection: false, mode: "open" });
// highlight-start
// `f.uri` is the original path and `f.fileCopyUri` is the path to the copy
let path = f.fileCopyUri;
// iOS workaround
if (Platform.OS === 'ios') path = path.replace(/^.*\/Documents\//, DDP + "/");
// highlight-end
const res = await readFile(path, 'ascii');
```
:::
_Writing Data_
```js
import * as XLSX from "xlsx";
import RNFetchBlob from 'rn-fetch-blob'; // or react-native-blob-util
const { writeFile, readFile, dirs:{ DocumentDir } } = RNFetchBlob.fs;
const wbout = XLSX.write(wb, {type:'buffer', bookType:"xlsx"});
const file = DocumentDir + "/sheetjsw.xlsx";
const res = await writeFile(file, Array.from(wbout), 'ascii');
```
</details>
#### `react-native-file-access`
The `base64` encoding returns strings compatible with the `base64` type:
<details open><summary><b>Reading and Writing snippets</b> (click to show)</summary>
_Reading Data_
```js
import * as XLSX from "xlsx";
import { FileSystem } from "react-native-file-access";
const b64 = await FileSystem.readFile(path, "base64");
/* b64 is a base64 string */
const workbook = XLSX.read(b64, {type: "base64"});
```
_Writing Data_
```js
import * as XLSX from "xlsx";
import { Dirs, FileSystem } from "react-native-file-access";
const DDP = Dirs.DocumentDir + "/";
const b64 = XLSX.write(workbook, {type:'base64', bookType:"xlsx"});
/* b64 is a base64 string */
await FileSystem.writeFile(DDP + "sheetjs.xlsx", b64, "base64");
```
</details>
#### `react-native-fs`
The `ascii` encoding returns binary strings compatible with the `binary` type:
<details open><summary><b>Reading and Writing snippets</b> (click to show)</summary>
_Reading Data_
```js
import * as XLSX from "xlsx";
import { readFile } from "react-native-fs";
const bstr = await readFile(path, "ascii");
/* bstr is a binary string */
const workbook = XLSX.read(bstr, {type: "binary"});
```
_Writing Data_
```js
import * as XLSX from "xlsx";
import { writeFile, DocumentDirectoryPath } from "react-native-fs";
const bstr = XLSX.write(workbook, {type:'binary', bookType:"xlsx"});
/* bstr is a binary string */
await writeFile(DocumentDirectoryPath + "/sheetjs.xlsx", bstr, "ascii");
```
</details>
#### `expo-file-system`
:::caution
Some Expo APIs return URIs that cannot be read with `expo-file-system`. This
will manifest as an error:
> Unsupported scheme for location '...'
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 show)</summary>
_Reading Data_
Calling `FileSystem.readAsStringAsync` with `FileSystem.EncodingType.Base64`
encoding returns a promise resolving to a string compatible with `base64` type:
```js
import * as XLSX from "xlsx";
import * as FileSystem from 'expo-file-system';
const b64 = await FileSystem.readAsStringAsync(uri, { encoding: FileSystem.EncodingType.Base64 });
const workbook = XLSX.read(b64, { type: "base64" });
```
_Writing Data_
The `FileSystem.EncodingType.Base64` encoding accepts Base64 strings:
```js
import * as XLSX from "xlsx";
import * as FileSystem from 'expo-file-system';
const b64 = XLSX.write(workbook, {type:'base64', bookType:"xlsx"});
/* b64 is a base64 string */
await FileSystem.writeAsStringAsync(FileSystem.documentDirectory + "sheetjs.xlsx", b64, { encoding: FileSystem.EncodingType.Base64 });
```
</details>
### Demo
:::warning
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. Details like creating an Android Virtual Device
are not covered here.
:::
<details open><summary><b>Complete Example</b> (click to show)</summary>
This example tries to separate the library-specific functions.
0) **Follow the official React Native CLI Quickstart!**
Quickstart URL: <http://reactnative.dev/docs/environment-setup>
Follow the instructions for iOS and for Android. They will cover installation
and system configuration. By the end, you should be able to run the sample app
in the Android and the iOS simulators.
1) Create project:
```
npx react-native init SheetJSRN --version="0.67.2"
```
2) Install shared dependencies:
```bash
cd SheetJSRN
curl -LO http://oss.sheetjs.com/assets/img/logo.png
npm i -S https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
npm i -S react-native-table-component react-native-document-picker
```
Refresh iOS project by running `pod install` from the `ios` subfolder:
```bash
cd ios
pod install
cd ..
```
3) Download [`index.js`](pathname:///mobile/index.js) and replace:
```bash
curl -LO https://docs.sheetjs.com/mobile/index.js
```
Start the iOS emulator:
```bash
npx react-native run-ios
```
You should see the skeleton app:
![React Native iOS App](pathname:///mobile/rnios1.png)
4) Pick a filesystem library for integration:
<Tabs>
<TabItem value="RNBU" label="RNBU">
Install `react-native-blob-util` dependency:
```bash
npm i -S react-native-blob-util
```
Add the highlighted lines to `index.js`:
```js title="index.js"
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
// highlight-start
import { read, write } from 'xlsx';
import { pickSingle } from 'react-native-document-picker';
import { Platform } from 'react-native';
import RNFetchBlob from 'react-native-blob-util';
async function pickAndParse() {
/* rn-fetch-blob / react-native-blob-util need a copy */
const f = await pickSingle({allowMultiSelection: false, copyTo: "documentDirectory", mode: "open" });
let path = f.fileCopyUri;
if (Platform.OS === 'ios') path = path.replace(/^.*\/Documents\//, RNFetchBlob.fs.dirs.DocumentDir + "/");
const res = await RNFetchBlob.fs.readFile(path, 'ascii');
return read(new Uint8Array(res), {type: 'buffer'});
}
async function writeWorkbook(wb) {
const wbout = write(wb, {type:'buffer', bookType:"xlsx"});
const file = RNFetchBlob.fs.dirs.DocumentDir + "/sheetjsw.xlsx";
await RNFetchBlob.fs.writeFile(file, Array.from(wbout), 'ascii');
return file;
}
// highlight-end
const make_width = ws => {
```
</TabItem>
<TabItem value="RNFA" label="RNFA">
Install `react-native-file-access` dependency:
```bash
npm i -S react-native-file-access
```
Add the highlighted lines to `index.js`:
```js title="index.js"
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
// highlight-start
import { read, write } from 'xlsx';
import { pickSingle } from 'react-native-document-picker';
import { Dirs, FileSystem } from 'react-native-file-access';
async function pickAndParse() {
/* react-native-file-access does not need a copy */
const f = await pickSingle({allowMultiSelection: false, mode: "open" });
const res = await FileSystem.readFile(f.uri, "base64");
return read(res, {type: 'base64'});
}
async function writeWorkbook(wb) {
const wbout = write(wb, {type:'base64', bookType:"xlsx"});
const file = Dirs.DocumentDir + "/sheetjsw.xlsx";
await FileSystem.writeFile(file, wbout, "base64");
return file;
}
// highlight-end
const make_width = ws => {
```
</TabItem>
<TabItem value="RNFB" label="RNFB">
Install `rn-fetch-blob` dependency:
```bash
npm i -S rn-fetch-blob
```
Add the highlighted lines to `index.js`:
```js title="index.js"
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
// highlight-start
import { read, write } from 'xlsx';
import { pickSingle } from 'react-native-document-picker';
import { Platform } from 'react-native';
import RNFetchBlob from 'rn-fetch-blob';
async function pickAndParse() {
/* rn-fetch-blob / react-native-blob-util need a copy */
const f = await pickSingle({allowMultiSelection: false, copyTo: "documentDirectory", mode: "open" });
let path = f.fileCopyUri;
if (Platform.OS === 'ios') path = path.replace(/^.*\/Documents\//, RNFetchBlob.fs.dirs.DocumentDir + "/");
const res = await RNFetchBlob.fs.readFile(path, 'ascii');
return read(new Uint8Array(res), {type: 'buffer'});
}
async function writeWorkbook(wb) {
const wbout = write(wb, {type:'buffer', bookType:"xlsx"});
const file = RNFetchBlob.fs.dirs.DocumentDir + "/sheetjsw.xlsx";
await RNFetchBlob.fs.writeFile(file, Array.from(wbout), 'ascii');
return file;
}
// highlight-end
const make_width = ws => {
```
</TabItem>
<TabItem value="RNFS" label="RNFS">
Install `react-native-fs` dependency:
```bash
npm i -S react-native-fs
```
Add the highlighted lines to `index.js`:
```js title="index.js"
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
// highlight-start
import { read, write } from 'xlsx';
import { pickSingle } from 'react-native-document-picker';
import { writeFile, readFile, DocumentDirectoryPath } from 'react-native-fs';
async function pickAndParse() {
/* react-native-fs needs a copy */
const f = await pickSingle({allowMultiSelection: false, copyTo: "cachesDirectory", mode: "open" });
const bstr = await readFile(f.fileCopyUri, 'ascii');
return read(bstr, {type:'binary'});
}
async function writeWorkbook(wb) {
const wbout = write(wb, {type:'binary', bookType:"xlsx"});
const file = DocumentDirectoryPath + "/sheetjsw.xlsx";
await writeFile(file, wbout, 'ascii');
return file;
}
// highlight-end
const make_width = ws => {
```
</TabItem>
<TabItem value="EXPO" label="EXPO">
:::caution
At the time of testing, the `npx install-expo-modules` step breaks the Android
project. The demo works as expected on iOS.
:::
Install `expo-file-system` and `expo-document-picker` dependencies:
```bash
npx install-expo-modules
npm i -S expo-file-system expo-document-picker
```
Add the highlighted lines to `index.js`:
```js title="index.js"
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
// highlight-start
import { read, write } from 'xlsx';
import { getDocumentAsync } from 'expo-document-picker';
import { documentDirectory, readAsStringAsync, writeAsStringAsync } from 'expo-file-system';
async function pickAndParse() {
const result = await getDocumentAsync({copyToCacheDirectory: true});
const path = result.uri;
const res = await readAsStringAsync(path, { encoding: "base64" });
return read(res, {type: 'base64'});
}
async function writeWorkbook(wb) {
const wbout = write(wb, {type:'base64', bookType:"xlsx"});
const file = documentDirectory + "sheetjsw.xlsx";
await writeAsStringAsync(file, wbout, { encoding: "base64" });
return file;
}
// highlight-end
const make_width = ws => {
```
</TabItem>
</Tabs>
5) Refresh the app:
```bash
cd ios
pod install
cd ..
```
After doing this, the simulator must be stopped and the dev server must reload:
```bash
npx react-native run-ios
```
**iOS Testing**
The app can be tested with the following sequence in the simulator:
- 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.
![save file iOS](pathname:///mobile/quasar7a.png)
- Make sure "On My iPhone" is highlighted and select "Save"
- Click the Home icon again then select the SheetJSRN app
- Click "Import data" and select `pres`:
![pick file iOS](pathname:///mobile/rnios2.png)
Once selected, the screen should refresh with new contents:
![read file iOS](pathname:///mobile/rnios3.png)
- Click "Export data". You will see a popup with a location:
![write file iOS](pathname:///mobile/rnios4.png)
- Find the file and verify the contents are correct:
```bash
find ~/Library/Developer/CoreSimulator -name sheetjsw.xlsx |
while read x; do echo "$x"; npx xlsx-cli "$x"; done
```
Once testing is complete, stop the simulator and the dev process.
**Android Testing**
There are no Android-specific steps. Emulator can be started with:
```bash
npx react-native run-android
```
![React Native Android App](pathname:///mobile/rnand1.png)
The app can be tested with the following sequence in the simulator:
- Download <https://sheetjs.com/pres.numbers>
- Click and drag `pres.numbers` from a Finder window into the simulator.
- Click "Import data" and select `pres.numbers`:
![pick file Android](pathname:///mobile/rnand2.png)
Once selected, the screen should refresh with new contents:
![read file Android](pathname:///mobile/rnand3.png)
- Click "Export data". You will see a popup with a location:
![write file Android](pathname:///mobile/rnand4.png)
- Pull the file from the simulator and verify the contents:
```bash
adb exec-out run-as com.sheetjsrn cat files/sheetjsw.xlsx > /tmp/sheetjsw.xlsx
npx xlsx-cli /tmp/sheetjsw.xlsx
```
</details>
## NativeScript
:::note
@ -465,7 +1091,7 @@ window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
The demo builds off of the Vite example. Familiarity with VueJS and TypeScript
is assumed.
<details open><summary><b>Complete Example</b> (click to show)</summary>
<details><summary><b>Complete Example</b> (click to show)</summary>
0) Ensure all of the dependencies are installed. Install the CLI globally:

@ -21,7 +21,7 @@ The demo projects include small runnable examples and short explainers.
- [`Angular.JS`](./legacy#angularjs)
- [`Angular 2+ and Ionic`](https://github.com/SheetJS/SheetJS/tree/master/demos/angular2/)
- [`Knockout`](./legacy#knockout)
- [`React, React Native and NextJS`](https://github.com/SheetJS/SheetJS/tree/master/demos/react/)
- [`React and NextJS`](https://github.com/SheetJS/SheetJS/tree/master/demos/react/)
- [`VueJS`](https://github.com/SheetJS/SheetJS/tree/master/demos/vue/)
### Front-End UI Components
@ -35,13 +35,13 @@ The demo projects include small runnable examples and short explainers.
### Platforms and Integrations
- [`Command-Line Tools`](./cli)
- [`iOS / Android Mobile Applications`](./mobile)
- [`iOS and Android Mobile Applications`](./mobile)
- [`NodeJS Server-Side Processing`](https://github.com/SheetJS/SheetJS/tree/master/demos/server/)
- [`Content Management and Static Sites`](./content)
- [`Electron`](./desktop#electron)
- [`NW.js`](./desktop#nwjs)
- [`Tauri`](./desktop#tauri)
- [`Chrome / Chromium Extension`](./chromium)
- [`Chrome and Chromium Extensions`](./chromium)
- [`Google Sheets API`](./gsheet)
- [`ExtendScript for Adobe Apps`](./extendscript)
- [`NetSuite SuiteScript`](./netsuite)
@ -51,7 +51,7 @@ The demo projects include small runnable examples and short explainers.
- [`Other JavaScript Engines`](./engines)
- [`"serverless" functions`](https://github.com/SheetJS/SheetJS/tree/master/demos/function/)
- [`Databases and Structured Data Stores`](./database)
- [`NoSQL, K/V, and Unstructured Data Stores`](./nosql)
- [`NoSQL and Unstructured Data Stores`](./nosql)
- [`Legacy Internet Explorer`](./legacy#internet-explorer)
### Bundlers and Tooling

@ -96,81 +96,7 @@ shows a complete example and details the required version-specific settings.
</TabItem>
<TabItem value="reactnative" label="React Native">
:::caution
React Native does not provide a way to read files from the filesystem. A
separate third-party library must be used.
Since React Native internals change between releases, libraries may only work
with specific versions of React Native. Project documentation should be
consulted before picking a library.
:::caution
The [`react` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/react) includes a sample React Native app.
The following libraries have been tested:
- [`react-native-file-access`](https://npm.im/react-native-file-access)
The `base64` encoding returns strings compatible with the `base64` type:
```js
import * as XLSX from "xlsx";
import { FileSystem } from "react-native-file-access";
const b64 = await FileSystem.readFile(path, "base64");
/* b64 is a base64 string */
const workbook = XLSX.read(b64, {type: "base64"});
```
- [`react-native-fs`](https://npm.im/react-native-fs)
The `ascii` encoding returns binary strings compatible with the `binary` type:
```js
import * as XLSX from "xlsx";
import { readFile } from "react-native-fs";
const bstr = await readFile(path, "ascii");
/* bstr is a binary string */
const workbook = XLSX.read(bstr, {type: "binary"});
```
- [`expo-file-system`](https://www.npmjs.com/package/expo-file-system)
:::caution
Some Expo APIs return URIs that cannot be read with `expo-file-system`. This
will manifest as an error:
> Unsupported scheme for location '...'
When using `DocumentPicker.getDocumentAsync`, enable `copyToCacheDirectory`:
```js
import * as DocumentPicker from 'expo-document-picker';
const result = await DocumentPicker.getDocumentAsync({
// highlight-next-line
copyToCacheDirectory: true,
type: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
});
const uri = result.uri;
```
:::
Calling `FileSystem.readAsStringAsync` with `FileSystem.EncodingType.Base64`
encoding returns a promise resolving to a string compatible with `base64` type:
```js
import * as XLSX from "xlsx";
import * as FileSystem from 'expo-file-system';
const b64 = await FileSystem.readAsStringAsync(uri, { encoding: FileSystem.EncodingType.Base64 });
const workbook = XLSX.read(b64, { type: "base64" });
```
[The React Native Demo](../demos/mobile#rn-file-plugins) covers tested plugins.
</TabItem>
<TabItem value="extendscript" label="Photoshop">

@ -241,62 +241,7 @@ shows a complete example and details the required version-specific settings.
</TabItem>
<TabItem value="reactnative" label="React Native">
:::caution
React Native does not provide a way to write files to the filesystem. A
separate third-party library must be used.
Since React Native internals change between releases, libraries may only work
with specific versions of React Native. Project documentation should be
consulted before picking a library.
:::
The [`react` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/react) includes a sample React Native app.
The following libraries have been tested:
- [`react-native-file-access`](https://npm.im/react-native-file-access)
The `base64` encoding accepts Base64 strings compatible with the `binary` type:
```js
import * as XLSX from "xlsx";
import { Dirs, FileSystem } from "react-native-file-access";
const DDP = Dirs.DocumentDir + "/";
const b64 = XLSX.write(workbook, {type:'base64', bookType:"xlsx"});
/* b64 is a base64 string */
await FileSystem.writeFile(DDP + "sheetjs.xlsx", b64, "base64");
```
- [`react-native-fs`](https://npm.im/react-native-fs)
The `ascii` encoding accepts binary strings compatible with the `binary` type:
```js
import * as XLSX from "xlsx";
import { writeFile, DocumentDirectoryPath } from "react-native-fs";
const DDP = DocumentDirectoryPath + "/";
const bstr = XLSX.write(workbook, {type:'binary', bookType:"xlsx"});
/* bstr is a binary string */
await writeFile(DDP + "sheetjs.xlsx", bstr, "ascii");
```
- [`expo-file-system`](https://www.npmjs.com/package/expo-file-system)
The `FileSystem.EncodingType.Base64` encoding accepts Base64 strings:
```js
import * as XLSX from "xlsx";
import * as FileSystem from 'expo-file-system';
const DDP = FileSystem.documentDirectory;
const b64 = XLSX.write(workbook, {type:'base64', bookType:"xlsx"});
/* b64 is a base64 string */
await FileSystem.writeAsStringAsync(DDP + "sheetjs.xlsx", b64, { encoding: FileSystem.EncodingType.Base64 });
```
[The React Native Demo](../demos/mobile#rn-file-plugins) covers tested plugins.
</TabItem>
<TabItem value="extendscript" label="Photoshop">

@ -0,0 +1,85 @@
/* sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
import { utils } from 'xlsx';
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, Button, Alert, Image, ScrollView } from 'react-native';
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
const make_width = ws => {
const aoa = utils.sheet_to_json(ws, {header:1}), res = [];
aoa.forEach((r) => { r.forEach((c, C) => { res[C] = Math.max(res[C]||60, String(c).length * 10); }); });
for(let C = 0; C < res.length; ++C) if(!res[C]) res[C] = 60;
return res;
};
class SheetJSRN extends Component {
constructor(props) {
super(props);
this.state = {
data: ["SheetJS".split(""),[5,4,3,3,7,9,5],[8,6,7,5,3,0,9]],
widthArr: Array.from({length:7}, () => 20)
};
this.importFile = this.importFile.bind(this);
this.exportFile = this.exportFile.bind(this);
};
async importFile() { try {
/* select and parse file */
const wb = await pickAndParse();
/* 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 */
this.setState({ data: data, widthArr: make_width(ws) });
} catch(err) { Alert.alert("importFile Error", "Error " + err.message); }}
async exportFile() { try {
/* convert AOA back to worksheet */
const ws = utils.aoa_to_sheet(this.state.data);
/* build new workbook */
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "SheetJS");
/* write file */
const res = await writeWorkbook(wb);
Alert.alert("exportFile success", "Exported to " + res);
} catch(err) { Alert.alert("exportFile Error", "Error " + err.message); }}
render() { return (
<ScrollView contentContainerStyle={styles.container} vertical={true}>
<Text style={styles.welcome}> </Text>
<Text style={styles.welcome}> <Image source={require("./logo.png")} style={styles.image}/> &nbsp; SheetJS × React Native</Text>
<Button onPress={this.importFile} title="Import data from a spreadsheet" color="#841584" />
<Button disabled={!this.state.data.length} onPress={this.exportFile} title="Export data to XLSX" color="#841584" />
<Text style={styles.bolded}>Current Data</Text>
<ScrollView style={styles.table} horizontal={true} >
<Table style={styles.table}>
<TableWrapper>
<Row data={this.state.data[0]} style={styles.thead} textStyle={styles.text} widthArr={this.state.widthArr}/>
</TableWrapper>
<ScrollView vertical={true}>
<TableWrapper>
<Rows data={this.state.data.slice(1)} style={styles.tr} textStyle={styles.text} widthArr={this.state.widthArr}/>
</TableWrapper>
</ScrollView>
</Table>
</ScrollView>
</ScrollView>
); };
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF' },
welcome: { fontSize: 20, textAlign: 'center', margin: 10 },
bolded: { textAlign: 'center', color: '#333333', marginBottom: 5, fontWeight: "bold" },
thead: { height: 40, backgroundColor: '#f1f8ff' },
tr: { height: 30 },
text: { marginLeft: 5, },
table: { width: "100%" },
image: { height: 16, width: 16 }
});
AppRegistry.registerComponent('SheetJSRN', () => SheetJSRN);

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB