forked from sheetjs/docs.sheetjs.com
		
	rnm
This commit is contained in:
		
							parent
							
								
									fc8923d20f
								
							
						
					
					
						commit
						bb95be789f
					
				
							
								
								
									
										12
									
								
								.spelling
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										12
									
								
								.spelling
									
									
									
									
									
								
							| @ -7,6 +7,10 @@ docs.sheetjs.com | ||||
| TabItem | ||||
| DocCardList | ||||
| 
 | ||||
| # frontmatter noise | ||||
| api | ||||
| csf | ||||
| 
 | ||||
| # Excel-related terms | ||||
| A1-Style | ||||
| AutoFilter | ||||
| @ -111,6 +115,7 @@ Auth | ||||
| BOM | ||||
| Base64 | ||||
| Base64-encoded | ||||
| Big5 | ||||
| Booleans | ||||
| Browserify | ||||
| Bundlers | ||||
| @ -139,11 +144,13 @@ ES5 | ||||
| ES6 | ||||
| ESM | ||||
| ETH | ||||
| Endian | ||||
| Ethercalc | ||||
| ExpressJS | ||||
| ExtendScript | ||||
| Fastify | ||||
| FileReader | ||||
| GBK | ||||
| GatsbyJS | ||||
| Goja | ||||
| HTML | ||||
| @ -222,6 +229,7 @@ SWF | ||||
| Schemas | ||||
| Serverless | ||||
| SessionStorage | ||||
| Shift-JIS | ||||
| SlimerJS | ||||
| Snowpack | ||||
| SuiteScript | ||||
| @ -321,7 +329,3 @@ vscode-data-preview | ||||
| webpack | ||||
| weex | ||||
| wkhtmltopdf | ||||
| 
 | ||||
| # frontmatter noise | ||||
| api | ||||
| csf | ||||
|  | ||||
| @ -1124,7 +1124,7 @@ the `ItemGroup` that contains `ReactPackageProvider.cs`: | ||||
|   </TabItem> | ||||
|   <TabItem value="cpp" label="C++"> | ||||
| 
 | ||||
| 2) Create the file `windows\SheetJSWin\DocumentPicker.h` with the following: | ||||
| 4) Create the file `windows\SheetJSWin\DocumentPicker.h` with the following: | ||||
| 
 | ||||
| ```cpp title="windows\SheetJSWin\DocumentPicker.h" | ||||
| #pragma once | ||||
| @ -1174,7 +1174,7 @@ namespace SheetJSWin { | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 3) Add the highlighted line to `windows\SheetJSWin\ReactPackageProvider.cpp`: | ||||
| 5) Add the highlighted line to `windows\SheetJSWin\ReactPackageProvider.cpp`: | ||||
| 
 | ||||
| ```cpp title="windows\SheetJSWin\ReactPackageProvider.cpp" | ||||
| #include "ReactPackageProvider.h" | ||||
| @ -1188,7 +1188,7 @@ namespace SheetJSWin { | ||||
| 
 | ||||
| Now the native module will be added to the app. | ||||
| 
 | ||||
| 4) Remove `App.js` and save the following to `App.tsx`: | ||||
| 6) Remove `App.js` and save the following to `App.tsx`: | ||||
| 
 | ||||
| ```tsx title="App.tsx" | ||||
| import React, { useState, type Node } from 'react'; | ||||
| @ -1234,7 +1234,7 @@ const styles = StyleSheet.create({ | ||||
| export default App; | ||||
| ``` | ||||
| 
 | ||||
| 5) Test the app again: | ||||
| 7) Test the app again: | ||||
| 
 | ||||
