forked from sheetjs/docs.sheetjs.com
		
	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: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| <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