diff --git a/docz/docs/02-getting-started/01-installation/04-nodejs.md b/docz/docs/02-getting-started/01-installation/03-nodejs.md similarity index 99% rename from docz/docs/02-getting-started/01-installation/04-nodejs.md rename to docz/docs/02-getting-started/01-installation/03-nodejs.md index b1a79fa..59243e6 100644 --- a/docz/docs/02-getting-started/01-installation/04-nodejs.md +++ b/docz/docs/02-getting-started/01-installation/03-nodejs.md @@ -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 --- diff --git a/docz/docs/02-getting-started/01-installation/06-amd.md b/docz/docs/02-getting-started/01-installation/04-amd.md similarity index 99% rename from docz/docs/02-getting-started/01-installation/06-amd.md rename to docz/docs/02-getting-started/01-installation/04-amd.md index 51cf7ac..f6b3a88 100644 --- a/docz/docs/02-getting-started/01-installation/06-amd.md +++ b/docz/docs/02-getting-started/01-installation/04-amd.md @@ -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 --- diff --git a/docz/docs/02-getting-started/01-installation/03-deno.md b/docz/docs/02-getting-started/01-installation/06-deno.md similarity index 98% rename from docz/docs/02-getting-started/01-installation/03-deno.md rename to docz/docs/02-getting-started/01-installation/06-deno.md index b5ef774..725b35a 100644 --- a/docz/docs/02-getting-started/01-installation/03-deno.md +++ b/docz/docs/02-getting-started/01-installation/06-deno.md @@ -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 --- diff --git a/docz/docs/03-demos/03-desktop.md b/docz/docs/03-demos/03-desktop.md index 062b041..32519ea 100644 --- a/docz/docs/03-demos/03-desktop.md +++ b/docz/docs/03-demos/03-desktop.md @@ -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) + +
Complete Example (click to show) + +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! + +
+ +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 +} +``` diff --git a/docz/docs/03-demos/13-vue.md b/docz/docs/03-demos/13-vue.md index 4e724f3..b5237eb 100644 --- a/docz/docs/03-demos/13-vue.md +++ b/docz/docs/03-demos/13-vue.md @@ -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) diff --git a/docz/docs/03-demos/14-svelte.md b/docz/docs/03-demos/14-svelte.md index 421b973..14b5b90 100644 --- a/docz/docs/03-demos/14-svelte.md +++ b/docz/docs/03-demos/14-svelte.md @@ -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 diff --git a/docz/docs/03-demos/index.md b/docz/docs/03-demos/index.md index b41ccdc..d34e837 100644 --- a/docz/docs/03-demos/index.md +++ b/docz/docs/03-demos/index.md @@ -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) diff --git a/docz/static/wails/App.svelte b/docz/static/wails/App.svelte new file mode 100644 index 0000000..8465bd8 --- /dev/null +++ b/docz/static/wails/App.svelte @@ -0,0 +1,127 @@ + + +
+ +
SheetJS × Wails {version}
+ + +
{@html html}
+
+ + diff --git a/docz/static/wails/app.go b/docz/static/wails/app.go new file mode 100644 index 0000000..de98d73 --- /dev/null +++ b/docz/static/wails/app.go @@ -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) +} diff --git a/docz/static/wails/macos.png b/docz/static/wails/macos.png new file mode 100644 index 0000000..6611550 Binary files /dev/null and b/docz/static/wails/macos.png differ