| ```powershell | ||||
| npx react-native run-windows --no-telemetry | ||||
| @ -1350,6 +1350,329 @@ import { getEnforcing } from 'react-native/Libraries/TurboModule/TurboModuleRegi | ||||
| const DocumentPicker = getEnforcing('DocumentPicker'); | ||||
| 
 | ||||
| 
 | ||||
| /* ... in some event handler ... */ | ||||
| async() => { | ||||
|   const b64 = await DocumentPicker.PickAndRead(); | ||||
|   const wb = read(b64); | ||||
|   // DO SOMETHING WITH `wb` HERE | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## React Native MacOS | ||||
| 
 | ||||
| The [NodeJS Module](../getting-started/installation/nodejs) can be imported | ||||
| from the main app script.  File operations must be written in native code. | ||||
| 
 | ||||
| This demo was tested against `v0.64.30` on 2022 September 10 in MacOS 12.4 | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| At the time of writing, the latest supported React Native version was `v0.64.3` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| <details><summary><b>Complete Example</b> (click to show)</summary> | ||||
| 
 | ||||
| 0) Follow the [React Native](https://reactnative.dev/docs/environment-setup) | ||||
|    guide for React Native CLI on MacOS. | ||||
| 
 | ||||
| :::caution | ||||
| 
 | ||||
| NodeJS `v16` is required.  There are OS-specific tools for downgrading: | ||||
| 
 | ||||
| - [`nvm-windows`](https://github.com/coreybutler/nvm-windows/releases) Windows | ||||
| - [`nvm`](https://github.com/nvm-sh/nvm/) Linux, MacOS, WSL, etc. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 1) Create a new project using React Native `0.64`: | ||||
| 
 | ||||
| ```bash | ||||
| npx react-native init SheetJSmacOS --template react-native@^0.64.0 | ||||
| cd SheetJSmacOS | ||||
| ``` | ||||
| 
 | ||||
| Create the MacOS part of the application: | ||||
| 
 | ||||
| ```bash | ||||
| npx react-native-macos-init --no-telemetry | ||||
| ``` | ||||
| 
 | ||||
| Install Library: | ||||
| 
 | ||||
| ``` | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz | ||||
| ``` | ||||
| 
 | ||||
| To ensure that the app works, launch the app: | ||||
| 
 | ||||
| ```powershell | ||||
| 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`: | ||||
| 
 | ||||
| ```objc title="macos/SheetJSmacOS-macOS/RCTDocumentPicker.h" | ||||
| #import <React/RCTBridgeModule.h> | ||||
| @interface RCTDocumentPicker : NSObject <RCTBridgeModule> | ||||
| @end | ||||
| ``` | ||||
| 
 | ||||
| Create the file `macos/SheetJSmacOS-macOS/RCTDocumentPicker.m`: | ||||
| 
 | ||||
| ```objc title="macos/SheetJSmacOS-macOS/RCTDocumentPicker.m" | ||||
| #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 | ||||
| ``` | ||||
| 
 | ||||
| 3) Edit the project file `macos/SheetJSmacOS.xcodeproj/project.pbxproj`. | ||||
| 
 | ||||
| There are four places where lines must be added: | ||||
| 
 | ||||
| A) Immediately after `/* 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 */; }; | ||||
| ``` | ||||
| 
 | ||||
| B) Immediately after `/* Begin PBXFileReference section */` | ||||
| 
 | ||||
| ```plist | ||||
| /* Begin PBXFileReference section */ | ||||
| // highlight-start | ||||
|     4717DC6828CC495400A9BE56 /* RCTDocumentPicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RCTDocumentPicker.h; path = "SheetJSMacOS-macOS/RCTDocumentPicker.h"; sourceTree = "<group>"; }; | ||||
|     4717DC6928CC499A00A9BE56 /* RCTDocumentPicker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RCTDocumentPicker.m; path = "SheetJSMacOS-macOS/RCTDocumentPicker.m"; sourceTree = "<group>"; }; | ||||
| // highlight-end | ||||
|     008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; }; | ||||
| ``` | ||||
| 
 | ||||
| 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`: | ||||
| 
 | ||||
| ```plist | ||||
| /* Begin PBXNativeTarget section */ | ||||
| ... | ||||
|       productType = "com.apple.product-type.application"; | ||||
|     }; | ||||
| // highlight-next-line | ||||
|     514201482437B4B30078DB4F /* SheetJSmacOS-macOS */ = { | ||||
|       isa = PBXNativeTarget; | ||||
| ... | ||||
| /* End PBXNativeTarget section */ | ||||
| ``` | ||||
| 
 | ||||
| Within the block, look for `buildPhases` and find the hex string for `Sources`: | ||||
| 
 | ||||
| ```plist | ||||
|       buildPhases = ( | ||||
|         1A938104A937498D81B3BD3B /* [CP] Check Pods Manifest.lock */, | ||||
|         381D8A6F24576A6C00465D17 /* Start Packager */, | ||||
| // highlight-next-line | ||||
|         514201452437B4B30078DB4F /* Sources */, | ||||
|         514201462437B4B30078DB4F /* Frameworks */, | ||||
|         514201472437B4B30078DB4F /* Resources */, | ||||
|         381D8A6E24576A4E00465D17 /* Bundle React Native code and images */, | ||||
|         3689826CA944E2EF44FCBC17 /* [CP] Copy Pods Resources */, | ||||
|       ); | ||||
| ``` | ||||
| 
 | ||||
| Search for that hex string (`514201452437B4B30078DB4F` in our example) in the | ||||
| file and it should show up in a `PBXSourcesBuildPhase` section. Within `files`, | ||||
| add the highlighted line: | ||||
| 
 | ||||
| ```plist | ||||
|     514201452437B4B30078DB4F /* Sources */ = { | ||||
|       isa = PBXSourcesBuildPhase; | ||||
|       buildActionMask = 2147483647; | ||||
|       files = ( | ||||
| // highlight-next-line | ||||
|         4717DC6A28CC499A00A9BE56 /* RCTDocumentPicker.m in Sources */, | ||||
|         514201502437B4B30078DB4F /* ViewController.m in Sources */, | ||||
|         514201582437B4B40078DB4F /* main.m in Sources */, | ||||
|         5142014D2437B4B30078DB4F /* AppDelegate.m in Sources */, | ||||
|       ); | ||||
|       runOnlyForDeploymentPostprocessing = 0; | ||||
|     }; | ||||
| ``` | ||||
| 
 | ||||
| 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`: | ||||
| 
 | ||||
| ```plist | ||||
| /* Begin PBXProject section */ | ||||
|     83CBB9F71A601CBA00E9B192 /* Project object */ = { | ||||
|       isa = PBXProject; | ||||
| ... | ||||
|         Base, | ||||
|       ); | ||||
| // highlight-next-line | ||||
|       mainGroup = 83CBB9F61A601CBA00E9B192; | ||||
|       productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; | ||||
| ... | ||||
| /* End PBXProject section */ | ||||
| ``` | ||||
| 
 | ||||
| Search for that hex string (`83CBB9F61A601CBA00E9B192` in our example) in the | ||||
| file and it should show up in a `PBXGroup` section.  Within `children`, add the | ||||
| highlighted lines: | ||||
| 
 | ||||
| ```plist | ||||
|     83CBB9F61A601CBA00E9B192 = { | ||||
|       isa = PBXGroup; | ||||
|       children = ( | ||||
| // highlight-start | ||||
|         4717DC6828CC495400A9BE56 /* RCTDocumentPicker.h */, | ||||
|         4717DC6928CC499A00A9BE56 /* RCTDocumentPicker.m */, | ||||
| // highlight-end | ||||
|         5142014A2437B4B30078DB4F /* SheetJSmacOS-macOS */, | ||||
|         13B07FAE1A68108700A75B9A /* SheetJSmacOS-iOS */, | ||||
| ``` | ||||
| 
 | ||||
| 4) Replace `App.js` with the following: | ||||
| 
 | ||||
