This commit is contained in:
SheetJS 2022-08-31 02:46:03 -04:00
parent 62bba8b309
commit 28b752eafc
10 changed files with 455 additions and 5 deletions

@ -1,7 +1,7 @@
---
pagination_prev: getting-started/index
pagination_next: getting-started/example
sidebar_position: 4
sidebar_position: 3
sidebar_custom_props:
summary: Server-side and other frameworks using NodeJS modules
---

@ -1,7 +1,7 @@
---
pagination_prev: getting-started/index
pagination_next: getting-started/example
sidebar_position: 6
sidebar_position: 4
sidebar_custom_props:
summary: NetSuite, SAP UI5, RequireJS
---

@ -1,7 +1,7 @@
---
pagination_prev: getting-started/index
pagination_next: getting-started/example
sidebar_position: 3
sidebar_position: 6
sidebar_custom_props:
summary: Import ECMAScript Modules and TypeScript definitions
---

@ -549,3 +549,215 @@ async function saveFile() {
await writeBinaryFile(selected, d);
}
```
## Wails
The [NodeJS Module](../getting-started/installation/nodejs) can be imported
from JavaScript code.
This demo was tested against Wails `v2.0.0-beta.44.2` on 2022 August 31 using
the Svelte TypeScript starter.
:::caution
Wails currently does not provide the equivalent of NodeJS `fs` module.
The HTML File Input Element does not show a file picker. This is a known bug.
All raw file operations must be performed in Go code.
:::
The "Complete Example" creates an app that looks like the screenshot:
![SheetJS Wails MacOS screenshot](pathname:///wails/macos.png)
<details><summary><b>Complete Example</b> (click to show)</summary>
0) [Read Wails "Getting Started" guide and install dependencies.](https://wails.io/docs/gettingstarted/installation)
1) Create a new Wails app:
```bash
wails init -n sheetjs-wails -t svelte-ts
```
2) Enter the directory:
```bash
cd sheetjs-wails
```
3) Install front-end dependencies:
```bash
cd frontend
curl -L -o src/assets/logo.png https://sheetjs.com/sketch1024.png
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
cd ..
```
4) Download source files:
- Download [`app.go`](pathname:///wails/app.go) and replace `app.go`
- Download [`App.svelte`](pathname:///wails/App.svelte) and replace
`frontend/src/App.svelte`
5) Build the app with
```bash
wails build
```
At the end, it will print the path to the generated program. Run the program!
</details>
All operations must be run from Go code. This example passes Base64 strings.
### Reading Files
The file picker and reading operations can be combined in one Go function.
#### Go
```go
import (
"context"
// highlight-start
"encoding/base64"
"io/ioutil"
"github.com/wailsapp/wails/v2/pkg/runtime"
// highlight-end
)
type App struct {
ctx context.Context
}
// ReadFile shows an open file dialog and returns the data as Base64 string
func (a *App) ReadFile() string {
// highlight-next-line
selection, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
Title: "Select File",
Filters: []runtime.FileFilter{
{ DisplayName: "Excel Workbooks (*.xlsx)", Pattern: "*.xlsx", },
// ... more filters for more file types
},
})
if err != nil { return "" } // The demo app shows an error message
// highlight-next-line
data, err := ioutil.ReadFile(selection)
if err != nil { return "" } // The demo app shows an error message
// highlight-next-line
return base64.StdEncoding.EncodeToString(data)
}
```
#### JS
Wails will automatically create `window.go.main.App.ReadFile` for use in JS:
```js title="frontend/src/App.svelte"
import { read, utils } from 'xlsx';
async function importFile(evt) {
// highlight-start
const b64 = window['go']['main']['App']['ReadFile']();
const wb = read(b64, { type: "base64" });
// highlight-end
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
html = utils.sheet_to_html(ws); // generate HTML and update state
}
```
### Writing Files
There is a multi-part dance since the library needs the file extension.
1) Show the save file picker in Go, pass back to JS
2) Generate the file data in JS, pass the data back to Go
3) Write to file in Go
##### Go
Two Go functions will be exposed.
- `SaveFile` will show the file picker and return the path:
```go
import (
"context"
// highlight-start
"github.com/wailsapp/wails/v2/pkg/runtime"
// highlight-end
)
type App struct {
ctx context.Context
}
func (a *App) SaveFile() string {
// highlight-next-line
selection, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
Title: "Select File",
DefaultFilename: "SheetJSWails.xlsx",
Filters: []runtime.FileFilter{
{ DisplayName: "Excel Workbooks (*.xlsx)", Pattern: "*.xlsx", },
// ... more filters for more file types
},
})
if err != nil { return "" } // The demo app shows an error message
return selection
}
```
- `WriteFile` performs the file write given a Base64 string and file path:
```go
import (
"context"
// highlight-start
"encoding/base64"
"io/ioutil"
// highlight-end
)
type App struct {
ctx context.Context
}
func (a *App) WriteFile(b64 string, path string) {
// highlight-start
buf, _ := base64.StdEncoding.DecodeString(b64);
_ = ioutil.WriteFile(path, buf, 0644);
// highlight-end
}
```
#### JS
Wails will automatically create bindings for use in JS:
```js
import { utils, write } from 'xlsx';
async function exportFile(wb) {
/* generate workbook */
const elt = tbl.getElementsByTagName("TABLE")[0];
const wb = utils.table_to_book(elt);
/* show save picker and get path */
const path = await window['go']['main']['App']['SaveFile']();
/* generate base64 string based on the path */
const b64 = write(wb, { bookType: path.slice(path.lastIndexOf(".")+1), type: "base64" });
/* write to file */
await window['go']['main']['App']['WriteFile'](b64, path);
// The demo shows a success message at this point
}
```

@ -11,6 +11,7 @@ Other demos cover general VueJS deployments, including:
- [Static Site Generation powered by NuxtJS](./content#nuxtjs)
- [iOS and Android applications powered by Quasar](./mobile#quasar)
- [Desktop application powered by Tauri](./desktop#tauri)
- [`vue3-table-lite` UI component](./grid#vue3-table-lite)

@ -7,9 +7,10 @@ title: Svelte
This demo tries to cover common Svelte data flow ideas and strategies. Svelte
familiarity is assumed.
Other demos cover general React deployments, including:
Other demos cover general Svelte deployments, including:
- [iOS applications powered by CapacitorJS](./mobile#capacitorjs)
- [Desktop application powered by Wails](./desktop#wails)
## Installation

@ -25,7 +25,7 @@ run in the web browser, demos will include interactive examples.
- [`Svelte`](./svelte)
- [`VueJS`](./vue)
- [`Angular.JS`](./legacy#angularjs)
- [`Dojo`](./legacy#dojo)
- [`Dojo`](./legacy#dojo-toolkit)
- [`Knockout`](./legacy#knockout)
### Front-End UI Components
@ -46,6 +46,7 @@ run in the web browser, demos will include interactive examples.
- [`Electron`](./desktop#electron)
- [`NW.js`](./desktop#nwjs)
- [`Tauri`](./desktop#tauri)
- [`Wails`](./desktop#wails)
- [`Chrome and Chromium Extensions`](./chromium)
- [`Google Sheets API`](./gsheet)
- [`ExtendScript for Adobe Apps`](./extendscript)

@ -0,0 +1,127 @@
<script lang="ts">
import logo from './assets/logo.png'
import { onMount } from 'svelte';
import { read, utils, write, version } from 'xlsx';
async function writeFile(wb) {
const path = await window['go']['main']['App']['SaveFile']();
const b64 = write(wb, { bookType: path.slice(path.lastIndexOf(".")+1), type: "base64" });
await window['go']['main']['App']['WriteFile'](b64, path);
window['go']['main']['App']['ShowInfo']("Saved File", path);
}
async function readFile() {
const res = await window['go']['main']['App']['ReadFile']();
if(res.length == 0) throw "failed";
return res;
}
async function err(body, title = "") {
return window['go']['main']['App']['ShowError'](title, typeof body == "string" ? body : body.message);
}
let html = "";
let tbl;
/* Fetch and update the state once */
onMount(async() => {
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
const wb = read(f); // parse the array buffer
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
// highlight-start
html = utils.sheet_to_html(ws); // generate HTML and update state
// highlight-end
});
/* get state data and export to XLSX */
function exportFile() {
const elt = tbl.getElementsByTagName("TABLE")[0];
const wb = utils.table_to_book(elt);
try { writeFile(wb); } catch(e) { err(e); }
}
/* show file picker, read file, load table */
async function importFile(evt) {
try {
const b64 = await readFile();
const wb = read(b64, { type: "base64" });
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
html = utils.sheet_to_html(ws); // generate HTML and update state
} catch(e) { err(e); }
}
</script>
<main>
<img alt="Wails logo" id="logo" src="{logo}">
<div class="result" id="result">SheetJS × Wails {version}</div>
<button on:click={importFile}>Import File</button>
<button on:click={exportFile}>Export XLSX</button>
<div bind:this={tbl} class="ctr">{@html html}</div>
</main>
<style>
#logo {
display: block;
width: 25%;
height: 25%;
margin: auto;
padding: 10% 0 0;
background-position: center;
background-repeat: no-repeat;
background-size: 100% 100%;
background-origin: content-box;
}
.result {
height: 24px;
line-height: 24px;
font-size: 24px;
font-weight: bold;
margin: 1.5rem auto;
}
.ctr {
margin-left: auto;
margin-right: auto;
}
.input-box .btn {
width: 60px;
height: 30px;
line-height: 30px;
border-radius: 3px;
border: none;
margin: 0 0 0 20px;
padding: 0 8px;
cursor: pointer;
}
.input-box .btn:hover {
background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
color: #333333;
}
.input-box .input {
border: none;
border-radius: 3px;
outline: none;
height: 30px;
line-height: 30px;
padding: 0 10px;
background-color: rgba(240, 240, 240, 1);
-webkit-font-smoothing: antialiased;
}
.input-box .input:hover {
border: none;
background-color: rgba(255, 255, 255, 1);
}
.input-box .input:focus {
border: none;
background-color: rgba(255, 255, 255, 1);
}
</style>

108
docz/static/wails/app.go Normal file

@ -0,0 +1,108 @@
package main
import (
"context"
"encoding/base64"
"fmt"
"github.com/wailsapp/wails/v2/pkg/runtime"
"io/ioutil"
)
// App struct
type App struct {
ctx context.Context
}
// NewApp creates a new App application struct
func NewApp() *App {
return &App{}
}
// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
// Greet returns a greeting for the given name
func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s, It's show time!", name)
}
// ShowError displays an error message in a desktop dialog.
func (a *App) ShowError(title string, message string) {
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: title,
Message: message,
})
}
// ShowInfo displays an info message in a desktop dialog.
func (a *App) ShowInfo(title string, message string) {
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: title,
Message: message,
})
}
// ReadFile shows an open file dialog and returns the data as Base64 string
func (a *App) ReadFile() string {
selection, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
Title: "Select File",
Filters: []runtime.FileFilter{
{DisplayName: "Excel '97-2004 Workbooks (*.xls)", Pattern: "*.xls"},
{DisplayName: "Excel Workbooks (*.xlsx)", Pattern: "*.xlsx"},
{DisplayName: "Excel Binary Workbooks (*.xlsb)", Pattern: "*.xlsb"},
{DisplayName: "Numbers Spreadsheets (*.numbers)", Pattern: "*.numbers"},
},
})
if err != nil {
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Selection Error",
Message: err.Error(),
})
return ""
}
data, err := ioutil.ReadFile(selection)
if err != nil {
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Read Error",
Message: err.Error(),
})
return ""
}
return base64.StdEncoding.EncodeToString(data)
}
// SaveFile shows a save file dialog and returns the path
func (a *App) SaveFile() string {
selection, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
Title: "Select File",
DefaultFilename: "SheetJSWails.xlsx",
Filters: []runtime.FileFilter{
{DisplayName: "Excel '97-2004 Workbooks (*.xls)", Pattern: "*.xls"},
{DisplayName: "Excel Workbooks (*.xlsx)", Pattern: "*.xlsx"},
{DisplayName: "Excel Binary Workbooks (*.xlsb)", Pattern: "*.xlsb"},
{DisplayName: "Numbers Spreadsheets (*.numbers)", Pattern: "*.numbers"},
},
})
if err != nil {
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Selection Error",
Message: err.Error(),
})
return ""
}
return selection
}
// WriteFile decodes the first argument and writes to file
func (a *App) WriteFile(b64 string, path string) {
buf, _ := base64.StdEncoding.DecodeString(b64)
_ = ioutil.WriteFile(path, buf, 0644)
}

BIN
docz/static/wails/macos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB