This commit is contained in:
SheetJS 2023-03-12 01:25:57 -05:00
parent 3e30d569c2
commit 01d7333f44
12 changed files with 582 additions and 150 deletions

@ -55,19 +55,19 @@ For existing projects, the easiest approach is to uninstall and reinstall:
<TabItem value="npm" label="npm">
<pre><code parentName="pre" {...{"className": "language-bash"}}>{`\
npm rm --save xlsx
npm i --save file:vendor/xlsx-${current}.tgz`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</code></pre>
</TabItem>
<TabItem value="pnpm" label="pnpm">
<pre><code parentName="pre" {...{"className": "language-bash"}}>{`\
pnpm rm xlsx
pnpm install file:vendor/xlsx-${current}.tgz`}
pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</code></pre>
</TabItem>
<TabItem value="yarn" label="Yarn" default>
<pre><code parentName="pre" {...{"className": "language-bash"}}>{`\
yarn remove xlsx
yarn add file:vendor/xlsx-${current}.tgz`}
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</code></pre>
</TabItem>
</Tabs>

@ -114,6 +114,39 @@ function exportFile() {
</main>
```
<details open><summary><b>How to run the example</b> (click to show)</summary>
:::note
This demo was last run on 2023 March 08 using `svelte@3.55.1`. When running
`npm create`, the package `create-vite@4.1.0` was installed.
:::
1) Run `npm create vite@latest sheetjs-svelte -- --template svelte-ts`.
2) Install the SheetJS dependency and start the dev server:
```bash
cd sheetjs-svelte
npm install
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
npm run dev
```
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
4) Replace `src/App.svelte` with the `src/SheetJSSvelteAoO.svelte` example.
The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSSvelteAoA.xlsx`. There may be a
delay since Vite will try to optimize the SheetJS library on the fly.
5) Build the site with `npm run build`, then test with `npx http-server dist`.
Access `http://localhost:8080` with a web browser to test the bundled site.
</details>
### HTML
The main disadvantage of the Array of Objects approach is the specific nature
@ -158,3 +191,36 @@ function exportFile() {
<!-- highlight-end -->
</main>
```
<details open><summary><b>How to run the example</b> (click to show)</summary>
:::note
This demo was last run on 2023 March 08 using `svelte@3.55.1`. When running
`npm create`, the package `create-vite@4.1.0` was installed.
:::
1) Run `npm create vite@latest sheetjs-svelte -- --template svelte-ts`.
2) Install the SheetJS dependency and start the dev server:
```bash
cd sheetjs-svelte
npm install
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
npm run dev
```
3) Open a web browser and access the displayed URL (`http://localhost:5173`)
4) Replace `src/App.svelte` with the `src/SheetJSSvelteHTML.svelte` example.
The page will refresh and show a table with an Export button. Click the button
and the page will attempt to download `SheetJSSvelteHTML.xlsx`. There may be a
delay since Vite will try to optimize the SheetJS library on the fly.
5) Build the site with `npm run build`, then test with `npx http-server dist`.
Access `http://localhost:8080` with a web browser to test the bundled site.
</details>

