This commit is contained in:
SheetJS 2023-08-17 16:30:13 -04:00
parent 10ed191020
commit 2c4c81603d
14 changed files with 19410 additions and 168 deletions

@ -17,7 +17,7 @@ Each standalone release script is available at <https://cdn.sheetjs.com/>.
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.full.min.js</a> is the URL for {current}</p>
:::note
:::note pass
When referencing by file name, AMD loaders typically omit the file extension.
@ -63,7 +63,7 @@ sap.ui.define([
})
```
:::warning
:::warning pass
**Copy and pasting code does not work** for SheetJS scripts as they contain
Unicode characters that may be mangled. The standalone script should be
@ -73,7 +73,7 @@ downloaded and manually uploaded to the project.
## RequireJS
:::caution
:::caution pass
The standalone script must be aliased to the path `xlsx`.
@ -100,11 +100,11 @@ require(['xlsx'], function(XLSX) {
## Dojo Toolkit
Dojo has changed module loading strategies over the years. These examples were
tested with Dojo `1.10.4` and are not guaranteed to work with other versions.
tested with Dojo `1.17.3`. They are not guaranteed to work with other versions.
Live demos are included in ["Dojo Toolkit"](/docs/demos/frontend/legacy#dojo-toolkit)
:::caution
:::caution pass
The standalone scripts add `window.XLSX`, so it is recommended to use `_XLSX`
in the function arguments and access the library with `XLSX` in the callback:
@ -127,7 +127,7 @@ When `async` is set to `false` or `0`, the scripts can be directly referenced in
`require` calls.
<CodeBlock language="html">{`\
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js" data-dojo-config="isDebug:1, async:0"></script>
<script src="dojo.js" data-dojo-config="isDebug:1, async:0"></script>
<script>
require([
// highlight-next-line
@ -163,7 +163,7 @@ dojoConfig = {
]
};
</script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js" data-dojo-config="isDebug:1, async:1"></script>
<script src="dojo.js" data-dojo-config="isDebug:1, async:1"></script>
<script>
require(["xlsx"], function(_XLSX) {
// ... use XLSX here

@ -25,7 +25,7 @@ These are designed to be referenced with `<script>` tags.
## Internet Explorer
:::warning
:::warning pass
Internet Explorer is unmaintained and users should consider modern browsers.
The SheetJS testing grid still includes IE and should work.
@ -64,7 +64,7 @@ npx -y http-server .
<details><summary><b>Other Live Demos</b> (click to show)</summary>
:::caution
:::caution pass
The hosted solutions may not work in older versions of Windows. For testing,
demo pages should be downloaded and hosted using a simple HTTP server.
@ -141,7 +141,7 @@ approach is embedded in `XLSX.writeFile` and no additional shims are necessary.
**Flash-based Download**
It is possible to write to the file system using a SWF file. `Downloadify`
It is possible to write to the file system using a SWF file. `Downloadify`[^1]
implements one solution. Since a genuine click is required, there is no way to
force a download. The safest data type is Base64:
@ -171,7 +171,7 @@ scripting language can write to an arbitrary path on the filesystem. The shim
includes a special `IE_SaveFile` function to write binary strings to file. It
attempts to write to the Downloads folder or Documents folder or Desktop.
This approach can be triggered, but it requires the user to enable ActiveX. It
This approach does not require user interaction, but ActiveX must be enabled. It
is embedded as a strategy in `writeFile` and used only if the shim script is
included in the page and the relevant features are enabled on the target system.
@ -185,6 +185,22 @@ _Live Demos_
- [Download and display data](pathname:///dojo/read.html)
- [Fetch JSON and generate a workbook](pathname:///dojo/write.html)
:::note pass
These demos have been tested with Dojo Toolkit `1.10.4` and `1.17.3`.
:::
:::warning pass
The official Google CDN is out of date. This is a known CDN bug.
The script <https://docs.sheetjs.com/dojo/dojo.js> was fetched from the official
`1.17.3` uncompresssed release artifact[^2] on 2023-08-16.
:::
_Installation_
The ["AMD" instructions](/docs/getting-started/installation/amd#dojo-toolkit)
includes details for use with `require`.
@ -201,7 +217,7 @@ dojoConfig = {
]
}
</script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js" data-dojo-config="isDebug:1, async:1"></script>
<script src="dojo.js" data-dojo-config="isDebug:1, async:1"></script>
<script>
require(["dojo/request/xhr", "xlsx"], function(xhr, _XLSX) {
/* XLSX-related operations happen in the callback */
@ -339,4 +355,7 @@ binding is possible using the `$parent` and `$index` binding context properties:
</table>
```
</details>
</details>
[^1]: Project hosted at <https://github.com/dcneiner/Downloadify>
[^2]: All Dojo Toolkit releases are available at <https://download.dojotoolkit.org/>. The mirrored `dojo.js` corresponds to the `1.17.3` uncompressed script <http://download.dojotoolkit.org/release-1.17.3/dojo.js.uncompressed.js>.

@ -1,5 +1,7 @@
---
title: Glide Datagrid
title: Let Data Glide on Glide Data Grid
sidebar_label: Glide Data Grid
description: Display structured data with Glide Data Grid. Effortlessly import and export data using SheetJS. Modernize business processes while retaining legacy Excel structures.
pagination_prev: demos/frontend/index
pagination_next: demos/net/index
---
@ -7,24 +9,74 @@ pagination_next: demos/net/index
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
[Glide Data Grid](https://grid.glideapps.com/) is a high-performance data grid
designed for the ReactJS web framework.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Glide Data Grid and SheetJS to pull data from a spreadsheet and
display the content in a data grid. We'll explore how to import data from files
into the data grid and how to export modified data from the grid to workbooks.
The ["Demo"](#demo) section includes a complete example that displays data from
user-supplied sheets and exports data to XLSX workbooks:
![Glide Data Grid example](pathname:///gdg/gdg.png)
:::note
This demo was last tested on 2023 February 07 with the ViteJS+React+TypeScript
starter (Vite `4.1.1`, React `18.2.0`) and `@glideapps/glide-data-grid@5.2.1`.
This demo was last tested on 2023 August 17 with Glide Data Grid 5.2.1
:::
## Integration Details
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation with Yarn and other package managers.
Using the `npm` tool, this command installs SheetJS and Glide Data Grid:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @glideapps/glide-data-grid@5.2.1`}
</CodeBlock>
Methods and components in both libraries can be loaded in pages using `import`:
```js
import { read, utils, writeFile } from 'xlsx';
import { DataEditor, GridCellKind, GridCell, Item } from '@glideapps/glide-data-grid';
```
:::caution pass
Glide Data Grid is primarily event-based. It does not manage state directly.
Instead, developers are expected to manage updates when users edit cells.
:::
#### Backing Store
Under the hood, the `DataEditor` component is designed to call methods and
request data to display in the grid. It is typical to store data *outside* of
component state. A `getCellContent` callback will pull data from the external
backing store, while SheetJS operations will directly act on the store:
backing store, while SheetJS operations will directly act on the store.
For this demo, there are two parts to the data store:
- `data` is an "Array of Objects" that will hold the raw data[^1].
- `header` is an array of the header names
:::info pass
Following the Glide Data Grid conventions[^2], both objects are defined at the top
level of the component script. They are declared outside of the component!
:::
```js
// !! THESE OBJECTS ARE DEFINED OUTSIDE OF THE COMPONENT FUNCTION !!
// !! THESE ARRAYS ARE DEFINED OUTSIDE OF THE COMPONENT FUNCTION !!
// this will store the raw data objects
let data: any[] = [];
@ -34,9 +86,9 @@ let header: string[] = [];
#### Props
:::note
:::note pass
This is a high-level overview. The official documentation should be consulted.
This is a high-level overview. The official documentation should be consulted.[^3]
:::
@ -142,8 +194,16 @@ The demo uses the `onCellEdited` callback to write back to the data store.
_SheetJS to Data Store_
The raw data objects are readily generated with `sheet_to_json`. The headers
can be pulled by extracting the first row of the worksheet:
The SheetJS `read` method parses data from a number of sources[^4]. It returns a
workbook object which holds worksheet objects and other data[^5].
Raw data objects can be generated with the SheetJS `sheet_to_json` function[^6].
The headers can be pulled from the first row of the sheet. The `sheet_to_json`
method accepts a `range` option, and other SheetJS API functions can be used to
calculate the correct range for the header names[^7].
This example generates row objects from the first sheet in the workbook:
```js
import { utils, WorkBook } from 'xlsx';
@ -220,12 +280,22 @@ export default App;
### Writing Data
`json_to_sheet` works directly on the `data` array:
The SheetJS `json_to_sheet` method generates worksheet objects directly from
the `data` array[^8]:
```js
const ws = utils.json_to_sheet(data); // easy :)
```
The worksheet can be exported to XLSX by creating a SheetJS workbook object[^9]
and writing with `writeFile` or `writeFileXLSX`[^10]:
```js
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "Sheet1");
writeFileXLSX(wb, "sheetjs-gdg.xlsx");
```
Since the editor can change the header titles, it is strongly recommended to
pull column data from the state and rewrite the header row:
@ -262,7 +332,7 @@ export default App;
## Demo
1) Create a new project:
1) Create a new project from the `react-ts` template:
```bash
npm create vite@latest -- sheetjs-gdg --template react-ts
@ -297,7 +367,7 @@ Refresh the browser window and a grid should be displayed:
3) To test the export functionality, make some changes to the grid data.
Suppose you believe that President Grover Cleveland should be counted once.
Some statisticians believe President Grover Cleveland should be counted once.
That would imply President Clinton should be index 41 and the indices of the
other presidents should be decremented. By double-clicking on each cell in the
Index column, a cell editor should appear. Decrement each index:
@ -305,3 +375,14 @@ Index column, a cell editor should appear. Decrement each index:
![glide-data-grid after edits](pathname:///gdg/post.png)
Click on the "Export" button to create a file! Open the file and verify.
[^1]: See ["Array of Objects" in the ReactJS demo](/docs/demos/frontend/react#array-of-objects)
[^2]: The "Story" section of the ["Getting Started" page in the Glide Data Grid Storybook](https://quicktype.github.io/glide-data-grid/?path=/story/glide-data-grid-docs--getting-started) stores an Array of Objects outside of the component.
[^3]: See [the "Data" section in `DataEditorProps`](https://grid.glideapps.com/docs/interfaces/DataEditorProps.html#columns:~:text=default-,Data,-Readonly) in the Glide Data Grid API documentation.
[^4]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^5]: See ["SheetJS Data Model"](/docs/csf)
[^6]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^7]: ["Addresses and Ranges"](/docs/csf/general) covers general concepts and utility functions including [`decode_range`](/docs/csf/general#cell-ranges-1).
[^8]: See ["Array of Objects Input" in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
[^9]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^10]: See [`writeFile` in "Writing Files"](/docs/api/write-options)

@ -1,5 +1,5 @@
---
title: Lume
title: Illuminating Data with Lume
sidebar_label: Lume
description: Make static websites from spreadsheets using Lume. Seamlessly integrate data into your website using SheetJS. Illuminate data without leaving the comfort of Excel.
pagination_prev: demos/net/index
@ -8,8 +8,6 @@ sidebar_custom_props:
type: native
---
# Illuminating Data with Lume
[Lume](https://lume.land/) is a lightweight unopinionated static site generator.
It has a rich ecosystem of JavaScript-powered plugins[^1]

@ -1,5 +1,7 @@
---
title: NuxtJS
title: Spreadsheets in VueJS Sites with NuxtJS
sidebar_label: NuxtJS
description: Make static websites from spreadsheets using NuxtJS. Seamlessly integrate data into the data layer using SheetJS. Create content without leaving the comfort of Excel.
pagination_prev: demos/net/index
pagination_next: demos/mobile/index
---
@ -7,11 +9,27 @@ pagination_next: demos/mobile/index
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
`@nuxt/content` is a file-based CMS for Nuxt, enabling static-site generation
and on-demand server rendering powered by spreadsheets.
[Nuxt Content](https://content.nuxtjs.org/) is a file-based CMS for NuxtJS,
enabling static-site generation and on-demand server rendering from data files.
The [NodeJS module](/docs/getting-started/installation/nodejs) can be imported
from Content v1 "parsers" and Content v2 "transformers".
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses NuxtJS and SheetJS to pull data from a spreadsheet and display
the content in an HTML table.
:::info pass
There were breaking changes between VueJS 2.x and VueJS 3.x. Since many projects
still use VueJS 2.x, this demo includes examples for both versions of VueJS.
:::
The ["Nuxt Content v1"](#nuxt-content-v1) section explores "parsers" for NuxtJS
Content v1 (paired with VueJS 2.x and NuxtJS 2.x)
The ["Nuxt Content v2"](#nuxt-content-v2) section explores "transformers" for
NuxtJS Content v2 (paired with VueJS 3.x and NuxtJS 3.x)
:::note
@ -19,8 +37,8 @@ The following deployments were tested:
| Nuxt Content | Nuxt | Date |
|:-------------|:---------|:-----------|
| `1.15.1` | `2.16.3` | 2023-06-01 |
| `2.6.0` | `3.5.2` | 2023-06-01 |
| `1.15.1` | `2.17.1` | 2023-08-16 |
| `2.7.2` | `3.6.5` | 2023-08-16 |
:::
@ -57,7 +75,7 @@ telemetry.enabled=false
## Nuxt Content v1
Nuxt Content v1 is designed to work with Nuxt v2.
Nuxt Content v1 is designed to work with NuxtJS v2 and VueJS v2.
The following diagram depicts the workbook waltz:
@ -74,22 +92,59 @@ flowchart LR
aoo --> |index.vue\ntemplate| html
```
### Configuration
### Installation
Through an override in `nuxt.config.js`, Nuxt Content will use custom parsers.
Differences from a stock `create-nuxt-app` config are shown below:
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
imported from `nuxt.config.js` for build-time processing.
```js title="nuxt.config.js"
### Custom Parser
:::info pass
Custom parsers receive the file content interpreted as UTF-8 strings. For binary
formats including XLSX and XLS, the data is corrupt and cannot be used.
The workaround involves safely re-reading the spreadsheets.
:::
The second argument passed to the parser is an object. The `path` property of
the object is the path to the file.
The SheetJS `readFile`[^1] method can locate and parse the spreadsheet files.
The `sheet_to_json`[^2] utility function can generate arrays of row objects for
use in NuxtJS pages:
```js title="Custom Parser (in nuxt.config.js)"
import { readFile, utils } from 'xlsx';
// This will be called when the files change
/* This will be called when the files change */
const parseSheet = (file, { path }) => {
// `path` is a path that can be read with `XLSX.readFile`
/* `path` is a path that can be read with `XLSX.readFile` */
const wb = readFile(path);
const o = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])}));
/* iterate through each worksheet name and generate row objects */
const o = wb.SheetNames.map(name => ({
name: name,
data: utils.sheet_to_json(wb.Sheets[name])
}));
/* The final result must be stored in the `data` key of an object */
return { data: o };
};
```
### Configuration
Using `content.extendParser`[^3], Nuxt Content will use custom parsers.
The property is expected to be an object whose keys are file extensions (with
the `.` before the extension) and whose values are custom parser functions.
The relevant part of the config is shown below. In this snippet, the custom
parser `parseSheet` will be associated with XLSX, XLS and NUMBERS files:
```js title="nuxt.config.js"
export default {
// ...
@ -114,7 +169,7 @@ When a spreadsheet is placed in the `content` folder, Nuxt will find it. The
data can be referenced in a view with `asyncData`. The name should not include
the extension, so `"sheetjs.numbers"` would be referenced as `"sheetjs"`:
```js
```js title="Script section of .vue VueJS page"
async asyncData ({$content}) {
return {
// $content('sheetjs') will match files with extensions in nuxt.config.js
@ -125,9 +180,9 @@ the extension, so `"sheetjs.numbers"` would be referenced as `"sheetjs"`:
In the template, `data.data` is an array of objects. Each object has a `name`
property for the worksheet name and a `data` array of row objects. This maps
neatly with nested `v-for`:
neatly with nested `v-for`[^4]:
```xml
```xml title="Template section of .vue VueJS page"
<!-- loop over the worksheets -->
<div v-for="item in data.data" v-bind:key="item.name">
<table>
@ -143,10 +198,10 @@ neatly with nested `v-for`:
### Nuxt Content Demo
:::caution
:::caution pass
When the demo was last tested, parts of the Nuxt dependency tree did not support
NodeJS version 20. The creation step will show warnings like
For some older versions, parts of the Nuxt dependency tree did not support
NodeJS version 20. `EBADENGINE` warnings were displayed during app creation:
```
npm WARN EBADENGINE Unsupported engine {
@ -247,7 +302,7 @@ Replace the property with the following definition:
5) Replace `pages/index.vue` with the following:
```html
```html title="pages/index.vue"
<!-- sheetjs (C) 2013-present SheetJS -- https://sheetjs.com -->
<template><div>
<div v-for="item in data.data" v-bind:key="item.name">
@ -310,7 +365,7 @@ will not change.
## Nuxt Content v2
Nuxt Content v2 is designed to work with Nuxt v3.
Nuxt Content v2 is designed to work with NuxtJS v3 and VueJS v3.
The following diagram depicts the workbook waltz:
@ -327,14 +382,35 @@ flowchart LR
aoo --> |index.vue\nContentRenderer| html
```
### Overview
### Installation
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
safely imported from `nuxt.config.js` or transformer or module scripts. As long
as the SheetJS modules are not imported in the various `.vue` pages, the library
will not be added to the final page bundle!
### Custom Transformer
Nuxt Content `v2` supports custom transformers for controlling data. Although
the library hard-codes UTF-8 interpretations, the `_id` field currently uses
the pattern `content:` followed by the filename (if files are placed in the
`content` folder directly). This enables a transformer to re-read the file:
`content` folder directly). This enables a transformer to re-read the file.
```ts title="Transformer"
<details><summary><b>Transformer Details</b> (click to show)</summary>
For example, if the file `pres.xlsx` is stored in the `content` folder, NuxtJS
Content will use ID `"content:pres.xlsx"`. `"./content/" + _id.slice(8)` will
be the original path `"./content/pres.xlsx"`.
The NodeJS `resolve`[^5] method will return a file path. `readFileSync`[^6] will
read the file and return a NodeJS `Buffer`. That `Buffer` object can be parsed
with the SheetJS `read`[^7] method. The `sheet_to_json`[^8] utility function can
generate arrays of row objects for use in NuxtJS pages.
</details>
```ts title="sheetformer.ts (Transformer)"
// @ts-ignore
import { defineTransformer } from "@nuxt/content/transformers/utils";
import { read, utils } from "xlsx";
@ -343,6 +419,7 @@ import { resolve } from 'node:path';
export default defineTransformer({
name: 'sheetformer',
/* list of file extensions */
extensions: ['.xlsx'],
parse (_id: string, rawContent: string) {
// highlight-start
@ -352,47 +429,115 @@ export default defineTransformer({
const wb = read(buf);
// highlight-end
/* generate JS objects for each worksheet */
const body = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])}));
const body = wb.SheetNames.map(name => ({
name: name,
data: utils.sheet_to_json(wb.Sheets[name])
}));
/* The final result must have the `_id` key and must store data in `body` */
return { _id, body };
}
});
```
Pages can pull data using `useAsyncData`:
The data object returned by the transformer must have the original `_id` key.
The data is stored in the `body` property of the final object.
```html title="Page"
### Custom Modules
NuxtJS modules are the main mechanism for adding transformers to the pipeline.
<details><summary><b>Module Details</b> (click to show)</summary>
Due to the structure of the NuxtJS system, modules must be defined in separate
script files. The module script is expected to export a module configured with
`defineNuxtModule`. The setup method is expected to do the following:
- Register itself with the "Nitro" subsystem
- Add the transformer to Nuxt Content in the `content:context` hook
```js title="sheetmodule.ts (Module)"
import { resolve } from 'path'
import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
/* module setup method */
setup (_options, nuxt) {
/* register with the nitro subsystem */
nuxt.options.nitro.externals = nuxt.options.nitro.externals || {};
nuxt.options.nitro.externals.inline = nuxt.options.nitro.externals.inline || [];
nuxt.options.nitro.externals.inline.push(resolve('./sheetmodule'));
/* add transformer in the content:context hook */
// @ts-ignore
nuxt.hook('content:context', (contentContext) => {
contentContext.transformers.push(resolve('./sheetformer.ts'));
});
}
});
```
The module must be loaded in `nuxt.config.ts` and added to the `modules` array:
```ts title="nuxt.config.ts"
// highlight-next-line
import SheetJSModule from './sheetmodule'
export default defineNuxtConfig({
// @ts-ignore
telemetry: false,
modules: [
// highlight-next-line
SheetJSModule,
/* it is recommended to load the custom modules before @nuxt/content */
'@nuxt/content'
],
content: {}
});
```
</details>
### Rendering Data
Pages can pull data using `useAsyncData` in the page setup:
```html title="Script section of .vue VueJS page"
<script setup>
const key = "pres"; // matches pres.xlsx
const {data} = await useAsyncData('x', ()=>queryContent(`/${key}`).findOne());
// data.body is the output from the transformer and can be used in the template
const { data } = await useAsyncData('x', ()=>queryContent(`/${key}`).findOne());
</script>
```
Pages should use `ContentRenderer` to reference the data:
Pages should use `ContentRenderer` to reference the data. The `data` variable
from the script setup will be shaped like the return value from the transformer.
`data.body` is an array of objects that holds the worksheet name and data.
```html title="Page"
<template><ContentRenderer :value="data">
<!-- data.body is the array defined in the transformer -->
<div v-for="item in data.body" v-bind:key="item.name">
<!-- each item has a "name" string for worsheet name -->
<h2>{{ item.name }}</h2>
<!-- each item has a "body" array of data rows -->
<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
<tr v-for="row in item.data" v-bind:key="row.Index">
<!-- Assuming the sheet uses the columns "Name" and "Index" -->
<td>{{ row.Name }}</td>
<td>{{ row.Index }}</td>
</tr>
</tbody></table>
</div>
</ContentRenderer></template>
```html title="Template section of .vue VueJS page"
<template>
<ContentRenderer :value="data">
<!-- data.body is the array defined in the transformer -->
<div v-for="item in data.body" v-bind:key="item.name">
<!-- each item has a "name" string for worksheet name -->
<h2>{{ item.name }}</h2>
<!-- each item has a "body" array of data rows -->
<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
<tr v-for="row in item.data" v-bind:key="row.Index">
<!-- Assuming the sheet uses the columns "Name" and "Index" -->
<td>{{ row.Name }}</td>
<td>{{ row.Index }}</td>
</tr>
</tbody></table>
</div>
</ContentRenderer>
</template>
```
### Nuxt Content 2 Demo
:::caution
:::caution pass
When the demo was last tested, parts of the Nuxt dependency tree did not support
For some older versions, parts of the Nuxt dependency tree did not support
NodeJS version 20. If the `yarn install` step fails with a message like
```
@ -434,47 +579,14 @@ The server is listening on that URL. Open the link in a web browser.
curl -L -o content/pres.xlsx https://sheetjs.com/pres.xlsx
```
4) Create the transformer.
4) Create the transformer. Two files must be saved at the root of the project:
Two files must be written at the root of the project:
- [`sheetformer.ts`](https://docs.sheetjs.com/nuxt/3/sheetformer.ts) (raw transformer module)
- [`sheetmodule.ts`](https://docs.sheetjs.com/nuxt/3/sheetmodule.ts) (Nuxt configuration module)
- `sheetformer.ts` (the raw transformer module):
```ts title="sheetformer.ts"
// @ts-ignore
import { defineTransformer } from "@nuxt/content/transformers/utils";
import { read, utils } from "xlsx";
import { readFileSync } from "node:fs";
import { resolve } from 'node:path';
export default defineTransformer({
name: 'sheetformer',
extensions: ['.xlsx'],
parse (_id: string, rawContent: string) {
const wb = read(readFileSync(resolve("./content/" + _id.slice(8))));
const body = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])}));
return { _id, body };
}
});
```
- `sheetmodule.ts` (the Nuxt configuration module):
```ts title="sheetmodule.ts"
import { resolve } from 'path'
import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (_options, nuxt) {
nuxt.options.nitro.externals = nuxt.options.nitro.externals || {}
nuxt.options.nitro.externals.inline = nuxt.options.nitro.externals.inline || []
nuxt.options.nitro.externals.inline.push(resolve('./sheetmodule'))
// @ts-ignore
nuxt.hook('content:context', (contentContext) => {
contentContext.transformers.push(resolve('./sheetformer.ts'))
})
}
})
```bash
curl -O https://docs.sheetjs.com/nuxt/3/sheetformer.ts
curl -O https://docs.sheetjs.com/nuxt/3/sheetmodule.ts
```
After creating the source files, the module must be added to `nuxt.config.ts`:
@ -490,7 +602,7 @@ export default defineNuxtConfig({
'@nuxt/content'
],
content: {}
})
});
```
Restart the dev server by exiting the process (Control+C) and running:
@ -520,25 +632,11 @@ Loading `http://localhost:3000/pres` should show some JSON data:
},
```
5) Create a page. Save the following content to `pages/pres.vue`:
5) Download [`pres.vue`](pathname:///nuxt/3/pres.vue) and save to `pages`:
```html title="pages/pres.vue"
<script setup>
const {data} = await useAsyncData('s5s', () => queryContent('/pres').findOne());
</script>
<template><ContentRenderer :value="data">
<div v-for="item in data.body" v-bind:key="item.name">
<h2>{{ item.name }}</h2>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
<tr v-for="row in item.data" v-bind:key="row.Index">
<td>{{ row.Name }}</td>
<td>{{ row.Index }}</td>
</tr>
</tbody></table>
</div>
</ContentRenderer></template>
```bash
curl -o pages/pres.vue https://docs.sheetjs.com/nuxt/3/pres.vue
```
Restart the dev server by exiting the process (Control+C) and running:
```bash
@ -568,3 +666,12 @@ npx -y http-server .output/public
Accessing `http://localhost:8080/pres` will show the page contents. Verifying
the static nature is trivial: make another change in Excel and save. The page
will not change.
[^1]: See [`readFile` in "Reading Files"](/docs/api/parse-options)
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
[^3]: See [`extendParser`](https://content.nuxtjs.org/v1/getting-started/configuration#extendparser) in the NuxtJS documentation.
[^4]: See ["Array of Objects" in the VueJS demo](/docs/demos/frontend/vue#array-of-objects)
[^5]: See [`resolve`](https://nodejs.org/api/path.html#pathresolvepaths) in the NodeJS `node:path` documentation
[^6]: See [`readFileSync`](https://nodejs.org/api/fs.html#fsreadfilesyncpath-options) in the NodeJS `node:fs` documentation
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^8]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)

@ -177,7 +177,7 @@ XLSX.writeFile(wb, "textport.numbers", {numbers: XLSX_ZAHL_PAYLOAD, compression:
After installing the package:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
bun i https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
The scripts will be available at `xlsx/dist/xlsx.zahl` (CommonJS) and
@ -226,33 +226,33 @@ XLSX.writeFile(wb, "textport.numbers", {numbers: XLSX_ZAHL_PAYLOAD, compression:
For broad compatibility with third-party tools, this library supports many
output formats. The specific file type is controlled with `bookType` option:
| `bookType` | file ext | container | sheets | Description |
| :--------- | -------: | :-------: | :----- |:------------------------------- |
| `xlsx` | `.xlsx` | ZIP | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format |
| `biff8` | `.xls` | CFB | multi | Excel 97-2004 Workbook Format |
| `biff5` | `.xls` | CFB | multi | Excel 5.0/95 Workbook Format |
| `biff4` | `.xls` | none | single | Excel 4.0 Worksheet Format |
| `biff3` | `.xls` | none | single | Excel 3.0 Worksheet Format |
| `biff2` | `.xls` | none | single | Excel 2.0 Worksheet Format |
| `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) |
| `numbers` |`.numbers`| ZIP | single | Numbers 3.0+ Spreadsheet |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | none | multi | Flat OpenDocument Spreadsheet |
| `wk3` | `.wk3` | none | multi | Lotus Workbook (WK3) |
| `csv` | `.csv` | none | single | Comma Separated Values |
| `txt` | `.txt` | none | single | UTF-16 Unicode Text (TXT) |
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `html` | `.html` | none | single | HTML Document |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
| `dbf` | `.dbf` | none | single | dBASE II + VFP Extensions (DBF) |
| `wk1` | `.wk1` | none | single | Lotus Worksheet (WK1) |
| `rtf` | `.rtf` | none | single | Rich Text Format (RTF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text |
| `eth` | `.eth` | none | single | Ethercalc Record Format (ETH) |
| `bookType` | extension | sheets | Description |
|:-----------|:-----------|:-------|:--------------------------------|
| `xlsx` | `.xlsx` | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | multi | Excel 2007+ Binary Format |
| `biff8` | `.xls` | multi | Excel 97-2004 Workbook Format |
| `biff5` | `.xls` | multi | Excel 5.0/95 Workbook Format |
| `biff4` | `.xls` | single | Excel 4.0 Worksheet Format |
| `biff3` | `.xls` | single | Excel 3.0 Worksheet Format |
| `biff2` | `.xls` | single | Excel 2.0 Worksheet Format |
| `xlml` | `.xls` | multi | Excel 2003-2004 (SpreadsheetML) |
| `numbers` | `.numbers` | multi | Numbers 3.0+ Spreadsheet |
| `ods` | `.ods` | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | multi | Flat OpenDocument Spreadsheet |
| `wk3` | `.wk3` | multi | Lotus Workbook (WK3) |
| `csv` | `.csv` | single | Comma Separated Values |
| `txt` | `.txt` | single | UTF-16 Unicode Text (TXT) |
| `sylk` | `.sylk` | single | Symbolic Link (SYLK) |
| `html` | `.html` | single | HTML Document |
| `dif` | `.dif` | single | Data Interchange Format (DIF) |
| `dbf` | `.dbf` | single | dBASE II + VFP Extensions (DBF) |
| `wk1` | `.wk1` | single | Lotus Worksheet (WK1) |
| `rtf` | `.rtf` | single | Rich Text Format (RTF) |
| `prn` | `.prn` | single | Lotus Formatted Text |
| `eth` | `.eth` | single | Ethercalc Record Format (ETH) |
- `compression` only applies to formats with ZIP containers.
- `compression` applies to ZIP-based formats (XLSX, XLSM, XLSB, NUMBERS, ODS)
- Formats that only support a single sheet require a `sheet` option specifying
the worksheet. If the string is empty, the first worksheet is used.
- `writeFile` will automatically guess the output file format based on the file

18979
docz/static/dojo/dojo.js vendored Normal file

File diff suppressed because it is too large Load Diff

@ -1,6 +1,9 @@
<!DOCTYPE html>
<html>
<head><title>SheetJS + Dojo Read Demo</title></head>
<head>
<meta name="robots" content="noindex">
<title>SheetJS + Dojo Read Demo</title>
</head>
<body>
<h1>SheetJS + Dojo Read Demo</h1>
@ -13,16 +16,14 @@ dojoConfig = { packages: [
{ name: "xlsx", location: "https://cdn.sheetjs.com/xlsx-latest/package/dist", main: "xlsx.full.min" }
] };
</script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js" data-dojo-config="isDebug:1, async:1"></script>
<script src="dojo.js" data-dojo-config="isDebug:1, async:1"></script>
<script>
require(["dojo/request/xhr", "xlsx"], function(xhr, _XLSX) {
xhr("https://sheetjs.com/pres.numbers", {
headers: { "X-Requested-With": null },
// highlight-next-line
handleAs: "arraybuffer"
}).then(function(ab) {
/* read ArrayBuffer */
// highlight-next-line
var wb = XLSX.read(ab);
/* display first worksheet data */
var ws = wb.Sheets[wb.SheetNames[0]];

@ -1,6 +1,9 @@
<!DOCTYPE html>
<html>
<head><title>SheetJS + Dojo Write Demo</title></head>
<head>
<meta name="robots" content="noindex">
<title>SheetJS + Dojo Write Demo</title>
</head>
<body>
<h1>SheetJS + Dojo Write Demo</h1>
@ -11,7 +14,7 @@ dojoConfig = { packages: [
{ name: "xlsx", location: "https://cdn.sheetjs.com/xlsx-latest/package/dist", main: "xlsx.full.min" }
] };
</script>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js" data-dojo-config="isDebug:1, async:1"></script>
<script src="dojo.js" data-dojo-config="isDebug:1, async:1"></script>
<script>
require(["dojo/request/xhr", "xlsx"], function(xhr, _XLSX) {
xhr("https://sheetjs.com/data/executive.json", {

BIN
docz/static/gdg/gdg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

@ -0,0 +1,11 @@
import SheetJSModule from './sheetmodule'
export default defineNuxtConfig({
// @ts-ignore
telemetry: false,
modules: [
SheetJSModule,
'@nuxt/content'
],
content: {}
});

@ -0,0 +1,14 @@
<script setup>
const {data} = await useAsyncData('s5s', () => queryContent('/pres').findOne());
</script>
<template><ContentRenderer :value="data">
<div v-for="item in data.body" v-bind:key="item.name">
<h2>{{ item.name }}</h2>
<table><thead><tr><th>Name</th><th>Index</th></tr></thead><tbody>
<tr v-for="row in item.data" v-bind:key="row.Index">
<td>{{ row.Name }}</td>
<td>{{ row.Index }}</td>
</tr>
</tbody></table>
</div>
</ContentRenderer></template>

@ -0,0 +1,15 @@
// @ts-ignore
import { defineTransformer } from "@nuxt/content/transformers/utils";
import { read, utils } from "xlsx";
import { readFileSync } from "node:fs";
import { resolve } from 'node:path';
export default defineTransformer({
name: 'sheetformer',
extensions: ['.xlsx'],
parse (_id: string, rawContent: string) {
const wb = read(readFileSync(resolve("./content/" + _id.slice(8))));
const body = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])}));
return { _id, body };
}
});

@ -0,0 +1,14 @@
import { resolve } from 'path'
import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (_options, nuxt) {
nuxt.options.nitro.externals = nuxt.options.nitro.externals || {}
nuxt.options.nitro.externals.inline = nuxt.options.nitro.externals.inline || []
nuxt.options.nitro.externals.inline.push(resolve('./sheetmodule'))
// @ts-ignore
nuxt.hook('content:context', (contentContext) => {
contentContext.transformers.push(resolve('./sheetformer.ts'))
})
}
});