docs.sheetjs.com/docz/docs/03-demos/17-mobile/07-lynx.md

402 lines
11 KiB
Markdown
Raw Normal View History

2025-03-12 03:37:40 +00:00
---
title: Sheets at Native Speed with Lynx
sidebar_label: Lynx
description: Build data-intensive mobile apps with Lynx. Seamlessly integrate spreadsheets into your app using SheetJS. Securely process and generate Excel files in the field.
pagination_prev: demos/static/index
pagination_next: demos/desktop/index
sidebar_position: 7
sidebar_custom_props:
2025-03-27 02:49:13 +00:00
summary: React + Native Rendering
2025-03-12 03:37:40 +00:00
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
export const r = {style: {color:"red"}};
export const g = {style: {color:"green"}};
export const y = {style: {color:"gold"}};
export const gr = {style: {color:"gray"}};
2025-03-27 02:49:13 +00:00
[Lynx](https://lynxjs.org/) is a modern cross-platform framework. It builds iOS,
Android and Web apps that use JavaScript for describing layouts and events.
2025-03-12 03:37:40 +00:00
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
:::caution Lynx support is considered experimental.
2025-03-27 02:49:13 +00:00
Lynx is relatively new and does not currently have a deep community.
Any issues should be reported to the Lynx project for further diagnosis.
2025-03-12 03:37:40 +00:00
:::
2025-03-27 02:49:13 +00:00
This demo uses React (using [ReactLynx](https://lynxjs.org/react)) and SheetJS
to process and generate spreadsheets. We'll explore how to load SheetJS in Lynx
apps in the following scenarios:
2025-03-12 03:37:40 +00:00
- ["Fetching Remote Data"](#fetching-remote-data) uses the built-in `fetch` to download
and parse remote workbook files.
The "Fetching Remote Data" example creates an app that looks like the screenshots below:
<table><thead><tr>
<th><a href="#demo">iOS</a></th>
<th><a href="#demo">Android</a></th>
</tr></thead><tbody><tr><td>
![iOS screenshot](pathname:///lynx/react_lynx_fetch_demo_ios_1.jpeg)
</td><td>
![Android screenshot](pathname:///lynx/react_lynx_fetch_demo_android_1.png)
</td></tr></tbody></table>
:::caution pass
2025-03-27 02:49:13 +00:00
2025-03-12 03:37:40 +00:00
**Before testing this demo, follow the official React Lynx Guide!**[^1]
Follow the instructions for iOS (requires macOS) and for Android. They will
cover installation and system configuration. You should be able to build and run
a sample app in the Android and the iOS (if applicable) simulators.
:::
2025-03-27 02:49:13 +00:00
:::danger pass
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
**Lynx development requires an Apple Silicon-powered Macintosh!**
[X64 is currently unsupported.](https://github.com/lynx-family/lynx/issues/219)
:::
## Integration Details
2025-03-12 03:37:40 +00:00
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
imported from any component or script in the app.
### 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 represents a row in the table.
This demo also keeps track of the column widths as a single array of numbers.
The widths are used by the display component.
2025-03-27 02:49:13 +00:00
```ts title="State variables"
2025-03-12 03:37:40 +00:00
const [data, setData] = useState<any[]>([
2025-03-27 02:49:13 +00:00
"SheetJS".split(""),
[5, 4, 3, 3, 7, 9, 5],
[8, 6, 7, 5, 3, 0, 9]
]);
2025-03-12 03:37:40 +00:00
const [widths, setWidths] = useState<number[]>(Array.from({ length: 7 }, () => 20));
```
#### Updating State
Starting from a SheetJS worksheet object, `sheet_to_json`[^3] with the `header`
option can generate an array of arrays:
2025-03-27 02:49:13 +00:00
```js title="Updating state from a workbook"
2025-03-12 03:37:40 +00:00
/* 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 */
setData(data);
/* update column widths */
setWidths(make_width(data));
}
```
_Calculating Column Widths_
Column widths can be calculated by walking each column and calculating the max
data width. Using the array of arrays:
2025-03-27 02:49:13 +00:00
```js title="Calculating column widths"
2025-03-12 03:37:40 +00:00
/* this function takes an array of arrays and generates widths */
function make_width(aoa) {
/* walk each row */
aoa.forEach((r) => {
/* walk each column */
r.forEach((c, C) => {
/* update column width based on the length of the cell contents */
res[C] = Math.max(res[C]||60, String(c).length * 10);
});
});
/* use a default value for columns with no data */
for(let C = 0; C < res.length; ++C) if(!res[C]) res[C] = 60;
return res;
}
```
### Displaying Data
2025-03-27 02:49:13 +00:00
Lynx does not ship with a component for displaying tabular data.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
The demo uses Lynx `<view/>` and `<text/>` elements to display tabular data:
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
```tsx title="Example JSX for displaying data"
2025-03-12 03:37:40 +00:00
{/* Table container */}
<view className='Table'>
2025-03-27 02:49:13 +00:00
{/* Map through each row in the data array */}
{data.map((row, rowIndex) => (
<view key={`row-${rowIndex}`} className="Row">
{/* Map through each cell in the current row */}
{Array.isArray(row) && row.map((cell, cellIndex) => (
{/* Cell with dynamic width based on content */}
<view
key={`cell-${rowIndex}-${cellIndex}`} className="Cell"
style={{ width: `${widths[cellIndex]}px` }}>
{/* Display cell content as text */}
<text>{String(cell)}</text>
</view>
))}
</view>
))}
2025-03-12 03:37:40 +00:00
</view>
```
## Fetching Remote Data
This snippet downloads and parses https://docs.sheetjs.com/pres.xlsx:
2025-03-27 02:49:13 +00:00
```js
2025-03-12 03:37:40 +00:00
/* fetch data into an ArrayBuffer */
const ab = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
/* parse data */
const wb = XLSX.read(ab);
```
### Fetch Demo
:::note Tested Deployments
This demo was tested in the following environments:
**Simulators**
| OS | Device | Lynx | LynxExplorer | Dev Platform | Date |
|:-----------|:--------------------|:---------|:-------------|:-------------|:-----------|
2025-03-27 02:49:13 +00:00
| Android 35 | Pixel 3a | `0.8.6` | `3.2.0-rc.1` | `darwin-arm` | 2025-03-26 |
| iOS 18.3 | iPhone 16 Pro | `0.8.6` | `3.2.0-rc.1` | `darwin-arm` | 2025-03-26 |
2025-03-12 03:37:40 +00:00
:::
:::danger Real Devices
2025-03-27 02:49:13 +00:00
When this demo was last tested, there was no simple standalone guide for running
Lynx apps on real devices.
2025-03-12 03:37:40 +00:00
:::
2025-03-27 02:49:13 +00:00
:::caution pass
First install Lynx by following the Guide![^1].
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
Make sure you can run a basic test app on your simulator before continuing!
2025-03-12 03:37:40 +00:00
:::
0) Install Lynx dependencies
1) Create project:
```bash
2025-03-27 02:49:13 +00:00
npm create rspeedy@0.8.6 -- -d SheetJSLynxFetch -t react-ts --tools biome
2025-03-12 03:37:40 +00:00
```
2) Install shared dependencies:
<CodeBlock language="bash">{`\
cd SheetJSLynxFetch
curl -o ./src/assets/SheetJS-logo.png https://docs.sheetjs.com/logo.png
2025-03-27 02:49:13 +00:00
npm i
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
2025-03-12 03:37:40 +00:00
</CodeBlock>
2025-03-27 02:49:13 +00:00
3) Download [`App.tsx`](pathname:///lynx/App.tsx) into the `src` folder:
2025-03-12 03:37:40 +00:00
```bash
curl -o ./src/App.tsx https://docs.sheetjs.com/lynx/App.tsx
```
2025-03-27 02:49:13 +00:00
4) Download [`App.css`](pathname:///lynx/App.css) into the `src` folder:
2025-03-12 03:37:40 +00:00
```bash
curl -o ./src/App.css https://docs.sheetjs.com/lynx/App.css
```
2025-03-30 06:31:40 +00:00
<a name="step5"></a>
2025-03-27 02:49:13 +00:00
5) Start the development server:
2025-03-12 03:37:40 +00:00
```bash
2025-03-27 02:49:13 +00:00
npm run dev
2025-03-12 03:37:40 +00:00
```
2025-03-27 02:49:13 +00:00
Keep the window open.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
#### Android
6) Start the Android emulator:
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
<Tabs>
<TabItem name="Android Studio" value="Android Studio">
2025-03-12 03:37:40 +00:00
In Android Studio, click "More actions" > "Virtual Device Manager". Look for the
emulated device in the list and click the ▶ button to play.
2025-03-27 02:49:13 +00:00
</TabItem>
<TabItem name="Android Studio" value="Command Line">
List the available emulators with `emulator -list-avds`:
2025-03-12 03:37:40 +00:00
```
shjs@sheetjs SheetJSLynxFetch % emulator -list-avds
Medium_Phone_API_35
^^^^^^^^^^^^^^^^^^^--- emulator name
```
2025-03-27 02:49:13 +00:00
The emulator name should be passed to `emulator -avd`. In a previous test, the
name was `Medium_Phone_API_35` and the launch command was:
2025-03-12 03:37:40 +00:00
```bash
emulator -avd Medium_Phone_API_35
```
:::note pass
On macOS, `~/Library/Android/sdk/emulator/` is the typical location
for the `emulator` binary. If it cannot be found, add the folder to `PATH`:
```bash
export PATH="$PATH":~/Library/Android/sdk/emulator
emulator -avd Medium_Phone_API_35
```
:::
</TabItem>
</Tabs>
2025-03-27 02:49:13 +00:00
7) Download the LynxExplorer[^4] APK.
The latest test used [`LynxExplorer-noasan-release.apk` for version `3.2.0-rc.1`](https://github.com/lynx-family/lynx/releases/download/3.2.0-rc.1/LynxExplorer-noasan-release.apk).
8) Drag and drop the APK into the Android emulator window.
The emulator will install LynxExplorer.
9) In the terminal window from [step 5](#step5), copy the HTTP link. It will be
printed below the QR code, as shown in the following screenshot:
2025-03-12 03:37:40 +00:00
![lynx live server link](pathname:///lynx/lynx_live_server_link.png)
2025-03-27 02:49:13 +00:00
10) In the emulator, open the "LynxExplorer" app.
11) In the **Enter Card URL** input field, paste the link. Tap **Go**.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
The view will refresh. The app should look like the "Before" screenshot:
2025-03-12 03:37:40 +00:00
<table><thead><tr>
<th>Before</th>
<th>After</th>
</tr></thead><tbody><tr><td>
![before screenshot](pathname:///lynx/react_lynx_fetch_demo_android_1.png)
</td><td>
![after screenshot](pathname:///lynx/react_lynx_fetch_demo_android_2.png)
</td></tr></tbody></table>
2025-03-27 02:49:13 +00:00
12) Tap "Import data from a spreadsheet" and verify that the app shows new data.
The app should look like the "After" screenshot.
2025-03-12 03:37:40 +00:00
**iOS Testing**
:::danger pass
**iOS testing can only be performed on Apple hardware running macOS!**
Xcode and iOS simulators are not available on Windows or Linux.
:::
2025-03-27 02:49:13 +00:00
13) Download the LynxExplorer[^4] app tarball.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
The latest test used [`LynxExplorer-arm64.app.tar.gz` for version `3.2.0-rc.1`](https://github.com/lynx-family/lynx/releases/download/3.2.0-rc.1/LynxExplorer-arm64.app.tar.gz).
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
14) Open `LynxExplorer-arm64.app.tar.gz` using Finder.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
The tarball contains an app named `LynxExplorer-arm64` .
15) Launch the iOS Simulator.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
16) Click and drag `LynxExplorer-arm64` into the Simulator window.
The simulator will install the "LynxExplorer" app.
17) Copy the HTTP link from the terminal window in [step 5](#step5).
2025-03-12 03:37:40 +00:00
![lynx live server link](pathname:///lynx/lynx_live_server_link.png)
2025-03-27 02:49:13 +00:00
18) Tap the "LynxExplorer" icon in the simulator to launch the app.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
19) Tap the **Enter Card URL** input field and paste the link. Tap **Go**.
The view will refresh. The app should look like the "Before" screenshot:
2025-03-12 03:37:40 +00:00
<table><thead><tr>
<th>Before</th>
<th>After</th>
</tr></thead><tbody><tr><td>
![before screenshot](pathname:///lynx/react_lynx_fetch_demo_ios_1.jpeg)
</td><td>
![after screenshot](pathname:///lynx/react_lynx_fetch_demo_ios_2.jpeg)
</td></tr></tbody></table>
2025-03-27 02:49:13 +00:00
20) Tap "Import data from a spreadsheet" and verify that the app shows new data. The app should look like the "After" screenshot.
[^1]: Follow ["Quick Start"](https://lynxjs.org/guide/start/quick-start.html) in
the Lynx documentation and select the appropriate "Lynx Explorer sandbox"
2025-03-12 03:37:40 +00:00
[^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)
2025-03-27 02:49:13 +00:00
[^4]: See ["LynxExplorer sandbox"](https://github.com/lynx-family/lynx/releases/latest/)