| ```tsx title="App.js" | ||||
| 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: | ||||
| 
 | ||||
| ```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. | ||||
| 
 | ||||
| 6) Make a release build: | ||||
| 
 | ||||
| ```bash | ||||
| xcodebuild -workspace macos/SheetJSmacOS.xcworkspace -scheme SheetJSmacOS-macOS -config Release | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ### Reading Files | ||||
| 
 | ||||
| Only the main UI thread can show file pickers.  This is similar to Web Worker | ||||
| DOM access limitations in the Web platform. | ||||
| 
 | ||||
| This example defines a `PickAndRead` function that will show the file picker, | ||||
| read the file contents, and return a Base64 string. | ||||
| 
 | ||||
| ```objc | ||||
| /* the resolve/reject is projected on the JS side as a Promise */ | ||||
| RCT_EXPORT_METHOD(PickAndRead:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) | ||||
| { | ||||
|   /* perform file picker action in the UI thread */ | ||||
|   // highlight-next-line | ||||
|   RCTExecuteOnMainQueue(^{ | ||||
|     /* create file picker */ | ||||
|     NSOpenPanel *panel = [NSOpenPanel openPanel]; | ||||
|     [panel setCanChooseDirectories:NO]; | ||||
|     [panel setAllowsMultipleSelection:NO]; | ||||
|     [panel setMessage:@"Select a spreadsheet to read"]; | ||||
| 
 | ||||
|     /* show file picker */ | ||||
|     // highlight-next-line | ||||
|     [panel beginWithCompletionHandler:^(NSInteger result){ | ||||
|       if (result == NSModalResponseOK) { | ||||
|         /* read data and return base64 string */ | ||||
|         NSURL *selected = [[panel URLs] objectAtIndex:0]; | ||||
|         NSFileHandle *hFile = [NSFileHandle fileHandleForReadingFromURL:selected error:nil]; | ||||
|         if(hFile) { | ||||
|           NSData *data = [hFile readDataToEndOfFile]; | ||||
|           // highlight-next-line | ||||
|           resolve([data base64EncodedStringWithOptions:0]); | ||||
|         } else reject(@"read_failure", @"Could not read selected file!", nil); | ||||
|       } else reject(@"select_failure", @"No file selected!", nil); | ||||
|     }]; | ||||
|   }); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| This module is referenced in the same way as the React Native Windows example: | ||||
| 
 | ||||
| ```js | ||||
| import { read } from 'xlsx'; | ||||
| import { getEnforcing } from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; | ||||
| const DocumentPicker = getEnforcing('DocumentPicker'); | ||||
| 
 | ||||
| 
 | ||||
| /* ... in some event handler ... */ | ||||
| async() => { | ||||
|   const b64 = await DocumentPicker.PickAndRead(); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user