@ -29,7 +29,7 @@ features like scrolling may not work as expected.
function SheetJSXSpread() {
const [url, setUrl] = React.useState("https://sheetjs.com/pres.numbers");
const [done, setDone] = React.useState(false);
const ref = React.useRef();
const ref = React.useRef(); // ref to DIV container
const set_url = React.useCallback((evt) => setUrl(evt.target.value));
return ( <>
@ -43,7 +43,7 @@ function SheetJSXSpread() {
x_spreadsheet(ref.current).loadData(stox(wb));
setDone(true);
}}><b>Fetch!</b></button>
</>)}
</> )}
</> );
}
```

@ -0,0 +1,137 @@
---
title: Canvas Datagrid
pagination_prev: demos/frontend/index
pagination_next: demos/net/index
---
<head>
<script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script>
</head>
After extensive testing, `canvas-datagrid` stood out as a high-performance grid
with a straightforward API.
[Click here for a live standalone integration demo.](pathname:///cdg/)
## Live Demo
:::note
Due to CSS conflicts between the data grid and the documentation generator,
features like scrolling may not work as expected.
[The linked demo uses a simple HTML page.](pathname:///cdg/)
:::
```jsx live
function SheetJSCDG() {
const [url, setUrl] = React.useState("https://sheetjs.com/pres.numbers");
const [done, setDone] = React.useState(false);
const ref = React.useRef(); // ref to DIV container
const set_url = React.useCallback((evt) => setUrl(evt.target.value));
const [cdg, setCdg] = React.useState(null); // reference to grid object
return ( <>
<div height={300} width={300} ref={ref}/>
{!done && ( <>
<b>URL: </b><input type="text" value={url} onChange={set_url} size="50"/>
<br/><button onClick={async() => {
/* fetch and parse workbook */
const wb = XLSX.read(await (await fetch(url)).arrayBuffer());
const ws = wb.Sheets[wb.SheetNames[0]];
const data = XLSX.utils.sheet_to_json(ws, { header:1 });
/* set up grid and load data */
if(!cdg) setCdg(canvasDatagrid({ parentNode: ref.current, data }));
else cdg.data = data;
setDone(true);
}}><b>Fetch!</b></button>
</> )}
</> );
}
```
## Integration Details
#### Obtaining the Library
The `canvas-datagrid` NodeJS packages include a minified script that can be
directly inserted as a script tag. The unpkg CDN also serves this script:
```html
<script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script>
```
#### Previewing Data
The HTML document needs a container element:
```html
<div id="gridctr"></div>
```
Grid initialization is a one-liner:
```js
var grid = canvasDatagrid({
parentNode: document.getElementById('gridctr'),
data: []
});
```
For large data sets, it's necessary to constrain the size of the grid.
```js
grid.style.height = '100%';
grid.style.width = '100%';
```
Once the workbook is read and the worksheet is selected, assigning the data
variable automatically updates the view:
```js
grid.data = XLSX.utils.sheet_to_json(ws, {header:1});
```
This demo previews the first worksheet.
#### Editing
`canvas-datagrid` handles the entire edit cycle. No intervention is necessary.
#### Saving Data
`grid.data` is immediately readable and can be converted back to a worksheet.
Some versions return an array-like object without the length, so a little bit of
preparation may be needed:
```js
/* converts an array of array-like objects into an array of arrays */
function prep(arr) {
var out = [];
for(var i = 0; i < arr.length; ++i) {
if(!arr[i]) continue;
if(Array.isArray(arr[i])) { out[i] = arr[i]; continue };
var o = new Array();
Object.keys(arr[i]).forEach(function(k) { o[+k] = arr[i][k] });
out[i] = o;
}
return out;
}
/* build worksheet from the grid data */
var ws = XLSX.utils.aoa_to_sheet(prep(grid.data));
/* build up workbook */
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
/* generate download */
XLSX.writeFile(wb, "SheetJS.xlsx");
```
#### Additional Features
This demo barely scratches the surface. The underlying grid component includes
many additional features that work with [SheetJS Pro](https://sheetjs.com/pro).

@ -34,101 +34,16 @@ With a familiar UI, `x-spreadsheet` is an excellent choice for a modern editor.
[Click here for a live integration demo.](pathname:///xspreadsheet/)
[The exposition has been moved to a separate page.](/docs/demos/grid/xs)
**[The exposition has been moved to a separate page.](/docs/demos/grid/xs)**
### Canvas DataGrid
### Canvas Datagrid
After extensive testing, [`canvas-datagrid`](https://canvas-datagrid.js.org/demo.html)
stood out as a very high-performance grid with an incredibly simple API.
After extensive testing, `canvas-datagrid` stood out as a high-performance grid
with a straightforward API.
[Click here for a live integration demo.](pathname:///cdg/index.html)
<details><summary><b>Full Exposition</b> (click to show)</summary>
**Obtaining the Library**
The `canvas-datagrid` NodeJS packages include a minified script that can be
directly inserted as a script tag. The unpkg CDN also serves this script:
```html
<script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script>
```
**Previewing Data**
The HTML document needs a container element:
```html
<div id="gridctr"></div>
```
Grid initialization is a one-liner:
```js
var grid = canvasDatagrid({
parentNode: document.getElementById('gridctr'),
data: []
});
```
For large data sets, it's necessary to constrain the size of the grid.
```js
grid.style.height = '100%';
grid.style.width = '100%';
```
Once the workbook is read and the worksheet is selected, assigning the data
variable automatically updates the view:
```js
grid.data = XLSX.utils.sheet_to_json(ws, {header:1});
```
This demo previews the first worksheet.
**Editing**
`canvas-datagrid` handles the entire edit cycle. No intervention is necessary.
**Saving Data**
`grid.data` is immediately readable and can be converted back to a worksheet.
Some versions return an array-like object without the length, so a little bit of
preparation may be needed:
```js
/* converts an array of array-like objects into an array of arrays */
function prep(arr) {
var out = [];
for(var i = 0; i < arr.length; ++i) {
if(!arr[i]) continue;
if(Array.isArray(arr[i])) { out[i] = arr[i]; continue };
var o = new Array();
Object.keys(arr[i]).forEach(function(k) { o[+k] = arr[i][k] });
out[i] = o;
}
return out;
}
/* build worksheet from the grid data */
var ws = XLSX.utils.aoa_to_sheet(prep(grid.data));
/* build up workbook */
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
/* generate download */
XLSX.writeFile(wb, "SheetJS.xlsx");
```
**Additional Features**
This demo barely scratches the surface. The underlying grid component includes
many additional features including massive data streaming, sorting and styling.
</details>
**[The exposition has been moved to a separate page.](/docs/demos/grid/cdg)**
### Tabulator

@ -13,6 +13,15 @@ work as-is in WebSQL.
The public demo <https://sheetjs.com/sql> generates a database from workbook.
:::caution
WebSQL is only supported in Chromium-based browsers including Chrome.
Safari historically supported WebSQL but Safari 13 dropped support. Legacy
browsers including Internet Explorer and Firefox never added support.
:::
## WebSQL Details
Importing data from spreadsheets is straightforward using the `generate_sql`

@ -0,0 +1,211 @@
---
title: C + QuickJS
pagination_prev: demos/bigdata/index
pagination_next: solutions/input
---
QuickJS is an embeddable JS engine written in C. It provides a separate set of
functions for interacting with the filesystem and the global object. It can run
the standalone browser scripts.
## Integration Details
_Initialize QuickJS_
QuickJS provides a `global` object through `JS_GetGlobalObject`:
```c
/* initialize */
JSRuntime *rt = JS_NewRuntime();
JSContext *ctx = JS_NewContext(rt);
/* obtain reference to global object */
JSValue global = JS_GetGlobalObject(ctx);
/* DO WORK HERE */
/* free after use */
JS_FreeValue(ctx, global);
/* cleanup */
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
```
:::warning
All values must be freed with `JS_FreeValue` before calling `JS_FreeContext`!
`JS_IsException` should be used for validation.
Cleanup and validation code is omitted from the discussion. The integration
example shows structured validation and controlled memory usage.
:::
_Load SheetJS Scripts_
The main library can be loaded by reading the script from the file system and
evaluating in the QuickJS context:
```c
static char *read_file(const char *filename, size_t *sz) {
FILE *f = fopen(filename, "rb");
if(!f) return NULL;
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fsee (f, 0, SEEK_SET); }
char *buf = (char *)malloc(fsize * sizeof(char));
*sz = fread((void *) buf, 1, fsize, f);
fclose(f);
return buf;
}
// ...
/* load library */
{
size_t len; char *buf = read_file("xlsx.full.min.js", &len);
JS_Eval(ctx, buf, len, "<input>", 0);
free(buf);
}
```
To confirm the library is loaded, `XLSX.version` can be inspected:
```c
/* obtain reference to the XLSX object */
JSValue XLSX = JS_GetPropertyStr(ctx, global, "XLSX");
/* print version */
JSValue version = JS_GetPropertyStr(ctx, XLSX, "version");
size_t vlen; const char *vers = JS_ToCStringLen(ctx, &vlen, version);
printf("Version: %s\n", vers);
```
### Reading Files
`JS_NewArrayBuffer` can generate an `ArrayBuffer` from a C byte array. The
function signature expects `uint8_t *` instead of `char *`:
```c
/* read file */
size_t dlen; uint8_t * dbuf = (uint8_t *)read_file("pres.numbers", &dlen);
/* load data into array buffer */
JSValue ab = JS_NewArrayBuffer(ctx, dbuf, dlen, NULL, NULL, 0);
/* obtain reference to the XLSX object */
JSValue XLSX = JS_GetPropertyStr(ctx, global, "XLSX");
/* call XLSX.read(ab) */
JSValue XLSX_read = JS_GetPropertyStr(ctx, XLSX, "read");
JSValue args[] = { ab };
JSValue wb = JS_Call(ctx, XLSX_read, XLSX, 1, args);
```
## Complete Example
The "Integration Example" covers a traditional integration in a C application,
while the "CLI Test" demonstrates other concepts using the `quickjs` CLI tool.
### Integration Example
:::note
This demo was last tested on 2023 March 11 against QuickJS commit `2788d71` on
a Intel Mac. `gcc -v` printed:
```
Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: x86_64-apple-darwin21.6.0
```
:::
0) Build `libquickjs.a`:
```bash
git clone --depth=1 https://github.com/bellard/quickjs
cd quickjs
git checkout 2788d71
make
cd ..
```
1) Copy `libquickjs.a` and `quickjs.h` into the working directory:
```bash
cp quickjs/libquickjs.a .
cp quickjs/quickjs.h .
```
2) Download [`sheetjs.quick.c`](pathname:///quickjs/sheetjs.quick.c):
```bash
curl -LO https://docs.sheetjs.com/quickjs/sheetjs.quick.c
```
3) Build the sample application:
```bash
gcc -o sheetjs.quick -Wall -lm libquickjs.a sheetjs.quick.c
```
This program tries to parse the file specified by the first argument
4) Download the standalone script and test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
```bash
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js
curl -LO https://sheetjs.com/pres.numbers
```
5) Run the test program:
```
./sheetjs.quick pres.numbers
```
If successful, the program will print the library version number, file size,
first worksheet name, and the contents of the first sheet as CSV rows.
### CLI Test
:::note
This demo was last tested on 2023 March 11 against QuickJS `2021-03-27`.
:::
0) Ensure `quickjs` command line utility is installed
1) Download the standalone script and the test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
```bash
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js
curl -LO https://sheetjs.com/pres.numbers
```
2) Download [`SheetJSQuick.js`](pathname:///quickjs/SheetJSQuick.js)
```bash
curl -LO https://docs.sheetjs.com/quickjs/SheetJSQuick.js
```
3) Test the program:
```bash
quickjs SheetJSQuick.js
```
If successful, the script will generate `SheetJSQuick.xlsx`.

@ -246,57 +246,7 @@ QuickJS is an embeddable JS engine written in C. It provides a separate set of
functions for interacting with the filesystem and the global object. It can run
the standalone browser scripts.
<details><summary><b>Complete Example</b> (click to show)</summary>
0) Ensure `quickjs` command line utility is installed
1) Download the standalone script, the shim and the test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js`}>shim.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
2) Save the following script to `SheetJSQuick.js`:
```js title="SheetJSQuick.js"
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
/* load XLSX */
import * as std from "std";
globalThis.global = globalThis;
std.loadScript("xlsx.full.min.js");
/* read contents of file */
var rh = std.open("pres.numbers", "rb");
rh.seek(0, std.SEEK_END);
var sz = rh.tell();
var ab = new ArrayBuffer(sz);
rh.seek();
rh.read(ab, 0, sz);
rh.close();
/* parse file */
var wb = XLSX.read(ab);
/* write XLSX */
var out = XLSX.write(wb, {bookType: "xlsx", type: "array"});
/* write contents to file */
var wh = std.open("SheetJSQuick.xlsx", "wb");
wh.write(out, 0, out.byteLength);
wh.close();
```
3) Test the program:
```bash
quickjs SheetJSQuick.js
```
If successful, the script will generate `SheetJSQuick.xlsx`.
</details>
This demo has been moved [to a dedicated page](/docs/demos/engines/quickjs).
### Rhino

