This project is part of the @thi.ng/umbrella monorepo.
Browser DOM bridge API for hybrid TypeScript & Zig applications. This is a support package for @thi.ng/wasm-api.
This package provides a minimal, but already quite usable TypeScript core API and related Ziglang bindings for UI & DOM creation/manipulation via WebAssembly.
Current key features for the Zig (WASM) side:
- ID handle management for WASM created DOM elements & listeners
- Declarative & imperative DOM tree creation
- Canvas element creation (with HDPI support, see @thi.ng/adapt-dpi)
- Attribute setters/getters (string, numeric, boolean)
.innerHTML
&.innerText
setters- Event handlers, event types (see generated types in api.zig for details):
- drag 'n drop (WIP)
- focus
- input
- key
- mouse
- pointer
- scroll
- touch
- wheel
- Fullscreen API wrapper
- (Browser) window info queries
Since DOM related resources created on the JS side cannot be returned to the WASM module directly, the bridge API caches those on the host side and uses managed ID (integer) handles to exchange them. These IDs can then be used in subsequent API calls to refer to certain DOM elements, listeners etc.
For element & event related functionality the following IDs are reserved:
-1
the browserwindow
itself0
document.head
1
document.body
All are exposed in the Zig module as window
, head
, body
constants to help
avoiding magic numbers in userland code.
Single DOM elements and entire element trees can be created via the
createElement()
function:
const dom = @import("wasmdom");
const handle = dom.createElement(&.{
// element tag
.tag = "div",
// CSS classes
.class = "bg-red white",
// parent element ID (here `document.body`)
.parent = dom.body,
// optional child element specs
.children = &.{
.{
.tag = "h1",
// text content for this element
.text = "OMG, it works!",
// nested childen
.children = &.{
.{
.tag = "span",
.text = "(recursive DOM creation FTW!)",
.class = "bg-yellow black",
},
},
},
},
});
The CreateElementOpts struct has some additional options and more are planned. All WIP!
Once a DOM element has been created, event listeners can be attached to it. All
listeners take two arguments: an Event
struct and an optional opaque pointer
for passing arbitrary user context.
This small Zig example defines a click counter button component:
const wasm = @import("wasmapi");
const dom = @import("wasmdom");
/// Simple click counter component
const Counter = struct {
/// element ID
el: i32,
/// listener ID
listener: u16,
/// click counter
clicks: usize,
const Self = @This();
/// Create & setup DOM element w/ listener
pub fn init(self: *Self, parent: i32) !void {
self.clicks = 0;
// create DOM button element
self.el = dom.createElement(&.{
.tag = "button",
.text = "click me!",
.parent = parent,
});
// add click event listener w/ user context arg
self.listener = try dom.addListener(self.el, "click", &.{
.callback = onClick,
.ctx = self,
});
}
/// Event listener & state update
fn onClick(_: *const dom.Event, raw: ?*anyopaque) void {
// safely cast raw pointer
if (wasm.ptrCast(*Self, raw)) |self| {
self.clicks += 1;
// format new button label
var buf: [16]u8 = undefined;
var label = std.fmt.bufPrint(&buf, "clicks: {d}", .{self.clicks}) catch return;
// update DOM element
dom.setInnerText(self.el, label);
}
}
};
var counter: Counter = undefined;
export fn start() void {
// instantiate counter
counter.init(dom.body) catch |e| @panic(@errorName(e));
}
ALPHA - bleeding edge / work-in-progress
Search or submit any issues for this package
yarn add @thi.ng/wasm-api-dom
ES module import:
<script type="module" src="https://cdn.skypack.dev/@thi.ng/wasm-api-dom"></script>
For Node.js REPL:
# with flag only for < v16
node --experimental-repl-await
> const wasmApiDom = await import("@thi.ng/wasm-api-dom");
Package sizes (gzipped, pre-treeshake): ESM: 3.88 KB
Several demos in this repo's /examples directory are using this package.
A selection:
Screenshot | Description | Live demo | Source |
---|---|---|---|
Zig-based DOM creation & canvas drawing app | Demo | Source |
For now, please see the package docs, source code comments (TS & Zig) and the various comments in the zig-canvas example project for further reference and usage patterns! Thank you!
- Karsten Schmidt (@postspectacular)
- Marcus Wågberg (@MarcusWagberg)
If this project contributes to an academic publication, please cite it as:
@misc{thing-wasm-api-dom,
title = "@thi.ng/wasm-api-dom",
author = "Karsten Schmidt and others",
note = "https://thi.ng/wasm-api-dom",
year = 2022
}
© 2022 Karsten Schmidt // Apache Software License 2.0