From c2433e1c8330bcac31ff749909b835bce238a6cd Mon Sep 17 00:00:00 2001 From: SheetJS Date: Sun, 14 Aug 2022 23:07:34 -0400 Subject: [PATCH] RN --- docz/docs/03-demos/16-desktop.md | 2 +- docz/docs/03-demos/19-mobile.md | 628 +++++++++++++++++++++++++++- docz/docs/03-demos/index.md | 8 +- docz/docs/06-solutions/01-input.md | 76 +--- docz/docs/06-solutions/05-output.md | 57 +-- docz/static/mobile/index.js | 85 ++++ docz/static/mobile/rnand1.png | Bin 0 -> 77640 bytes docz/static/mobile/rnand2.png | Bin 0 -> 76731 bytes docz/static/mobile/rnand3.png | Bin 0 -> 104865 bytes docz/static/mobile/rnand4.png | Bin 0 -> 47799 bytes docz/static/mobile/rnios1.png | Bin 0 -> 79802 bytes docz/static/mobile/rnios2.png | Bin 0 -> 72156 bytes docz/static/mobile/rnios3.png | Bin 0 -> 110885 bytes docz/static/mobile/rnios4.png | Bin 0 -> 175796 bytes 14 files changed, 719 insertions(+), 137 deletions(-) create mode 100644 docz/static/mobile/index.js create mode 100644 docz/static/mobile/rnand1.png create mode 100644 docz/static/mobile/rnand2.png create mode 100644 docz/static/mobile/rnand3.png create mode 100644 docz/static/mobile/rnand4.png create mode 100644 docz/static/mobile/rnios1.png create mode 100644 docz/static/mobile/rnios2.png create mode 100644 docz/static/mobile/rnios3.png create mode 100644 docz/static/mobile/rnios4.png diff --git a/docz/docs/03-demos/16-desktop.md b/docz/docs/03-demos/16-desktop.md index 2179da6..4b6dbf8 100644 --- a/docz/docs/03-demos/16-desktop.md +++ b/docz/docs/03-demos/16-desktop.md @@ -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'; diff --git a/docz/docs/03-demos/19-mobile.md b/docz/docs/03-demos/19-mobile.md index 227de2d..cd50890 100644 --- a/docz/docs/03-demos/19-mobile.md +++ b/docz/docs/03-demos/19-mobile.md @@ -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` + +
Selecting a file (click to show) + +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 +``` + +
+ +#### `expo-document-picker` + +
Selecting a file (click to show) + +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; +``` + +
+ + +### 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. + +
Reading and Writing snippets (click to show) + +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'); +``` + + +
+ +#### `react-native-file-access` + +The `base64` encoding returns strings compatible with the `base64` type: + +
Reading and Writing snippets (click to show) + +_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"); +``` + +
+ +#### `react-native-fs` + +The `ascii` encoding returns binary strings compatible with the `binary` type: + +
Reading and Writing snippets (click to show) + +_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"); +``` + +
+ +#### `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. + +
Reading and Writing snippets (click to show) + +_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 }); +``` + +
+ +### 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. + +::: + +
Complete Example (click to show) + +This example tries to separate the library-specific functions. + +0) **Follow the official React Native CLI Quickstart!** + +Quickstart URL: + +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: + + + + + +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 => { +``` + + + + +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 => { +``` + + + + +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 => { +``` + + + + +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 => { +``` + + + + +:::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 => { +``` + + + + + +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 +- 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 +- 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 +``` + +
+ ## 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. -
Complete Example (click to show) +
Complete Example (click to show) 0) Ensure all of the dependencies are installed. Install the CLI globally: diff --git a/docz/docs/03-demos/index.md b/docz/docs/03-demos/index.md index 74abc46..fd9cf3e 100644 --- a/docz/docs/03-demos/index.md +++ b/docz/docs/03-demos/index.md @@ -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 diff --git a/docz/docs/06-solutions/01-input.md b/docz/docs/06-solutions/01-input.md index ab99426..fdd23d8 100644 --- a/docz/docs/06-solutions/01-input.md +++ b/docz/docs/06-solutions/01-input.md @@ -96,81 +96,7 @@ shows a complete example and details the required version-specific settings. -:::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. diff --git a/docz/docs/06-solutions/05-output.md b/docz/docs/06-solutions/05-output.md index 4abed6d..f100ac6 100644 --- a/docz/docs/06-solutions/05-output.md +++ b/docz/docs/06-solutions/05-output.md @@ -241,62 +241,7 @@ shows a complete example and details the required version-specific settings. -:::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. diff --git a/docz/static/mobile/index.js b/docz/static/mobile/index.js new file mode 100644 index 0000000..cbc697d --- /dev/null +++ b/docz/static/mobile/index.js @@ -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 ( + + +   SheetJS × React Native +