rnm
This commit is contained in:
parent
be15eb3620
commit
4dd53501ff
@ -36,8 +36,8 @@ new versions are released!
|
||||
|
||||
## NetSuite
|
||||
|
||||
After downloading the script, it can be referenced directly in `define` calls
|
||||
in SuiteScripts:
|
||||
After uploading the script to the File Cabinet, it can be referenced directly in
|
||||
`define` calls in SuiteScripts:
|
||||
|
||||
```js
|
||||
define(['N/file', './xlsx.full.min'], function(file, XLSX) {
|
||||
@ -45,8 +45,8 @@ define(['N/file', './xlsx.full.min'], function(file, XLSX) {
|
||||
})
|
||||
```
|
||||
|
||||
As explained in the [NetSuite demo](/docs/demos/cloud/netsuite), module
|
||||
aliases are created in config files referenced via `@NAmdConfig` comments.
|
||||
As explained in the [NetSuite demo](/docs/demos/cloud/netsuite), module aliases
|
||||
can be created in config files referenced via `@NAmdConfig` comments.
|
||||
|
||||
## SAP UI5
|
||||
|
||||
|
@ -54,13 +54,13 @@ module.exports = {
|
||||
NextJS collects telemetry by default. The `telemetry` subcommand can disable it:
|
||||
|
||||
```js
|
||||
npx next@13.4.4 telemetry disable
|
||||
npx next@13.4.12 telemetry disable
|
||||
```
|
||||
|
||||
The setting can be verified by running
|
||||
|
||||
```js
|
||||
npx next@13.4.4 telemetry status
|
||||
npx next@13.4.12 telemetry status
|
||||
```
|
||||
|
||||
:::
|
||||
@ -69,10 +69,11 @@ npx next@13.4.4 telemetry status
|
||||
|
||||
The following deployments were tested:
|
||||
|
||||
| NextJS | Date |
|
||||
|:-------|:-----------|
|
||||
| 13.1.1 | 2023-01-14 |
|
||||
| 13.4.4 | 2023-05-26 |
|
||||
| NextJS | NodeJS | Date |
|
||||
|:--------|:----------|:-----------|
|
||||
| 11.1.4 | `16.20.1` | 2023-07-23 |
|
||||
| 12.3.4 | `18.17.0` | 2023-07-23 |
|
||||
| 13.4.12 | `18.17.0` | 2023-07-23 |
|
||||
|
||||
:::
|
||||
|
||||
@ -503,6 +504,9 @@ This demo showcases the following SheetJS + NextJS flows:
|
||||
| `/sheets/[id]` | asset module | `getStaticPaths` | `sheet_to_html` |
|
||||
| `/getServerSideProps` | lifecycle | `getServerSideProps` | `sheet_to_html` |
|
||||
|
||||
The commands in this demo use `next@13.4.12`. Other versions were tested by
|
||||
replacing the version number in the relevant commands.
|
||||
|
||||
:::
|
||||
|
||||
### Initial Setup
|
||||
@ -510,13 +514,13 @@ This demo showcases the following SheetJS + NextJS flows:
|
||||
0) Disable NextJS telemetry:
|
||||
|
||||
```js
|
||||
npx next@13.4.4 telemetry disable
|
||||
npx next@13.4.12 telemetry disable
|
||||
```
|
||||
|
||||
Confirm it is disabled by running
|
||||
|
||||
```js
|
||||
npx next@13.4.4 telemetry status
|
||||
npx next@13.4.12 telemetry status
|
||||
```
|
||||
|
||||
1) Set up folder structure. At the end, a `pages` folder with a `sheets`
|
||||
@ -538,7 +542,7 @@ curl -LO https://docs.sheetjs.com/next/sheetjs.xlsx
|
||||
3) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz next@13.4.4`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz next@13.4.12`}
|
||||
</CodeBlock>
|
||||
|
||||
4) Download NextJS config scripts and place in the root folder:
|
||||
@ -596,7 +600,7 @@ cd ../..
|
||||
6) Test the deployment:
|
||||
|
||||
```bash
|
||||
npx next@13.4.4
|
||||
npx next@13.4.12
|
||||
```
|
||||
|
||||
Open a web browser and access:
|
||||
@ -622,20 +626,20 @@ After saving the file, the website should refresh with the new row.
|
||||
8) Stop the server and run a production build:
|
||||
|
||||
```bash
|
||||
npx next@13.4.4 build
|
||||
npx next@13.4.12 build
|
||||
```
|
||||
|
||||
The final output will show a list of the routes and types:
|
||||
|
||||
```
|
||||
Route (pages) Size First Load JS
|
||||
┌ ○ / 563 B 74.4 kB
|
||||
├ /_app 0 B 73.9 kB
|
||||
├ ○ /404 182 B 74.1 kB
|
||||
├ λ /getServerSideProps 522 B 74.4 kB
|
||||
├ ● /getStaticPaths 2.89 kB 76.8 kB
|
||||
├ ● /getStaticProps 586 B 74.5 kB
|
||||
└ ● /sheets/[id] 522 B 74.4 kB
|
||||
┌ ○ / 563 B 75.3 kB
|
||||
├ /_app 0 B 74.8 kB
|
||||
├ ○ /404 182 B 75 kB
|
||||
├ λ /getServerSideProps 522 B 75.3 kB
|
||||
├ ● /getStaticPaths 2.91 kB 77.7 kB
|
||||
├ ● /getStaticProps 586 B 75.4 kB
|
||||
└ ● /sheets/[id] (303 ms) 522 B 75.3 kB
|
||||
├ /sheets/0
|
||||
└ /sheets/1
|
||||
```
|
||||
@ -647,7 +651,7 @@ worksheets in the file. `/getServerSideProps` is server-rendered.
|
||||
9) Try to build a static site:
|
||||
|
||||
```bash
|
||||
npx next@13.4.4 export
|
||||
npx next@13.4.12 export
|
||||
```
|
||||
|
||||
:::note The static export will fail!
|
||||
@ -663,19 +667,19 @@ is still server-rendered.
|
||||
|
||||
```bash
|
||||
rm -f pages/getServerSideProps.js
|
||||
npx next@13.4.4 build
|
||||
npx next@13.4.12 build
|
||||
```
|
||||
|
||||
Inspecting the output, there should be no lines with the `λ` symbol:
|
||||
|
||||
```
|
||||
Route (pages) Size First Load JS
|
||||
┌ ○ / 563 B 74.4 kB
|
||||
├ /_app 0 B 73.9 kB
|
||||
├ ○ /404 182 B 74.1 kB
|
||||
├ ● /getStaticPaths 2.89 kB 76.8 kB
|
||||
├ ● /getStaticProps 586 B 74.5 kB
|
||||
└ ● /sheets/[id] 522 B 74.4 kB
|
||||
┌ ○ / 563 B 75.3 kB
|
||||
├ /_app 0 B 74.8 kB
|
||||
├ ○ /404 182 B 75 kB
|
||||
├ ● /getStaticPaths 2.91 kB 77.7 kB
|
||||
├ ● /getStaticProps 586 B 75.4 kB
|
||||
└ ● /sheets/[id] 522 B 75.3 kB
|
||||
├ /sheets/0
|
||||
└ /sheets/1
|
||||
```
|
||||
@ -683,7 +687,7 @@ Route (pages) Size First Load JS
|
||||
11) Generate the static site:
|
||||
|
||||
```bash
|
||||
npx next@13.4.4 export
|
||||
npx next@13.4.12 export
|
||||
```
|
||||
|
||||
The static site will be written to the `out` subfolder
|
||||
@ -694,5 +698,6 @@ The static site will be written to the `out` subfolder
|
||||
npx http-server out
|
||||
```
|
||||
|
||||
The command will start a local HTTP server for testing the generated site. Note
|
||||
that `/getServerSideProps` will 404 since the page was removed.
|
||||
The command will start a local HTTP server at `http://localhost:8080/` for
|
||||
testing the generated site. Note that `/getServerSideProps` will 404 since the
|
||||
page was removed.
|
||||
|
@ -20,7 +20,6 @@ The following deployments were tested:
|
||||
| Nuxt Content | Nuxt | Date |
|
||||
|:-------------|:---------|:-----------|
|
||||
| `1.15.1` | `2.16.3` | 2023-06-01 |
|
||||
| `2.3.0` | `3.0.0` | 2023-01-19 |
|
||||
| `2.6.0` | `3.5.2` | 2023-06-01 |
|
||||
|
||||
:::
|
||||
|
@ -1,5 +1,7 @@
|
||||
---
|
||||
title: React Native
|
||||
sidebar_label: React Native
|
||||
description: Build data-intensive desktop apps with React Native. Seamlessly integrate spreadsheets into your app using SheetJS. Securely process and generate Excel files at the desk.
|
||||
pagination_prev: demos/mobile/index
|
||||
pagination_next: demos/data/index
|
||||
sidebar_position: 6
|
||||
@ -7,26 +9,25 @@ sidebar_custom_props:
|
||||
summary: Native Components with React
|
||||
---
|
||||
|
||||
# Sheets on the Desktop with React Native
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
:::note
|
||||
|
||||
This section covers React Native for desktop applications. For iOS and Android
|
||||
applications, [check the mobile demo](/docs/demos/mobile/reactnative)
|
||||
|
||||
:::
|
||||
|
||||
React Native for Windows + macOS is a backend for React Native that supports
|
||||
React Native for Windows + macOS[^1] is a backend for React Native that supports
|
||||
native apps. The Windows backend builds apps for use on Windows 10 / 11, Xbox,
|
||||
and other supported platforms. The macOS backend supports macOS 10.14 SDK
|
||||
|
||||
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
|
||||
from the main app script. File operations must be written in native code.
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
The "Complete Example" creates an app that looks like the screenshots below:
|
||||
This demo uses React Native for Windows + macOS and SheetJS to process
|
||||
spreadsheets. We'll explore how to load SheetJS in a React Native deskktop app
|
||||
and create native modules for selecting and reading files from the computer.
|
||||
|
||||
The Windows and macOS demos create apps that look like the screenshots below:
|
||||
|
||||
<table><thead><tr>
|
||||
<th><a href="#windows-demo">Win10</a></th>
|
||||
@ -41,8 +42,6 @@ The "Complete Example" creates an app that looks like the screenshots below:
|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
<details><summary><b>Tested Environments</b> (click to show)</summary>
|
||||
|
||||
:::note
|
||||
|
||||
This demo was tested in the following environments:
|
||||
@ -51,13 +50,169 @@ This demo was tested in the following environments:
|
||||
|:---------------|:-----|:------------|:-----------|
|
||||
| Windows 10 | x64 | `v0.70.10` | 2023-01-04 |
|
||||
| Windows 11 | x64 | `v0.71.11` | 2023-05-11 |
|
||||
| MacOS 12.4 | x64 | `v0.64.30` | 2023-01-04 |
|
||||
| MacOS 12.6 | x64 | `v0.71.26` | 2023-07-23 |
|
||||
| MacOS 13.4 | arm | `v0.71.18` | 2023-07-06 |
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
This section covers React Native for desktop applications. For iOS and Android
|
||||
applications, [check the mobile demo](/docs/demos/mobile/reactnative)
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
|
||||
imported from the main `App.js` entrypoint or any script in the project.
|
||||
|
||||
### 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>
|
||||
|
||||
![`pres.xlsx` data](pathname:///pres.png)
|
||||
|
||||
</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 within the structure corresponds to one row.
|
||||
|
||||
The state is initialized with the following snippet:
|
||||
|
||||
```js
|
||||
const [ aoa, setAoA ] = useState(["SheetJS".split(""), "5433795".split("")]);
|
||||
```
|
||||
|
||||
#### Updating State
|
||||
|
||||
Starting from a SheetJS worksheet object, `sheet_to_json`[^3] with the `header`
|
||||
option can generate an array of arrays:
|
||||
|
||||
```js
|
||||
/* 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 */
|
||||
setAoA(data);
|
||||
}
|
||||
```
|
||||
|
||||
### Displaying Data
|
||||
|
||||
The demos use native `View` elements from `react-native` to display data.
|
||||
|
||||
<details><summary><b>Explanation</b> (click to show)</summary>
|
||||
|
||||
Since some spreadsheets may have empty cells between cells containing data,
|
||||
looping over the rows may skip values!
|
||||
|
||||
This example explicitly loops over the row and column indices.
|
||||
|
||||
**Determining the Row Indices**
|
||||
|
||||
The first row index is `0` and the last row index is `aoa.length - 1`. This
|
||||
corresponds to the `for` loop:
|
||||
|
||||
```js
|
||||
for(var R = 0; R < aoa.length; ++R) {/* ... */}
|
||||
```
|
||||
|
||||
**Determining the Column Indices**
|
||||
|
||||
The first column index is `0` and the last column index must be calculated from
|
||||
the maximum column index across every row.
|
||||
|
||||
Traditionally this would be implemented in a `for` loop:
|
||||
|
||||
```js
|
||||
var max_col_index = 0;
|
||||
for(var R = 0; R < aoa.length; ++R) {
|
||||
if(!aoa[R]) continue;
|
||||
max_col_index = Math.max(max_col_index, aoa[R].length - 1);
|
||||
}
|
||||
```
|
||||
|
||||
`Array#reduce` simplifies this calculation:
|
||||
|
||||
```js
|
||||
const max_col_index = aoa.reduce((C,row) => Math.max(C,row.length), 1) - 1;
|
||||
```
|
||||
|
||||
**Looping from 0 to N-1**
|
||||
|
||||
Traditionally a `for` loop would be used:
|
||||
|
||||
```js
|
||||
var data = [];
|
||||
for(var R = 0; R < max_row; ++R) data[R] = func(R);
|
||||
```
|
||||
|
||||
For creating an array of React Native components, `Array.from` should be used:
|
||||
|
||||
```jsx
|
||||
var children = Array.from({length: max_row}, (_,R) => ( <Row key={R} /> ));
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
The relevant parts for rendering data are shown below:
|
||||
|
||||
```jsx
|
||||
import React, { useState, type FC } from 'react';
|
||||
import { SafeAreaView, ScrollView, Text, View } from 'react-native';
|
||||
|
||||
const App: FC = () => {
|
||||
const [ aoa, setAoA ] = useState(["SheetJS".split(""), "5433795".split("")]);
|
||||
const max_cols = aoa.reduce((acc,row) => Math.max(acc,row.length),1);
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||
{/* Table Container */}
|
||||
<View>{
|
||||
/* Loop over the row indices */
|
||||
// highlight-next-line
|
||||
Array.from({length: aoa.length}, (_, R) => (
|
||||
/* Table Row */
|
||||
<View key={R}>{
|
||||
/* Loop over the column indices */
|
||||
// highlight-next-line
|
||||
Array.from({length: max_cols}, (_, C) => (
|
||||
/* Table Cell */
|
||||
<View key={C}>
|
||||
// highlight-next-line
|
||||
<Text>{String(aoa?.[R]?.[C]??"")}</Text>
|
||||
</View>
|
||||
))
|
||||
}</View>
|
||||
))
|
||||
}</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
export default App;
|
||||
```
|
||||
|
||||
## Native Modules
|
||||
|
||||
:::caution
|
||||
@ -70,8 +225,7 @@ assumes some familiarity with Objective-C.
|
||||
|
||||
:::
|
||||
|
||||
React Native for Windows + macOS use [Turbo Modules](https://reactnative.dev/docs/the-new-architecture/pillars-turbomodules)
|
||||
for effortless integration with native libraries and code.
|
||||
React Native for Windows + macOS use Turbo Modules[^4] for native integrations.
|
||||
|
||||
The demos define a native module named `DocumentPicker`.
|
||||
|
||||
@ -357,20 +511,20 @@ curl -Lo windows/SheetJSWin/DocumentPicker.h https://docs.sheetjs.com/reactnativ
|
||||
|
||||
Now the native module will be added to the app.
|
||||
|
||||
4) Remove `App.js` (if it exists) and save the following to `App.tsx`:
|
||||
4) Remove `App.js` (if it exists) and download [`App.tsx`](https://docs.sheetjs.com/reactnative/rnw/App.tsx):
|
||||
|
||||
<Tabs groupId="shell">
|
||||
<TabItem value="pwsh" label="PowerShell">
|
||||
|
||||
```bash
|
||||
iwr -Uri https://docs.sheetjs.com/reactnative/desktop/App.tsx -OutFile App.tsx
|
||||
iwr -Uri https://docs.sheetjs.com/reactnative/rnw/App.tsx -OutFile App.tsx
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bash" label="WSL Bash">
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/reactnative/desktop/App.tsx
|
||||
curl -LO https://docs.sheetjs.com/reactnative/rnw/App.tsx
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@ -389,29 +543,31 @@ file picker to select the `pres.xlsx` file and the app will show the data.
|
||||
|
||||
## macOS Demo
|
||||
|
||||
0) Follow the [React Native](https://reactnative.dev/docs/environment-setup)
|
||||
guide for React Native CLI on MacOS.
|
||||
0) Follow the "Setting up the development environment"[^5] guide in the React
|
||||
Native documentation for "React Native CLI Quickstart" + "macOS" + "iOS".
|
||||
|
||||
1) Create a new project using React Native `0.71`:
|
||||
### Project Setup
|
||||
|
||||
1) Create a new React Native project using React Native `0.71`:
|
||||
|
||||
```bash
|
||||
npx react-native init SheetJSmacOS --template react-native@^0.71.0
|
||||
npx -y react-native init SheetJSmacOS --template react-native@^0.71.0
|
||||
cd SheetJSmacOS
|
||||
```
|
||||
|
||||
Create the MacOS part of the application:
|
||||
2) Create the MacOS part of the application:
|
||||
|
||||
```bash
|
||||
npx react-native-macos-init --no-telemetry
|
||||
npx -y react-native-macos-init --no-telemetry
|
||||
```
|
||||
|
||||
Install the SheetJS library:
|
||||
3) Install the SheetJS library:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
To ensure that the app works, launch the app:
|
||||
4) To ensure that the app works, launch the app:
|
||||
|
||||
```bash
|
||||
npx react-native run-macos
|
||||
@ -419,7 +575,10 @@ npx react-native run-macos
|
||||
|
||||
Close the running app from the dock and close the Metro terminal window.
|
||||
|
||||
2) Create the file `macos/SheetJSmacOS-macOS/RCTDocumentPicker.h`:
|
||||
### Native Module
|
||||
|
||||
5) Create the file `macos/SheetJSmacOS-macOS/RCTDocumentPicker.h` with the
|
||||
following contents:
|
||||
|
||||
```objc title="macos/SheetJSmacOS-macOS/RCTDocumentPicker.h"
|
||||
#import <React/RCTBridgeModule.h>
|
||||
@ -427,7 +586,8 @@ Close the running app from the dock and close the Metro terminal window.
|
||||
@end
|
||||
```
|
||||
|
||||
Create the file `macos/SheetJSmacOS-macOS/RCTDocumentPicker.m`:
|
||||
6) Create the file `macos/SheetJSmacOS-macOS/RCTDocumentPicker.m` with the
|
||||
following contents:
|
||||
|
||||
```objc title="macos/SheetJSmacOS-macOS/RCTDocumentPicker.m"
|
||||
#import <Foundation/Foundation.h>
|
||||
@ -462,20 +622,26 @@ RCT_EXPORT_METHOD(PickAndRead:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromi
|
||||
@end
|
||||
```
|
||||
|
||||
3) Edit the project file `macos/SheetJSmacOS.xcodeproj/project.pbxproj`.
|
||||
7) Edit the project file `macos/SheetJSmacOS.xcodeproj/project.pbxproj`.
|
||||
|
||||
There are four places where lines must be added:
|
||||
|
||||
A) Immediately after `/* Begin PBXBuildFile section */`
|
||||
:::note pass
|
||||
|
||||
A) Copy the highlighted line and paste under `/* Begin PBXBuildFile section */`:
|
||||
|
||||
```plist
|
||||
/* Begin PBXBuildFile section */
|
||||
// highlight-next-line
|
||||
4717DC6A28CC499A00A9BE56 /* RCTDocumentPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 4717DC6928CC499A00A9BE56 /* RCTDocumentPicker.m */; };
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||
5142014D2437B4B30078DB4F /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5142014C2437B4B30078DB4F /* AppDelegate.mm */; };
|
||||
```
|
||||
|
||||
B) Immediately after `/* Begin PBXFileReference section */`
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
B) Copy the highlighted lines and paste under `/* Begin PBXFileReference section */`:
|
||||
|
||||
```plist
|
||||
/* Begin PBXFileReference section */
|
||||
@ -486,6 +652,10 @@ B) Immediately after `/* Begin PBXFileReference section */`
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
C) The goal is to add a reference to the `PBXSourcesBuildPhase` block for the
|
||||
`macOS` target. To determine this, look in the `PBXNativeTarget section` for
|
||||
a block with the comment `SheetJSmacOS-macOS`:
|
||||
@ -505,6 +675,9 @@ a block with the comment `SheetJSmacOS-macOS`:
|
||||
Within the block, look for `buildPhases` and find the hex string for `Sources`:
|
||||
|
||||
```plist
|
||||
514201482437B4B30078DB4F /* SheetJSmacOS-macOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 5142015A2437B4B40078DB4F /* Build configuration list for PBXNativeTarget "SheetJSmacOS-macOS" */;
|
||||
buildPhases = (
|
||||
1A938104A937498D81B3BD3B /* [CP] Check Pods Manifest.lock */,
|
||||
381D8A6F24576A6C00465D17 /* Start Packager */,
|
||||
@ -528,14 +701,17 @@ add the highlighted line:
|
||||
files = (
|
||||
// highlight-next-line
|
||||
4717DC6A28CC499A00A9BE56 /* RCTDocumentPicker.m in Sources */,
|
||||
514201502437B4B30078DB4F /* ViewController.m in Sources */,
|
||||
514201582437B4B40078DB4F /* main.m in Sources */,
|
||||
5142014D2437B4B30078DB4F /* AppDelegate.m in Sources */,
|
||||
5142014D2437B4B30078DB4F /* AppDelegate.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
D) The goal is to add file references to the "main group". Search for
|
||||
`/* Begin PBXProject section */` and there should be one Project object.
|
||||
Within the project object, look for `mainGroup`:
|
||||
@ -570,63 +746,57 @@ highlighted lines:
|
||||
13B07FAE1A68108700A75B9A /* SheetJSmacOS-iOS */,
|
||||
```
|
||||
|
||||
4) Replace `App.tsx` with the following:
|
||||
:::
|
||||
|
||||
```tsx title="App.tsx"
|
||||
import React, { useState, type Node } from 'react';
|
||||
import { SafeAreaView, ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native';
|
||||
import { read, utils, version } from 'xlsx';
|
||||
import { getEnforcing } from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
|
||||
const DocumentPicker = getEnforcing('DocumentPicker');
|
||||
|
||||
const App: () => Node = () => {
|
||||
|
||||
const [ aoa, setAoA ] = useState(["SheetJS".split(""), "5433795".split("")]);
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.outer}>
|
||||
<Text style={styles.title}>SheetJS × React Native MacOS {version}</Text>
|
||||
<TouchableHighlight onPress={async() => {
|
||||
try {
|
||||
const b64 = await DocumentPicker.PickAndRead();
|
||||
const wb = read(b64);
|
||||
setAoA(utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header: 1 } ));
|
||||
} catch(err) { alert(`Error: ${err.message}`); }
|
||||
}}><Text style={styles.button}>Click here to Open File!</Text></TouchableHighlight>
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||
<View style={styles.table}>{aoa.map((row,R) => (
|
||||
<View style={styles.row} key={R}>{row.map((cell,C) => (
|
||||
<View style={styles.cell} key={C}><Text>{cell}</Text></View>
|
||||
))}</View>
|
||||
))}</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
cell: { flex: 4 },
|
||||
row: { flexDirection: 'row', justifyContent: 'space-evenly', padding: 10, backgroundColor: 'white', },
|
||||
table: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', },
|
||||
outer: { marginTop: 32, paddingHorizontal: 24, },
|
||||
title: { fontSize: 24, fontWeight: '600', },
|
||||
button: { marginTop: 8, fontSize: 18, fontWeight: '400', },
|
||||
});
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
5) Test the app:
|
||||
8) To ensure that the app still works, launch the app again:
|
||||
|
||||
```bash
|
||||
npx react-native run-macos
|
||||
```
|
||||
|
||||
Download <https://sheetjs.com/pres.xlsx>, then click on "open file". Use the
|
||||
file picker to select the `pres.xlsx` file and the app will show the data.
|
||||
Close the running app from the dock and close the Metro terminal window.
|
||||
|
||||
6) Make a release build:
|
||||
### Application
|
||||
|
||||
9) Download [`App.tsx`](https://docs.sheetjs.com/reactnative/rnm/App.tsx) and
|
||||
replace the file in the project:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/reactnative/rnm/App.tsx
|
||||
```
|
||||
|
||||
10) Test the app:
|
||||
|
||||
```bash
|
||||
npx react-native run-macos
|
||||
```
|
||||
|
||||
Download <https://sheetjs.com/pres.xlsx>.
|
||||
|
||||
Click "Click here to Open File!" and use the file picker to select `pres.xlsx` .
|
||||
The app will refresh and display the data from the file.
|
||||
|
||||
11) Make a release build:
|
||||
|
||||
```bash
|
||||
xcodebuild -workspace macos/SheetJSmacOS.xcworkspace -scheme SheetJSmacOS-macOS -config Release
|
||||
```
|
||||
|
||||
The last line of the output will include the path to the app. If it is not
|
||||
displayed, the app path can be found in the `DerivedData` folder:
|
||||
|
||||
```bash
|
||||
find ~/Library/Developer/Xcode/DerivedData -name SheetJSmacOS.app | grep Release
|
||||
```
|
||||
|
||||
12) Run the release app:
|
||||
|
||||
```bash
|
||||
open -a "$(find ~/Library/Developer/Xcode/DerivedData -name SheetJSmacOS.app | grep Release | head -n 1)"
|
||||
```
|
||||
|
||||
[^1]: The [official website](https://microsoft.github.io/react-native-windows/) covers both platforms, but there are separate repositories for [Windows](https://github.com/microsoft/react-native-windows) and [macOS](https://github.com/microsoft/react-native-macos)
|
||||
[^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 ["Turbo Native Modules"](https://reactnative.dev/docs/the-new-architecture/pillars-turbomodules) in the React Native documentation.
|
||||
[^5]: See ["Setting up the development environment"](https://reactnative.dev/docs/environment-setup) in the React Native documentation. Select the "React Native CLI Quickstart" tab and choose the Development OS "macOS" and the Target OS "iOS".
|
@ -15,7 +15,7 @@ changes. Git can also store and track binary data artifacts.
|
||||
|
||||
GitHub is a popular host for Git repositories. GitHub's "Flat Data" project
|
||||
explores storing and comparing versions of structured CSV and JSON data. The
|
||||
official "Excel to CSV" example uses SheetJS to generate CSV data from files:
|
||||
official "Excel to CSV"[^1] example uses SheetJS to generate CSV data from files:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
@ -273,3 +273,5 @@ jobs:
|
||||
|
||||
The update process will run once an hour. If you return in a few hours and
|
||||
refresh the page, there should be more commits in the selection list.
|
||||
|
||||
[^1]: See ["Excel to CSV"](https://githubnext.com/projects/flat-data#:~:text=View%20code-,Excel,-to%20CSV) in the "Flat Data" writeup
|
@ -28,6 +28,8 @@ form data. This can be disabled with cloud-specific configuration:
|
||||
|
||||
- [AWS Lambda Functions](/docs/demos/cloud/aws#aws-lambda-functions)
|
||||
- [Azure Functions](/docs/demos/cloud/azure#azure-functions)
|
||||
- [GitHub Actions](/docs/demos/cloud/github)
|
||||
- [Deno Deploy](/docs/demos/cloud/deno)
|
||||
|
||||
## Cloud Storage
|
||||
|
||||
@ -44,7 +46,6 @@ File hosting services provide simple solutions for storing data, synchronizing
|
||||
files across devices, and sharing with specific users or customers. Demos:
|
||||
|
||||
- [Dropbox](/docs/demos/cloud/dropbox)
|
||||
- [GitHub](/docs/demos/cloud/github)
|
||||
|
||||
## Cloud Data
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 108 KiB |
42
docz/static/reactnative/rnm/App.tsx
Normal file
42
docz/static/reactnative/rnm/App.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import React, { useState, type FC } from 'react';
|
||||
import { SafeAreaView, ScrollView, StyleSheet, Text, TouchableHighlight, View, Alert } from 'react-native';
|
||||
import { read, utils, version } from 'xlsx';
|
||||
import { getEnforcing } from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
|
||||
const DocumentPicker = getEnforcing('DocumentPicker');
|
||||
|
||||
const App: FC = () => {
|
||||
|
||||
const [ aoa, setAoA ] = useState(["SheetJS".split(""), "5433795".split("")]);
|
||||
const max_cols = aoa.reduce((acc,row) => Math.max(acc,row.length),1);
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.outer}>
|
||||
<Text style={styles.title}>SheetJS × React Native MacOS {version}</Text>
|
||||
<TouchableHighlight onPress={async() => {
|
||||
try {
|
||||
const b64 = await DocumentPicker.PickAndRead();
|
||||
const wb = read(b64);
|
||||
setAoA(utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header: 1 } ));
|
||||
} catch(err) { Alert.alert(`Read Error`, `${err instanceof Error ? err.message : err}`); }
|
||||
}}><Text style={styles.button}>Click here to Open File!</Text></TouchableHighlight>
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||
<View style={styles.table}>{Array.from({length: aoa.length}, (_, R) => (
|
||||
<View style={styles.row} key={R}>{Array.from({length: max_cols}, (_, C) => (
|
||||
<View style={styles.cell} key={C}><Text>{String(aoa?.[R]?.[C]??"")}</Text></View>
|
||||
))}</View>
|
||||
))}</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
cell: { flex: 4 },
|
||||
row: { flexDirection: 'row', justifyContent: 'space-evenly', padding: 10, backgroundColor: 'white', },
|
||||
table: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', },
|
||||
outer: { marginTop: 32, paddingHorizontal: 24, },
|
||||
title: { fontSize: 24, fontWeight: '600', },
|
||||
button: { marginTop: 8, fontSize: 18, fontWeight: '400', },
|
||||
});
|
||||
|
||||
export default App;
|
3
docz/static/reactnative/rnm/RCTDocumentPicker.h
Normal file
3
docz/static/reactnative/rnm/RCTDocumentPicker.h
Normal file
@ -0,0 +1,3 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
@interface RCTDocumentPicker : NSObject <RCTBridgeModule>
|
||||
@end
|
30
docz/static/reactnative/rnm/RCTDocumentPicker.m
Normal file
30
docz/static/reactnative/rnm/RCTDocumentPicker.m
Normal file
@ -0,0 +1,30 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
#import "RCTDocumentPicker.h"
|
||||
|
||||
@implementation RCTDocumentPicker
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
RCT_EXPORT_METHOD(PickAndRead:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
RCTExecuteOnMainQueue(^{
|
||||
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
||||
[panel setCanChooseDirectories:NO];
|
||||
[panel setAllowsMultipleSelection:NO];
|
||||
[panel setMessage:@"Select a spreadsheet to read"];
|
||||
|
||||
[panel beginWithCompletionHandler:^(NSInteger result){
|
||||
if (result == NSModalResponseOK) {
|
||||
NSURL *selected = [[panel URLs] objectAtIndex:0];
|
||||
NSFileHandle *hFile = [NSFileHandle fileHandleForReadingFromURL:selected error:nil];
|
||||
if(hFile) {
|
||||
NSData *data = [hFile readDataToEndOfFile];
|
||||
resolve([data base64EncodedStringWithOptions:0]);
|
||||
} else reject(@"read_failure", @"Could not read selected file!", nil);
|
||||
} else reject(@"select_failure", @"No file selected!", nil);
|
||||
}];
|
||||
});
|
||||
}
|
||||
@end
|
@ -1,12 +1,13 @@
|
||||
import React, { useState, type Node } from 'react';
|
||||
import { SafeAreaView, ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native';
|
||||
import React, { useState, type FC } from 'react';
|
||||
import { SafeAreaView, ScrollView, StyleSheet, Text, TouchableHighlight, View, Alert } from 'react-native';
|
||||
import { read, utils, version } from 'xlsx';
|
||||
import { getEnforcing } from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
|
||||
const DocumentPicker = getEnforcing('DocumentPicker');
|
||||
|
||||
const App: () => Node = () => {
|
||||
const App: FC = () => {
|
||||
|
||||
const [ aoa, setAoA ] = useState(["SheetJS".split(""), "5433795".split("")]);
|
||||
const max_cols = aoa.reduce((acc,row) => Math.max(acc,row.length),1);
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.outer}>
|
||||
@ -16,12 +17,12 @@ const App: () => Node = () => {
|
||||
const b64 = await DocumentPicker.PickAndRead();
|
||||
const wb = read(b64);
|
||||
setAoA(utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header: 1 } ));
|
||||
} catch(err) { alert(`Error: ${err.message}`); }
|
||||
} catch(err) { Alert.alert(`Read Error`, `${err instanceof Error ? err.message : err}`); }
|
||||
}}><Text style={styles.button}>Click here to Open File!</Text></TouchableHighlight>
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||
<View style={styles.table}>{aoa.map((row,R) => (
|
||||
<View style={styles.row} key={R}>{row.map((cell,C) => (
|
||||
<View style={styles.cell} key={C}><Text>{cell}</Text></View>
|
||||
<View style={styles.table}>{Array.from({length: aoa.length}, (_, R) => (
|
||||
<View style={styles.row} key={R}>{Array.from({length: max_cols}, (_, C) => (
|
||||
<View style={styles.cell} key={C}><Text>{String(aoa?.[R]?.[C]??"")}</Text></View>
|
||||
))}</View>
|
||||
))}</View>
|
||||
</ScrollView>
|
||||
@ -38,4 +39,4 @@ const styles = StyleSheet.create({
|
||||
button: { marginTop: 8, fontSize: 18, fontWeight: '400', },
|
||||
});
|
||||
|
||||
export default App;
|
||||
export default App;
|
Loading…
Reference in New Issue
Block a user