wails
This commit is contained in:
parent
62bba8b309
commit
28b752eafc
@ -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)
|
||||
|
127
docz/static/wails/App.svelte
Normal file
127
docz/static/wails/App.svelte
Normal file
@ -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
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
BIN
docz/static/wails/macos.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 172 KiB |
Loading…
Reference in New Issue
Block a user