mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-09 20:09:57 -06:00
Web: page host mode, so a page can host GridCraft in its own document (#2592)
data-host="page" on the canvas opts a page that loads the module into its own document into the host bridge. The ?host=parent messages travel as non-bubbling CustomEvents on the canvas; Bridge holds a Transport (Parent or Page) and shares the save, dirty and ready logic. Unframed ?host=parent still gets no bridge. Hosting guide documents both modes.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# Hosting GridCraft for the web
|
||||
|
||||
|
||||
`gridcraft-web-<version>.zip` (from the GitHub release, or `packaging/web/package.sh`) holds a
|
||||
static site in `gridcraft-web-<version>/`:
|
||||
|
||||
@@ -77,6 +78,77 @@ Local test: `python3 -m http.server 8765` inside the folder, then open http://lo
|
||||
one. Use them when the host window already shows the file name and owns the files, so the sheet's
|
||||
own Open or Save As cannot replace what the host thinks is open. Everything stays on by default.
|
||||
|
||||
### The parent bridge (`?host=parent`)
|
||||
|
||||
A page framing GridCraft in a **same-origin** iframe can add `?host=parent` to the iframe's URL
|
||||
(`https://example.com/gridcraft/?host=parent`) and have GridCraft hand its files over instead of
|
||||
downloading them. The two sides talk `postMessage` with plain window messages that carry a
|
||||
`type`:
|
||||
|
||||
| Message | Direction | Fields |
|
||||
|---|---|---|
|
||||
| `gridcraft:ready` | app → page | `version`: the app is listening; send a file now |
|
||||
| `gridcraft:open` | page → app | `name`, `bytes` (an `ArrayBuffer` or `Uint8Array`): open like File › Open |
|
||||
| `gridcraft:save` | app → page | `name`, `bytes` (a transferred `ArrayBuffer`): every file the app would otherwise download (Save, Save As, Export as PDF). The page stores it and reports failures itself. |
|
||||
| `gridcraft:dirty` | app → page | `dirty`: unsaved changes appeared or went away |
|
||||
| `gridcraft:failed` | app → page | `error`: the app couldn't start |
|
||||
|
||||
In host mode the app starts with an empty workbook instead of a sample, so the framing page
|
||||
should guard leaving the page while changes are unsaved. `&author=` names the user for the
|
||||
notes and comments the app creates. Opening `?host=parent` outside an iframe gives the normal
|
||||
app with downloads.
|
||||
|
||||
### Embedding without an iframe (page host mode)
|
||||
|
||||
A page that loads the GridCraft module into its own document — its own `<canvas
|
||||
id="gridcraft_canvas">` inside a component, sized to a box beside the page's own header and
|
||||
controls — opts in with `data-host="page"` on that canvas. The behaviour is the same as the
|
||||
parent bridge, except the transport is DOM `CustomEvent`s on the canvas instead of
|
||||
`postMessage`: the app dispatches `gridcraft:ready`, `gridcraft:save`, `gridcraft:dirty` and
|
||||
`gridcraft:failed` on the canvas with the message fields above as the event's `detail`, and the
|
||||
page dispatches `gridcraft:open` on the canvas with `detail: { name, bytes }` to open a
|
||||
workbook. Events don't bubble, so two editors on one page can't hear each other; listen on the
|
||||
canvas itself.
|
||||
|
||||
```html
|
||||
<canvas id="gridcraft_canvas" data-host="page" style="width: 100%; height: 600px; border: 0;"></canvas>
|
||||
```
|
||||
|
||||
```js
|
||||
import init from "./gridcraft-web-<hash>.js"; // the wasm-bindgen glue shipped with the app
|
||||
|
||||
// Listen first: the app announces itself while `init` runs.
|
||||
const canvas = document.getElementById("gridcraft_canvas");
|
||||
|
||||
canvas.addEventListener("gridcraft:ready", async () => {
|
||||
// Open a workbook of the page's own, as with File › Open.
|
||||
const bytes = await workbookBytes(); // an ArrayBuffer or Uint8Array
|
||||
canvas.dispatchEvent(new CustomEvent("gridcraft:open", {
|
||||
detail: { name: "Budget.xlsx", bytes },
|
||||
}));
|
||||
});
|
||||
|
||||
// Every file GridCraft would otherwise download (Save, Save As, Export as PDF) arrives here.
|
||||
canvas.addEventListener("gridcraft:save", (event) => {
|
||||
const { name, bytes } = event.detail; // bytes is an ArrayBuffer
|
||||
storeFile(name, bytes);
|
||||
});
|
||||
|
||||
canvas.addEventListener("gridcraft:dirty", (event) => {
|
||||
warnBeforeLeaving(event.detail.dirty);
|
||||
});
|
||||
canvas.addEventListener("gridcraft:failed", (event) => {
|
||||
showError(event.detail.error);
|
||||
});
|
||||
|
||||
await init({ module_or_path: "./gridcraft-web-<hash>_bg.wasm" });
|
||||
```
|
||||
|
||||
As with the parent bridge, the app starts with an empty workbook instead of a sample, and
|
||||
`?author=` on the page's URL names the user for new notes and comments. The `data-host` value
|
||||
must be exactly `page`; anything else is ignored, and the app runs as a standalone page with
|
||||
downloads.
|
||||
|
||||
## Renderer selection
|
||||
|
||||
GridCraft's web build is the same egui app as the desktop one, compiled to wasm
|
||||
|
||||
Reference in New Issue
Block a user