@ -32,7 +32,7 @@ run in the web browser, demos will include interactive examples.
### Front-End UI Components
- [`canvas-datagrid`](/docs/demos/grid#canvas-datagrid)
- [`canvas-datagrid`](/docs/demos/grid/cdg)
- [`x-spreadsheet`](/docs/demos/grid/xs)
- [`react-data-grid`](/docs/demos/grid#react-data-grid)
- [`glide-data-grid`](/docs/demos/grid#glide-data-grid)

@ -737,9 +737,8 @@ function Tabeller(props) {
/* fetch and update the workbook with an effect */
React.useEffect(() => { (async() => {
/* fetch and parse workbook -- see the fetch example for details */
const wb = XLSX.read(await (await fetch("sheetjs.xlsx")).arrayBuffer());
setWorkbook(wb);
})(); });
setWorkbook(XLSX.read(await (await fetch("sheetjs.xlsx")).arrayBuffer()));
})(); }, []);
return workbook.SheetNames.map(name => ( <>
<h3>name</h3>

@ -0,0 +1,25 @@
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
/* load XLSX */
import * as std from "std";
globalThis.global = globalThis;
std.loadScript("xlsx.full.min.js");
/* read contents of file */
var rh = std.open("pres.numbers", "rb");
rh.seek(0, std.SEEK_END);
var sz = rh.tell();
var ab = new ArrayBuffer(sz);
rh.seek();
rh.read(ab, 0, sz);
rh.close();
/* parse file */
var wb = XLSX.read(ab);
/* write XLSX */
var out = XLSX.write(wb, {bookType: "xlsx", type: "array"});
/* write contents to file */
var wh = std.open("SheetJSQuick.xlsx", "wb");
wh.write(out, 0, out.byteLength);
wh.close();

@ -0,0 +1,120 @@
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "quickjs.h"
static char *read_file(const char *filename, size_t *sz) {
FILE *f = fopen(filename, "rb");
if(!f) return NULL;
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
char *buf = (char *)malloc(fsize * sizeof(char));
*sz = fread((void *) buf, 1, fsize, f);
fclose(f);
return buf;
}
#define CLEANUP(v) { JS_FreeContext(ctx); JS_FreeRuntime(rt); return v; }
#define FREE(v) JS_FreeValue(ctx, v);
#define VALIDATE(v) { if(JS_IsException(v)) CLEANUP(1) }
#define VALINIT(xx,vv) \
JSValue xx = vv;\
VALIDATE(xx)
#define VALPROP(xx,vv,pp) VALINIT(xx, JS_GetPropertyStr(ctx, vv, pp))
int main(int argc, char *argv[]) {
JSRuntime *rt = JS_NewRuntime();
JSContext *ctx = JS_NewContext(rt);
/* load library */
{
size_t len; char * buf = read_file("xlsx.full.min.js", &len);
VALIDATE(JS_Eval(ctx, buf, len, "<input>", 0))
free(buf);
}
VALINIT(global, JS_GetGlobalObject(ctx))
VALPROP(XLSX, global, "XLSX");
FREE(global)
/* print version */
{
VALPROP(version, XLSX, "version")
size_t vlen; const char *vers = JS_ToCStringLen(ctx, &vlen, version);
printf("Version: %s\n", vers);
FREE(version)
}
/* parse workbook */
JSValue wb;
{
/* read file */
size_t dlen; uint8_t * dbuf = (uint8_t *)read_file(argv[1], &dlen);
/* load data into array buffer */
JSValue ab = JS_NewArrayBuffer(ctx, dbuf, dlen, NULL, NULL, 0);
{
VALPROP(byteLen, ab, "byteLength")
uint32_t byteLength; JS_ToUint32(ctx, &byteLength, byteLen);
FREE(byteLen)
printf("Size: %d\n", byteLength);
}
/* call XLSX.read(ab) */
{
VALPROP(XLSX_read, XLSX, "read");
JSValue args[] = { ab };
wb = JS_Call(ctx, XLSX_read, XLSX, 1, args);
FREE(XLSX_read)
}
/* cleanup */
FREE(ab)
free(dbuf);
}
/* print CSV of first worksheet */
{
/* get first worksheet */
JSValue ws;
{
/* get name of first sheet */
const char *wsname;
{
JSValue SheetNames = JS_GetPropertyStr(ctx, wb, "SheetNames");
JSValue Sheet1 = JS_GetPropertyStr(ctx, SheetNames, "0");
size_t wslen; wsname = JS_ToCStringLen(ctx, &wslen, Sheet1);
FREE(Sheet1)
FREE(SheetNames)
}
printf("Worksheet Name: %s\n", wsname);
/* get worksheet object */
{
VALPROP(Sheets, wb, "Sheets");
ws = JS_GetPropertyStr(ctx, Sheets, wsname);
FREE(Sheets)
}
}
/* print CSV */
{
VALPROP(utils, XLSX, "utils")
VALPROP(sheet_to_csv, utils, "sheet_to_csv")
JSValue args[] = { ws };
JSValue csv = JS_Call(ctx, sheet_to_csv, utils, 1, args);
FREE(sheet_to_csv)
FREE(utils)
size_t csvlen; const char *csvstr = JS_ToCStringLen(ctx, &csvlen, csv);
printf("%s\n", csvstr);
FREE(csv)
}
FREE(ws)
}
FREE(wb)
FREE(XLSX)
CLEANUP(0)
}