@thi.ng/wasm-api-dom
Advanced tools
Comparing version 0.9.0 to 0.10.0
83
api.d.ts
import type { WasmExports } from "@thi.ng/wasm-api"; | ||
export * from "./generated/api.js"; | ||
export interface DOMExports extends WasmExports { | ||
dom_callListener(listenerID: number, event: number): void; | ||
dom_callRAF(rafID: number, t: number): void; | ||
dom_fullscreenChanged(): void; | ||
_dom_init(): void; | ||
_dom_callListener(listenerID: number, event: number): void; | ||
_dom_callRAF(rafID: number, t: number): void; | ||
_dom_fullscreenChanged(): void; | ||
_dom_addListener(listenerAddr: number): number; | ||
_dom_removeListener(listenerID: number): void; | ||
} | ||
@@ -17,2 +20,11 @@ export interface DOMImports extends WebAssembly.ModuleImports { | ||
/** | ||
* Similar to global `document.getElementById()`, but returning ID handle of | ||
* found indexed element, or -1 if none could be found. If the DOM element | ||
* exists, but isn't yet managed/indexed, it will be added to the index and | ||
* its ID returned. | ||
* | ||
* @param nameAddr | ||
*/ | ||
getElementByID(nameAddr: number): number; | ||
/** | ||
* Takes a {@link CreateElementOpts} pointer and creates a new DOM element | ||
@@ -64,5 +76,19 @@ * according to the given opts. The created element is indexed and a unique | ||
/** | ||
* Sets `.innerHTML` property of a DOM element to provided `body` string. | ||
* | ||
* @param elementID | ||
* @param body | ||
*/ | ||
setInnerHtml(elementID: number, body: number): void; | ||
/** | ||
* Sets `.innerText` property of a DOM element to provided `body` string. | ||
* | ||
* @param elementID | ||
* @param body | ||
*/ | ||
setInnerText(elementID: number, body: number): void; | ||
/** | ||
* Sets attribute for given element to new string value. Both `nameAddr` and | ||
* `valAddr` are zero-terminated char pointers (or standard Zig `[]u8` / | ||
* `[]const u8` slices). | ||
* `valAddr` are pointers to zero-terminated u8 arrays (or standard Zig | ||
* string literals). | ||
* | ||
@@ -73,7 +99,8 @@ * @param elementID | ||
*/ | ||
_setStringAttrib(elementID: number, nameAddr: number, valAddr: number): void; | ||
setStringAttrib(elementID: number, nameAddr: number, valAddr: number): void; | ||
/** | ||
* Reads a string attribute value from DOM element and writes it | ||
* zero-terminated to char pointer `valAddr`. Only `maxBytes` are written. | ||
* Returns actual number of bytes written (excluding the sentinel). | ||
* Reads a string attribute value from DOM element, encodes it as UTF-8 and | ||
* writes zero-terminated bytes to char pointer `valAddr`. Only `maxBytes` | ||
* are written (incl. any sentinel). Returns actual number of bytes written | ||
* (excluding the sentinel). | ||
* | ||
@@ -87,5 +114,15 @@ * @param elementID | ||
/** | ||
* Similar to {@link DOMImports._getStringAttrib}, reads a string attribute | ||
* value from DOM element and allocates memory for it. Writes | ||
* `[pointer,length]` tuple to `sliceAddr` (using the global allocator | ||
* configured for the WASM bridge). Caller owns the memory. | ||
* | ||
* @param elementID | ||
* @param nameAddr | ||
* @param sliceAddr | ||
*/ | ||
_getStringAttribAlloc(elementID: number, nameAddr: number, sliceAddr: number): void; | ||
/** | ||
* Sets attribute for given element to new f64 value. `nameAddr` is a | ||
* zero-terminated char pointer (or standard Zig `[]u8` / `[]const u8` | ||
* slices). | ||
* pointer to a zero-terminated u8 array (or standard Zig `[]u8` slice). | ||
* | ||
@@ -96,21 +133,21 @@ * @param elementID | ||
*/ | ||
_setNumericAttrib(elementID: number, nameAddr: number, val: number): void; | ||
setNumericAttrib(elementID: number, nameAddr: number, val: number): void; | ||
/** | ||
* Sets (or removes) boolean attribute for given element. `nameAddr` is a | ||
* zero-terminated char pointer (or standard Zig `[]u8` / `[]const u8` | ||
* slices). If `state` is non-zero, the attrib will be created/ensured, if | ||
* zero it will be removed. | ||
* Reads a numeric attribute value from DOM element and returns it as f64. | ||
* | ||
* @param elementID | ||
* @param nameAddr | ||
* @param state | ||
*/ | ||
_setBooleanAttrib(elementID: number, nameAddr: number, state: number): void; | ||
getNumericAttrib(elementID: number, nameAddr: number): number; | ||
/** | ||
* Reads a numeric attribute value from DOM element and returns it as f64. | ||
* Sets (or removes) boolean attribute for given element. `nameAddr` is a | ||
* pointer to a zero-terminated u8 array (or standard Zig `[]u8` slice). If | ||
* `state` is non-zero, the attrib will be created/ensured, if zero it will | ||
* be removed. | ||
* | ||
* @param elementID | ||
* @param nameAddr | ||
* @param state | ||
*/ | ||
_getNumericAttrib(elementID: number, nameAddr: number): number; | ||
_setBooleanAttrib(elementID: number, nameAddr: number, state: number): void; | ||
/** | ||
@@ -123,4 +160,4 @@ * Check if the DOM element has given attribute and returns 1 if so, else 0. | ||
_getBooleanAttrib(elementID: number, nameAddr: number): number; | ||
_addClass(elementID: number, nameAddr: number): void; | ||
_removeClass(elementID: number, nameAddr: number): void; | ||
addClass(elementID: number, nameAddr: number): void; | ||
removeClass(elementID: number, nameAddr: number): void; | ||
/** | ||
@@ -148,4 +185,2 @@ * Attaches a new DOM event listener for given event name to element (or | ||
_removeListener(listenerID: number): void; | ||
_setInnerHtml(elementID: number, body: number): void; | ||
_setInnerText(elementID: number, body: number): void; | ||
_requestAnimationFrame(rafID: number): void; | ||
@@ -152,0 +187,0 @@ _requestFullscreen(elementID: number): Promise<void>; |
# Change Log | ||
- **Last updated**: 2022-11-01T12:32:51Z | ||
- **Last updated**: 2022-11-23T22:46:54Z | ||
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub) | ||
@@ -12,2 +12,31 @@ | ||
## [0.10.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api-dom@0.10.0) (2022-11-23) | ||
#### 🚀 Features | ||
- add getStringAttribAlloc() ([61e301c](https://github.com/thi-ng/umbrella/commit/61e301c)) | ||
- add getStringAttribAlloc() API method | ||
- refactor/simplify existing attrib accessors | ||
- add listener auto-cleanup ([4b443c2](https://github.com/thi-ng/umbrella/commit/4b443c2)) | ||
- add hidden DOM element property to stored listener IDs | ||
- update removeElement() to auto-cleanup listeners | ||
- update Zig API | ||
- add support for event listener attribs ([c7e4864](https://github.com/thi-ng/umbrella/commit/c7e4864)) | ||
- add/update typespecs | ||
- update Attrib type to support event listener specs to be | ||
given to createElement() & createCanvas() | ||
- add getElementByID() | ||
- update initElement() | ||
- add WASM auto-initializer hook | ||
- update generated types & string handling ([686f867](https://github.com/thi-ng/umbrella/commit/686f867)) | ||
- switch to extern structs for generated types | ||
- switch to zero-terminated pointers for string values | ||
- update/rename/simplify API methods, remove obsolete | ||
- fix string attrib handling/alloc sizes | ||
#### ♻️ Refactoring | ||
- update string, attrib & listener types/fns [zig] ([9928ab7](https://github.com/thi-ng/umbrella/commit/9928ab7)) | ||
- update generated types ([298f194](https://github.com/thi-ng/umbrella/commit/298f194)) | ||
## [0.9.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api-dom@0.9.0) (2022-11-01) | ||
@@ -17,5 +46,2 @@ | ||
- update ScrollEvent ([17a6205](https://github.com/thi-ng/umbrella/commit/17a6205)) | ||
- update ScrollEvent.fromEvent() to use target element's scroll offset | ||
- if attached to window, use global offset | ||
- add attrib creation opts ([0e69c47](https://github.com/thi-ng/umbrella/commit/0e69c47)) | ||
@@ -26,2 +52,5 @@ - add Attrib, AttribValue, AttribType types | ||
- update doc strings | ||
- update ScrollEvent ([17a6205](https://github.com/thi-ng/umbrella/commit/17a6205)) | ||
- update ScrollEvent.fromEvent() to use target element's scroll offset | ||
- if attached to window, use global offset | ||
@@ -57,8 +86,8 @@ ### [0.8.1](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api-dom@0.8.1) (2022-10-31) | ||
- add fullscreen methods for WindowInfo [zig] ([e480b2c](https://github.com/thi-ng/umbrella/commit/e480b2c)) | ||
- add isFullscreen() & hasFullscreen() helpers | ||
- add boolean attrib support ([1d9c543](https://github.com/thi-ng/umbrella/commit/1d9c543)) | ||
- add support for recursive DOM tree creation ([36d1857](https://github.com/thi-ng/umbrella/commit/36d1857)) | ||
- update CreateElementOpts w/ recursive `.children` field | ||
- update createElement() API method to also create children | ||
- add boolean attrib support ([1d9c543](https://github.com/thi-ng/umbrella/commit/1d9c543)) | ||
- add fullscreen methods for WindowInfo [zig] ([e480b2c](https://github.com/thi-ng/umbrella/commit/e480b2c)) | ||
- add isFullscreen() & hasFullscreen() helpers | ||
@@ -74,10 +103,2 @@ #### 🩹 Bug fixes | ||
- update fullscreen handling, add exit support ([998c5fc](https://github.com/thi-ng/umbrella/commit/998c5fc)) | ||
- add exitFullscreen() API method | ||
- add zig docstrings | ||
- update KeyEvent ([8c91c17](https://github.com/thi-ng/umbrella/commit/8c91c17)) | ||
- add u8 length field to avoid scanning for sentinel | ||
- add KeyEvent.hasModifier() (zig only) | ||
- add XML namespace support for createElement() ([6074d04](https://github.com/thi-ng/umbrella/commit/6074d04)) | ||
- add public registry of wellknown namespace aliases | ||
- major update event types & handling ([2878bce](https://github.com/thi-ng/umbrella/commit/2878bce)) | ||
@@ -90,2 +111,10 @@ - major simplification of JS side event processing | ||
- update WindowInfo to include fullscreen info & scroll offsets | ||
- add XML namespace support for createElement() ([6074d04](https://github.com/thi-ng/umbrella/commit/6074d04)) | ||
- add public registry of wellknown namespace aliases | ||
- update KeyEvent ([8c91c17](https://github.com/thi-ng/umbrella/commit/8c91c17)) | ||
- add u8 length field to avoid scanning for sentinel | ||
- add KeyEvent.hasModifier() (zig only) | ||
- update fullscreen handling, add exit support ([998c5fc](https://github.com/thi-ng/umbrella/commit/998c5fc)) | ||
- add exitFullscreen() API method | ||
- add zig docstrings | ||
@@ -96,2 +125,10 @@ ## [0.4.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api-dom@0.4.0) (2022-10-26) | ||
- support more event types/fns, minor zig updates ([d05803d](https://github.com/thi-ng/umbrella/commit/d05803d)) | ||
- add support for focus & wheel events | ||
- add stopPropagation() | ||
- update zig error fn return types | ||
- update Zig API ([5165f87](https://github.com/thi-ng/umbrella/commit/5165f87)) | ||
- remove ManagedIndex (see [d8bb3ee7d](https://github.com/thi-ng/umbrella/commit/d8bb3ee7d)) | ||
- move all Zig sources from /include => /zig | ||
- update pkg | ||
- add/update event types & handling ([4596757](https://github.com/thi-ng/umbrella/commit/4596757)) | ||
@@ -103,10 +140,2 @@ - add new more specific event structs to be more mem efficient | ||
- move typedefs.json to /src folder | ||
- update Zig API ([5165f87](https://github.com/thi-ng/umbrella/commit/5165f87)) | ||
- remove ManagedIndex (see [d8bb3ee7d](https://github.com/thi-ng/umbrella/commit/d8bb3ee7d)) | ||
- move all Zig sources from /include => /zig | ||
- update pkg | ||
- support more event types/fns, minor zig updates ([d05803d](https://github.com/thi-ng/umbrella/commit/d05803d)) | ||
- add support for focus & wheel events | ||
- add stopPropagation() | ||
- update zig error fn return types | ||
@@ -117,2 +146,5 @@ ## [0.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api-dom@0.3.0) (2022-10-17) | ||
- update EventListener and RAFListener ([9f97f3d](https://github.com/thi-ng/umbrella/commit/9f97f3d)) | ||
- update EventListener to allow storing a anyopaque pointer | ||
- update RAFListener to allow storing a anyopaque pointer | ||
- update/rename ManagedIndex ([078df79](https://github.com/thi-ng/umbrella/commit/078df79)) | ||
@@ -122,5 +154,2 @@ - rename FreeList => ManagedIndex | ||
- update DOM listener functions to only use u16 for IDs | ||
- update EventListener and RAFListener ([9f97f3d](https://github.com/thi-ng/umbrella/commit/9f97f3d)) | ||
- update EventListener to allow storing a anyopaque pointer | ||
- update RAFListener to allow storing a anyopaque pointer | ||
@@ -157,4 +186,4 @@ #### 🩹 Bug fixes | ||
- import as new pkg ([58bacc1](https://github.com/thi-ng/umbrella/commit/58bacc1)) | ||
- extend API & types, add docs ([27fc6d6](https://github.com/thi-ng/umbrella/commit/27fc6d6)) | ||
- import as new pkg ([58bacc1](https://github.com/thi-ng/umbrella/commit/58bacc1)) | ||
@@ -161,0 +190,0 @@ #### ♻️ Refactoring |
20
dom.d.ts
@@ -1,4 +0,5 @@ | ||
import type { IWasmAPI, ReadonlyWasmString, WasmBridge, WasmType } from "@thi.ng/wasm-api"; | ||
import type { NumOrString } from "@thi.ng/api"; | ||
import { IWasmAPI, ReadonlyWasmString, WasmBridge, WasmType } from "@thi.ng/wasm-api"; | ||
import { ObjectIndex } from "@thi.ng/wasm-api/object-index"; | ||
import { Attrib, CreateElementOpts, DOMExports, DOMImports, Event as WasmEvent } from "./api.js"; | ||
import { CreateElementOpts, DOMExports, DOMImports, Event as WasmEvent } from "./api.js"; | ||
/** @internal */ | ||
@@ -12,3 +13,3 @@ interface WasmListener { | ||
export declare class WasmDom implements IWasmAPI<DOMExports> { | ||
readonly id = "dom"; | ||
static readonly id = "dom"; | ||
parent: WasmBridge<DOMExports>; | ||
@@ -21,6 +22,6 @@ $Event: WasmType<WasmEvent>; | ||
protected currDataTransfer: DataTransfer | null; | ||
get id(): string; | ||
init(parent: WasmBridge<DOMExports>): Promise<boolean>; | ||
getImports(): DOMImports; | ||
protected initElement(el: Element, opts: Pick<Readonly<CreateElementOpts>, "class" | "id" | "index" | "parent"> & Partial<{ | ||
attribs: Attrib[]; | ||
protected initElement(elementID: number, el: Element, opts: Pick<Readonly<CreateElementOpts>, "attribs" | "class" | "id" | "index" | "parent"> & Partial<{ | ||
html: ReadonlyWasmString; | ||
@@ -30,4 +31,13 @@ text: ReadonlyWasmString; | ||
protected encodeModifiers(e: KeyboardEvent): number; | ||
protected getAttrib(elementID: number, nameAddr: number): string | number | boolean | ParentNode | ChildNode | DOMTokenList | ((this: Element, ev: Event) => any) | { | ||
(options?: ScrollToOptions | undefined): void; | ||
(x: number, y: number): void; | ||
} | ((arg?: boolean | ScrollIntoViewOptions | undefined) => void) | ((namespace: string | null, qualifiedName: string, value: string) => void) | { | ||
<K extends keyof ElementEventMap>(type: K, listener: (this: Element, ev: ElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions | undefined): void; | ||
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined): void; | ||
} | ((other: Node | null) => boolean) | (<T extends Node>(node: T, child: Node | null) => T) | ((...nodes: (string | Node)[]) => void) | HTMLCollection | NamedNodeMap | ((init: ShadowRootInit) => ShadowRoot) | ((pointerId: number) => void) | ((options?: FullscreenOptions | undefined) => Promise<void>) | ((qualifiedName: string, force?: boolean | undefined) => boolean) | NodeListOf<ChildNode> | ((options?: GetRootNodeOptions | undefined) => Node) | ((keyframes: PropertyIndexedKeyframes | Keyframe[] | null, options?: number | KeyframeAnimationOptions | undefined) => Animation) | ((options?: GetAnimationsOptions | undefined) => Animation[]) | null; | ||
protected setAttrib(elementID: number, nameAddr: number, value: NumOrString): void | NumOrString; | ||
protected removeListener(ctx: Window | Element, listenerID: number): void; | ||
} | ||
export {}; | ||
//# sourceMappingURL=dom.d.ts.map |
147
dom.js
import { adaptDPI } from "@thi.ng/adapt-dpi"; | ||
import { assert } from "@thi.ng/errors/assert"; | ||
import { WasmStringSlice, } from "@thi.ng/wasm-api"; | ||
import { ObjectIndex } from "@thi.ng/wasm-api/object-index"; | ||
import { $CreateCanvasOpts, $CreateElementOpts, $Event, $WindowInfo, AttribType, EventType, NS_PREFIXES, } from "./api.js"; | ||
/** | ||
* Hidden property for managed DOM elements to track IDs of attached WASM event | ||
* listeners | ||
*/ | ||
const __listeners = "__wasm_listeners"; | ||
/** | ||
* Map of JS event name regexps to {@link EventType} enums and {@link EventBody} | ||
* field names | ||
*/ | ||
const EVENT_MAP = [ | ||
@@ -26,3 +36,2 @@ [/^drag(end|enter|leave|over|start)|drop$/, "drag", EventType.DRAG], | ||
constructor() { | ||
this.id = "dom"; | ||
this.elements = new ObjectIndex({ name: "elements" }); | ||
@@ -33,4 +42,8 @@ this.listeners = {}; | ||
} | ||
get id() { | ||
return WasmDom.id; | ||
} | ||
async init(parent) { | ||
this.parent = parent; | ||
parent.exports._dom_init(); | ||
this.elements.add(document.head); | ||
@@ -61,2 +74,11 @@ this.elements.add(document.body); | ||
}, | ||
getElementByID: (nameAddr) => { | ||
const name = this.parent.getString(nameAddr); | ||
let id = this.elements.find((el) => el.id === name); | ||
if (id === undefined) { | ||
const el = document.getElementById(name); | ||
return el ? this.elements.add(el) : -1; | ||
} | ||
return id; | ||
}, | ||
createElement: (optsAddr) => { | ||
@@ -69,4 +91,4 @@ const create = (opts, nestedParent) => { | ||
: document.createElement(tagName); | ||
this.initElement(el, opts, nestedParent); | ||
const id = this.elements.add(el); | ||
this.initElement(id, el, opts, nestedParent); | ||
if (opts.children.length > 0) { | ||
@@ -87,7 +109,16 @@ for (let child of opts.children) { | ||
const remove = (el) => { | ||
const id = this.elements.find((x) => x === el, false); | ||
if (id !== undefined) | ||
this.elements.delete(id, false); | ||
const elementID = this.elements.find((x) => x === el, false); | ||
if (elementID !== undefined) { | ||
this.elements.delete(elementID, false); | ||
const elementListeners = el[__listeners]; | ||
if (elementListeners) { | ||
for (let listenerID of elementListeners) { | ||
this.removeListener(el, listenerID); | ||
// WASM side cleanup | ||
this.parent.exports._dom_removeListener(listenerID); | ||
} | ||
} | ||
} | ||
el.parentNode?.removeChild(el); | ||
for (let child of el.children) | ||
for (let child of [...el.children]) | ||
remove(child); | ||
@@ -101,32 +132,28 @@ }; | ||
adaptDPI(el, opts.width, opts.height, opts.dpr); | ||
this.initElement(el, opts); | ||
return this.elements.add(el); | ||
const id = this.elements.add(el); | ||
this.initElement(id, el, opts); | ||
return id; | ||
}, | ||
setCanvasSize: (elementID, width, height, dpr) => adaptDPI(this.elements.get(elementID), width, height, dpr), | ||
_setStringAttrib: (elementID, name, val) => this.elements | ||
.get(elementID) | ||
.setAttribute(this.parent.getString(name), this.parent.getString(val)), | ||
_setNumericAttrib: (elementID, name, val) => this.elements | ||
.get(elementID) | ||
.setAttribute(this.parent.getString(name), String(val)), | ||
_setBooleanAttrib: (elementID, name, val) => { | ||
setStringAttrib: (elementID, name, val) => this.setAttrib(elementID, name, this.parent.getString(val)), | ||
setNumericAttrib: (elementID, name, val) => this.setAttrib(elementID, name, val), | ||
_setBooleanAttrib: (elementID, nameAddr, val) => { | ||
const el = this.elements.get(elementID); | ||
const attr = this.parent.getString(name); | ||
val ? el.setAttribute(attr, "") : el.removeAttribute(attr); | ||
const name = this.parent.getString(nameAddr); | ||
if (name in el) { | ||
// @ts-ignore | ||
el[name] = !!val; | ||
} | ||
else { | ||
val ? el.setAttribute(name, "") : el.removeAttribute(name); | ||
} | ||
}, | ||
_getStringAttrib: (elementID, name, valAddr, maxBytes) => this.parent.setString(String(this.elements | ||
_getStringAttrib: (elementID, nameAddr, valAddr, maxBytes) => this.parent.setString(String(this.getAttrib(elementID, nameAddr) || ""), valAddr, maxBytes, true), | ||
_getStringAttribAlloc: (elementID, nameAddr, slice) => new WasmStringSlice(this.parent, slice).setAlloc(String(this.getAttrib(elementID, nameAddr) || ""), true), | ||
getNumericAttrib: (elementID, nameAddr) => Number(this.getAttrib(elementID, nameAddr) || ""), | ||
_getBooleanAttrib: (elementID, nameAddr) => ~~(this.getAttrib(elementID, nameAddr) != null), | ||
addClass: (elementID, name) => this.elements | ||
.get(elementID) | ||
.getAttribute(this.parent.getString(name)) || ""), valAddr, maxBytes, true), | ||
_getNumericAttrib: (elementID, name) => Number(this.elements | ||
.get(elementID) | ||
.getAttribute(this.parent.getString(name))), | ||
_getBooleanAttrib: (elementID, name) => this.elements | ||
.get(elementID) | ||
.getAttribute(this.parent.getString(name)) != null | ||
? 1 | ||
: 0, | ||
_addClass: (elementID, name) => this.elements | ||
.get(elementID) | ||
.classList.add(this.parent.getString(name)), | ||
_removeClass: (elementID, name) => this.elements | ||
removeClass: (elementID, name) => this.elements | ||
.get(elementID) | ||
@@ -168,6 +195,5 @@ .classList.remove(this.parent.getString(name)), | ||
} | ||
this.parent.exports.dom_callListener(listenerID, event.__base); | ||
if (slice) { | ||
this.parent.exports._dom_callListener(listenerID, event.__base); | ||
if (slice) | ||
this.parent.free(slice); | ||
} | ||
this.currEvent = null; | ||
@@ -184,2 +210,6 @@ this.currDataTransfer = null; | ||
}; | ||
if (ctxID >= 0) { | ||
(ctx[__listeners] || | ||
(ctx[__listeners] = new Set())).add(listenerID); | ||
} | ||
}, | ||
@@ -199,12 +229,14 @@ preventDefault: () => { | ||
const ctx = listener.ctx < 0 ? window : this.elements.get(listener.ctx); | ||
ctx.removeEventListener(listener.name, listener.fn); | ||
this.parent.logger.debug(`removing event listener #${listenerID}`); | ||
this.parent.free([listener.event.__base, this.$Event.size]); | ||
delete this.listeners[listenerID]; | ||
this.removeListener(ctx, listenerID); | ||
if (listener.ctx >= 0) { | ||
const listeners = ctx[__listeners]; | ||
if (listeners.has(listenerID)) | ||
listeners.delete(listenerID); | ||
} | ||
}, | ||
_setInnerHtml: (elementID, body) => { | ||
setInnerHtml: (elementID, body) => { | ||
this.elements.get(elementID).innerHTML = | ||
this.parent.getString(body); | ||
}, | ||
_setInnerText: (elementID, body) => { | ||
setInnerText: (elementID, body) => { | ||
this.elements.get(elementID).innerText = | ||
@@ -215,3 +247,3 @@ this.parent.getString(body); | ||
this.parent.logger.fine(`requestAnimationFrame #${rafID}`); | ||
requestAnimationFrame((t) => this.parent.exports.dom_callRAF(rafID, t)); | ||
requestAnimationFrame((t) => this.parent.exports._dom_callRAF(rafID, t)); | ||
}, | ||
@@ -227,3 +259,3 @@ _requestFullscreen: async (elementID) => { | ||
await method.bind(el)(); | ||
this.parent.exports.dom_fullscreenChanged(); | ||
this.parent.exports._dom_fullscreenChanged(); | ||
} | ||
@@ -237,3 +269,3 @@ }, | ||
await method.bind(document)(); | ||
this.parent.exports.dom_fullscreenChanged(); | ||
this.parent.exports._dom_fullscreenChanged(); | ||
} | ||
@@ -243,3 +275,3 @@ }, | ||
} | ||
initElement(el, opts, nestedParent) { | ||
initElement(elementID, el, opts, nestedParent) { | ||
const { id, attribs, class: $class, index } = opts; | ||
@@ -259,3 +291,8 @@ if (id.length) | ||
const name = attr.name.deref(); | ||
if (attr.kind === AttribType.FLAG) { | ||
if (attr.kind === AttribType.EVENT) { | ||
const listenerAddr = attr.value.event.__base; | ||
const listenerID = this.parent.exports._dom_addListener(listenerAddr); | ||
this.getImports()._addListener(elementID, attr.name.addr, listenerID); | ||
} | ||
else if (attr.kind === AttribType.FLAG) { | ||
attr.value.flag && el.setAttribute(name, ""); | ||
@@ -284,2 +321,24 @@ } | ||
} | ||
getAttrib(elementID, nameAddr) { | ||
const el = this.elements.get(elementID); | ||
const name = this.parent.getString(nameAddr); | ||
return name in el ? el[name] : el.getAttribute(name); | ||
} | ||
setAttrib(elementID, nameAddr, value) { | ||
const el = this.elements.get(elementID); | ||
const name = this.parent.getString(nameAddr); | ||
return name in el | ||
? // @ts-ignore | ||
(el[name] = value) | ||
: el.setAttribute(name, String(value)); | ||
} | ||
removeListener(ctx, listenerID) { | ||
const listener = this.listeners[listenerID]; | ||
assert(!!listener, `invalid listener ID ${listenerID}`); | ||
this.parent.logger.debug(`removing event listener #${listenerID}`); | ||
delete this.listeners[listenerID]; | ||
ctx.removeEventListener(listener.name, listener.fn); | ||
this.parent.free([listener.event.__base, this.$Event.size]); | ||
} | ||
} | ||
WasmDom.id = "dom"; |
/** | ||
* Generated by @thi.ng/wasm-api at 2022-11-01T11:56:07.332Z - DO NOT EDIT! | ||
* Generated by @thi.ng/wasm-api-bindgen at 2022-11-23T12:10:08.507Z | ||
* DO NOT EDIT! | ||
*/ | ||
import { MemorySlice, WasmStringSlice, WasmTypeBase, WasmTypeConstructor } from "@thi.ng/wasm-api"; | ||
import { MemorySlice, WasmStringPtr, WasmTypeBase, WasmTypeConstructor } from "@thi.ng/wasm-api"; | ||
export declare enum EventType { | ||
@@ -39,2 +40,5 @@ UNKOWN = -1, | ||
} | ||
/** | ||
* Various browser window related information [TODO] | ||
*/ | ||
export interface WindowInfo extends WasmTypeBase { | ||
@@ -121,7 +125,13 @@ /** | ||
* Value of the targeted input element. | ||
* The memory is owned by the DOM API and will be freed immediatedly after the | ||
* The memory is owned by the DOM API and will be freed immediately after the | ||
* event handler has returned. | ||
*/ | ||
value: WasmStringSlice; | ||
value: WasmStringPtr; | ||
/** | ||
* Length of the value string | ||
* | ||
* WASM type: u32 | ||
*/ | ||
len: number; | ||
/** | ||
* Encoded bitmask of currently pressed modifier keys, see `KeyModifier` enum | ||
@@ -223,3 +233,3 @@ * | ||
/** | ||
* The plane angle (in degrees, in the range of -90 to 90) between the Y–Z plane | ||
* The plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane | ||
* and the plane containing both the pointer (e.g. pen stylus) axis and the Y | ||
@@ -232,3 +242,3 @@ * axis. | ||
/** | ||
* The plane angle (in degrees, in the range of -90 to 90) between the X–Z plane | ||
* The plane angle (in degrees, in the range of -90 to 90) between the X-Z plane | ||
* and the plane containing both the pointer (e.g. pen stylus) axis and the X | ||
@@ -390,2 +400,30 @@ * axis. | ||
export declare const $Event: WasmTypeConstructor<Event>; | ||
/** | ||
* DOM event listener | ||
*/ | ||
export interface EventListener extends WasmTypeBase { | ||
/** | ||
* Event listener function. Takes an event and optional pointer to user supplied | ||
* arbitrary context data provided when registering the handler via | ||
* `addListener()` | ||
* | ||
* WASM type: u32 | ||
*/ | ||
callback: number; | ||
/** | ||
* Optional type erased pointer to arbitrary user context. This pointer can be | ||
* cast back into the desired type using this form: | ||
* `@ptrCast(?*Foo, @alignCast(@alignOf(Foo), raw))` | ||
* Also see: `wasmapi.ptrCast()` | ||
* | ||
* WASM type: ?u32 | ||
*/ | ||
ctx: number; | ||
} | ||
export declare const $EventListener: WasmTypeConstructor<EventListener>; | ||
/** | ||
* Data structure used for declarative creation of DOM elements / trees (passed | ||
* to `createElement()`) | ||
* Also see `CreateCanvasOpts` for canvas specific use cases | ||
*/ | ||
export interface CreateElementOpts extends WasmTypeBase { | ||
@@ -395,23 +433,23 @@ /** | ||
*/ | ||
tag: WasmStringSlice; | ||
tag: WasmStringPtr; | ||
/** | ||
* Namespace URI or wellknown registered alias (e.g. svg, xlink, xmlns) | ||
*/ | ||
ns: WasmStringSlice; | ||
ns: WasmStringPtr; | ||
/** | ||
* ID attrib | ||
*/ | ||
id: WasmStringSlice; | ||
id: WasmStringPtr; | ||
/** | ||
* Element class attrib | ||
*/ | ||
class: WasmStringSlice; | ||
class: WasmStringPtr; | ||
/** | ||
* Element inner text body | ||
*/ | ||
text: WasmStringSlice; | ||
text: WasmStringPtr; | ||
/** | ||
* Element inner HTML body | ||
*/ | ||
html: WasmStringSlice; | ||
html: WasmStringPtr; | ||
/** | ||
@@ -437,3 +475,3 @@ * Parent element ID. If >=0 the new element will be attached to that parent | ||
* Optional slice of attribute definitions for this element. Also see provided | ||
* `Attrib` factory fns for convenience. | ||
* `Attrib` factory functions for convenience. | ||
*/ | ||
@@ -443,25 +481,6 @@ attribs: Attrib[]; | ||
export declare const $CreateElementOpts: WasmTypeConstructor<CreateElementOpts>; | ||
export interface Attrib extends WasmTypeBase { | ||
name: WasmStringSlice; | ||
value: AttribValue; | ||
kind: AttribType; | ||
} | ||
export declare const $Attrib: WasmTypeConstructor<Attrib>; | ||
export interface AttribValue extends WasmTypeBase { | ||
str: WasmStringSlice; | ||
/** | ||
* WASM type: f64 | ||
*/ | ||
num: number; | ||
/** | ||
* WASM type: u8 | ||
*/ | ||
flag: number; | ||
} | ||
export declare const $AttribValue: WasmTypeConstructor<AttribValue>; | ||
export declare enum AttribType { | ||
STR = 0, | ||
NUM = 1, | ||
FLAG = 2 | ||
} | ||
/** | ||
* Data structure used for declarative creation of canvas elements (passed to | ||
* `createCanvas()`) | ||
*/ | ||
export interface CreateCanvasOpts extends WasmTypeBase { | ||
@@ -483,7 +502,7 @@ /** | ||
*/ | ||
id: WasmStringSlice; | ||
id: WasmStringPtr; | ||
/** | ||
* Element class attrib | ||
*/ | ||
class: WasmStringSlice; | ||
class: WasmStringPtr; | ||
/** | ||
@@ -508,4 +527,37 @@ * Same as CreateElementOpts.parent | ||
dpr: number; | ||
/** | ||
* Optional slice of attribute definitions for this element. Also see provided | ||
* `Attrib` factory functions for convenience. | ||
*/ | ||
attribs: Attrib[]; | ||
} | ||
export declare const $CreateCanvasOpts: WasmTypeConstructor<CreateCanvasOpts>; | ||
/** | ||
* DOM element attribute definition given as part of `CreateElementOpts` | ||
*/ | ||
export interface Attrib extends WasmTypeBase { | ||
name: WasmStringPtr; | ||
value: AttribValue; | ||
kind: AttribType; | ||
} | ||
export declare const $Attrib: WasmTypeConstructor<Attrib>; | ||
export interface AttribValue extends WasmTypeBase { | ||
event: EventListener; | ||
/** | ||
* WASM type: u8 | ||
*/ | ||
flag: number; | ||
/** | ||
* WASM type: f64 | ||
*/ | ||
num: number; | ||
str: WasmStringPtr; | ||
} | ||
export declare const $AttribValue: WasmTypeConstructor<AttribValue>; | ||
export declare enum AttribType { | ||
EVENT = 0, | ||
FLAG = 1, | ||
NUM = 2, | ||
STR = 3 | ||
} | ||
//# sourceMappingURL=api.d.ts.map |
/** | ||
* Generated by @thi.ng/wasm-api at 2022-11-01T11:56:07.332Z - DO NOT EDIT! | ||
* Generated by @thi.ng/wasm-api-bindgen at 2022-11-23T12:10:08.507Z | ||
* DO NOT EDIT! | ||
*/ | ||
// @ts-ignore possibly includes unused imports | ||
import { WasmStringSlice } from "@thi.ng/wasm-api"; | ||
import { WasmStringPtr } from "@thi.ng/wasm-api"; | ||
export var EventType; | ||
@@ -178,4 +179,10 @@ (function (EventType) { | ||
get value() { | ||
return $value || ($value = new WasmStringSlice(mem, base, true)); | ||
return $value || ($value = new WasmStringPtr(mem, base, true)); | ||
}, | ||
get len() { | ||
return mem.u32[(base + 4) >>> 2]; | ||
}, | ||
set len(x) { | ||
mem.u32[(base + 4) >>> 2] = x; | ||
}, | ||
get modifiers() { | ||
@@ -190,7 +197,4 @@ return mem.u8[(base + 8)]; | ||
const value = el.type === "checkbox" ? el.checked ? "on" : "off" : el.value; | ||
const bytes = new TextEncoder().encode(value); | ||
const slice = mem.allocate(bytes.length + 1); | ||
mem.u8.set(bytes, slice[0]); | ||
mem.u8[slice[0] + bytes.length] = 0; | ||
this.value.setSlice(slice[0], bytes.length); | ||
const slice = this.value.setAlloc(value); | ||
this.len = slice[1] - 1; | ||
return slice; | ||
@@ -640,2 +644,32 @@ } | ||
}); | ||
export const $EventListener = (mem) => ({ | ||
get align() { | ||
return 4; | ||
}, | ||
get size() { | ||
return 8; | ||
}, | ||
instance: (base) => { | ||
return { | ||
get __base() { | ||
return base; | ||
}, | ||
get __bytes() { | ||
return mem.u8.subarray(base, base + 8); | ||
}, | ||
get callback() { | ||
return mem.u32[base >>> 2]; | ||
}, | ||
set callback(x) { | ||
mem.u32[base >>> 2] = x; | ||
}, | ||
get ctx() { | ||
return mem.u32[(base + 4) >>> 2]; | ||
}, | ||
set ctx(x) { | ||
mem.u32[(base + 4) >>> 2] = x; | ||
}, | ||
}; | ||
} | ||
}); | ||
export const $CreateElementOpts = (mem) => ({ | ||
@@ -646,3 +680,3 @@ get align() { | ||
get size() { | ||
return 72; | ||
return 48; | ||
}, | ||
@@ -661,51 +695,51 @@ instance: (base) => { | ||
get __bytes() { | ||
return mem.u8.subarray(base, base + 72); | ||
return mem.u8.subarray(base, base + 48); | ||
}, | ||
get tag() { | ||
return $tag || ($tag = new WasmStringSlice(mem, base, true)); | ||
return $tag || ($tag = new WasmStringPtr(mem, base, true)); | ||
}, | ||
get ns() { | ||
return $ns || ($ns = new WasmStringSlice(mem, (base + 8), true)); | ||
return $ns || ($ns = new WasmStringPtr(mem, (base + 4), true)); | ||
}, | ||
get id() { | ||
return $id || ($id = new WasmStringSlice(mem, (base + 16), true)); | ||
return $id || ($id = new WasmStringPtr(mem, (base + 8), true)); | ||
}, | ||
get class() { | ||
return $class || ($class = new WasmStringSlice(mem, (base + 24), true)); | ||
return $class || ($class = new WasmStringPtr(mem, (base + 12), true)); | ||
}, | ||
get text() { | ||
return $text || ($text = new WasmStringSlice(mem, (base + 32), true)); | ||
return $text || ($text = new WasmStringPtr(mem, (base + 16), true)); | ||
}, | ||
get html() { | ||
return $html || ($html = new WasmStringSlice(mem, (base + 40), true)); | ||
return $html || ($html = new WasmStringPtr(mem, (base + 20), true)); | ||
}, | ||
get parent() { | ||
return mem.i32[(base + 48) >>> 2]; | ||
return mem.i32[(base + 24) >>> 2]; | ||
}, | ||
set parent(x) { | ||
mem.i32[(base + 48) >>> 2] = x; | ||
mem.i32[(base + 24) >>> 2] = x; | ||
}, | ||
get index() { | ||
return mem.i32[(base + 52) >>> 2]; | ||
return mem.i32[(base + 28) >>> 2]; | ||
}, | ||
set index(x) { | ||
mem.i32[(base + 52) >>> 2] = x; | ||
mem.i32[(base + 28) >>> 2] = x; | ||
}, | ||
get children() { | ||
const len = mem.u32[(base + 60) >>> 2]; | ||
const addr = mem.u32[(base + 56) >>> 2]; | ||
const addr = mem.u32[(base + 32) >>> 2]; | ||
const len = mem.u32[(base + 36) >>> 2]; | ||
const inst = $CreateElementOpts(mem); | ||
const slice = []; | ||
const buf = []; | ||
for (let i = 0; i < len; i++) | ||
slice.push(inst.instance(addr + i * 72)); | ||
return slice; | ||
buf.push(inst.instance(addr + i * 48)); | ||
return buf; | ||
}, | ||
get attribs() { | ||
const len = mem.u32[(base + 68) >>> 2]; | ||
const addr = mem.u32[(base + 64) >>> 2]; | ||
const addr = mem.u32[(base + 40) >>> 2]; | ||
const len = mem.u32[(base + 44) >>> 2]; | ||
const inst = $Attrib(mem); | ||
const slice = []; | ||
const buf = []; | ||
for (let i = 0; i < len; i++) | ||
slice.push(inst.instance(addr + i * 24)); | ||
return slice; | ||
buf.push(inst.instance(addr + i * 24)); | ||
return buf; | ||
}, | ||
@@ -715,2 +749,67 @@ }; | ||
}); | ||
export const $CreateCanvasOpts = (mem) => ({ | ||
get align() { | ||
return 4; | ||
}, | ||
get size() { | ||
return 32; | ||
}, | ||
instance: (base) => { | ||
let $id = null; | ||
let $class = null; | ||
return { | ||
get __base() { | ||
return base; | ||
}, | ||
get __bytes() { | ||
return mem.u8.subarray(base, base + 32); | ||
}, | ||
get width() { | ||
return mem.u16[base >>> 1]; | ||
}, | ||
set width(x) { | ||
mem.u16[base >>> 1] = x; | ||
}, | ||
get height() { | ||
return mem.u16[(base + 2) >>> 1]; | ||
}, | ||
set height(x) { | ||
mem.u16[(base + 2) >>> 1] = x; | ||
}, | ||
get id() { | ||
return $id || ($id = new WasmStringPtr(mem, (base + 4), true)); | ||
}, | ||
get class() { | ||
return $class || ($class = new WasmStringPtr(mem, (base + 8), true)); | ||
}, | ||
get parent() { | ||
return mem.i32[(base + 12) >>> 2]; | ||
}, | ||
set parent(x) { | ||
mem.i32[(base + 12) >>> 2] = x; | ||
}, | ||
get index() { | ||
return mem.i32[(base + 16) >>> 2]; | ||
}, | ||
set index(x) { | ||
mem.i32[(base + 16) >>> 2] = x; | ||
}, | ||
get dpr() { | ||
return mem.u8[(base + 20)]; | ||
}, | ||
set dpr(x) { | ||
mem.u8[(base + 20)] = x; | ||
}, | ||
get attribs() { | ||
const addr = mem.u32[(base + 24) >>> 2]; | ||
const len = mem.u32[(base + 28) >>> 2]; | ||
const inst = $Attrib(mem); | ||
const buf = []; | ||
for (let i = 0; i < len; i++) | ||
buf.push(inst.instance(addr + i * 24)); | ||
return buf; | ||
}, | ||
}; | ||
} | ||
}); | ||
export const $Attrib = (mem) => ({ | ||
@@ -733,3 +832,3 @@ get align() { | ||
get name() { | ||
return $name || ($name = new WasmStringSlice(mem, base, true)); | ||
return $name || ($name = new WasmStringPtr(mem, base, true)); | ||
}, | ||
@@ -767,5 +866,14 @@ get value() { | ||
}, | ||
get str() { | ||
return $str || ($str = new WasmStringSlice(mem, base, true)); | ||
get event() { | ||
return $EventListener(mem).instance(base); | ||
}, | ||
set event(x) { | ||
mem.u8.set(x.__bytes, base); | ||
}, | ||
get flag() { | ||
return mem.u8[base]; | ||
}, | ||
set flag(x) { | ||
mem.u8[base] = x; | ||
}, | ||
get num() { | ||
@@ -777,8 +885,5 @@ return mem.f64[base >>> 3]; | ||
}, | ||
get flag() { | ||
return mem.u8[base]; | ||
get str() { | ||
return $str || ($str = new WasmStringPtr(mem, base, true)); | ||
}, | ||
set flag(x) { | ||
mem.u8[base] = x; | ||
}, | ||
}; | ||
@@ -789,61 +894,6 @@ } | ||
(function (AttribType) { | ||
AttribType[AttribType["STR"] = 0] = "STR"; | ||
AttribType[AttribType["NUM"] = 1] = "NUM"; | ||
AttribType[AttribType["FLAG"] = 2] = "FLAG"; | ||
AttribType[AttribType["EVENT"] = 0] = "EVENT"; | ||
AttribType[AttribType["FLAG"] = 1] = "FLAG"; | ||
AttribType[AttribType["NUM"] = 2] = "NUM"; | ||
AttribType[AttribType["STR"] = 3] = "STR"; | ||
})(AttribType || (AttribType = {})); | ||
export const $CreateCanvasOpts = (mem) => ({ | ||
get align() { | ||
return 4; | ||
}, | ||
get size() { | ||
return 32; | ||
}, | ||
instance: (base) => { | ||
let $id = null; | ||
let $class = null; | ||
return { | ||
get __base() { | ||
return base; | ||
}, | ||
get __bytes() { | ||
return mem.u8.subarray(base, base + 32); | ||
}, | ||
get width() { | ||
return mem.u16[base >>> 1]; | ||
}, | ||
set width(x) { | ||
mem.u16[base >>> 1] = x; | ||
}, | ||
get height() { | ||
return mem.u16[(base + 2) >>> 1]; | ||
}, | ||
set height(x) { | ||
mem.u16[(base + 2) >>> 1] = x; | ||
}, | ||
get id() { | ||
return $id || ($id = new WasmStringSlice(mem, (base + 4), true)); | ||
}, | ||
get class() { | ||
return $class || ($class = new WasmStringSlice(mem, (base + 12), true)); | ||
}, | ||
get parent() { | ||
return mem.i32[(base + 20) >>> 2]; | ||
}, | ||
set parent(x) { | ||
mem.i32[(base + 20) >>> 2] = x; | ||
}, | ||
get index() { | ||
return mem.i32[(base + 24) >>> 2]; | ||
}, | ||
set index(x) { | ||
mem.i32[(base + 24) >>> 2] = x; | ||
}, | ||
get dpr() { | ||
return mem.u8[(base + 28)]; | ||
}, | ||
set dpr(x) { | ||
mem.u8[(base + 28)] = x; | ||
}, | ||
}; | ||
} | ||
}); |
{ | ||
"name": "@thi.ng/wasm-api-dom", | ||
"version": "0.9.0", | ||
"version": "0.10.0", | ||
"description": "Browser DOM bridge API for hybrid TypeScript & WASM (Zig) applications", | ||
@@ -28,3 +28,3 @@ "type": "module", | ||
"build": "yarn clean && tsc --declaration", | ||
"build:types": "npx wasm-api -a analytics.json --lang ts -o src/generated/api.ts --lang zig -o zig/api.zig src/typedefs.json", | ||
"build:types": "npx wasm-api-bindgen -a analytics.json --config src/typedefs-config.json --lang ts -o src/generated/api.ts --lang zig -o zig/api.zig src/typedefs.json", | ||
"clean": "rimraf '*.js' '*.d.ts' '*.map' doc", | ||
@@ -39,14 +39,15 @@ "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts", | ||
"dependencies": { | ||
"@thi.ng/adapt-dpi": "^2.2.1", | ||
"@thi.ng/api": "^8.4.5", | ||
"@thi.ng/checks": "^3.3.2", | ||
"@thi.ng/errors": "^2.2.3", | ||
"@thi.ng/wasm-api": "^0.18.1" | ||
"@thi.ng/adapt-dpi": "^2.2.2", | ||
"@thi.ng/api": "^8.4.6", | ||
"@thi.ng/errors": "^2.2.4", | ||
"@thi.ng/prefixes": "^2.1.13", | ||
"@thi.ng/wasm-api": "^1.0.0", | ||
"@thi.ng/wasm-api-bindgen": "^0.1.0" | ||
}, | ||
"devDependencies": { | ||
"@microsoft/api-extractor": "^7.33.5", | ||
"@thi.ng/testament": "^0.3.4", | ||
"@thi.ng/testament": "^0.3.5", | ||
"rimraf": "^3.0.2", | ||
"tools": "^0.0.1", | ||
"typedoc": "^0.23.18", | ||
"typedoc": "^0.23.20", | ||
"typescript": "^4.8.4" | ||
@@ -79,5 +80,6 @@ }, | ||
"files": [ | ||
"*.js", | ||
"*.d.ts", | ||
"zig" | ||
"./*.js", | ||
"./*.d.ts", | ||
"zig", | ||
"generated" | ||
], | ||
@@ -100,3 +102,3 @@ "exports": { | ||
}, | ||
"gitHead": "a4b60163a8caddceed5ec1b6b3584d164f61e7b6\n" | ||
"gitHead": "044ee6a3895720fc78e115032d4d831b63510929\n" | ||
} |
124
README.md
@@ -32,3 +32,3 @@ <!-- This file is generated - DO NOT EDIT! --> | ||
This package provides a minimal, but already quite usable TypeScript core API | ||
This package provides a small TypeScript core API | ||
and related [Ziglang](https://ziglang.org) bindings for UI & DOM | ||
@@ -39,4 +39,4 @@ creation/manipulation via WebAssembly. | ||
- Fully declarative or imperative DOM tree creation & manipulation | ||
- ID handle management for WASM created DOM elements & listeners | ||
- Declarative & imperative DOM tree creation | ||
- Canvas element creation (with HDPI support, see | ||
@@ -61,5 +61,5 @@ [@thi.ng/adapt-dpi](https://github.com/thi-ng/umbrella/blob/develop/packages/adapt-dpi)) | ||
Before the Zig WASM API module can be used, it must be initialized with a | ||
standard `std.mem.Allocator`. The currently recommended pattern looks something | ||
like this: | ||
Before the Zig WASM API module can be used, it must be initialized | ||
(automatically or manually) with a standard `std.mem.Allocator`. The current | ||
recommended pattern looks something like this: | ||
@@ -85,4 +85,6 @@ ```zig | ||
fn init() !void { | ||
// the DOM API module must always be intialized first! | ||
try dom.init(WASM_ALLOCATOR); | ||
// all WASM API modules auto-initialize themselves if the root source | ||
// file exposes a `WASM_ALLOCATOR`, otherwise you'll have to initialize manually: | ||
// try dom.init(customAllocator); | ||
// ... | ||
@@ -117,31 +119,41 @@ } | ||
Single DOM elements and entire element trees can be created via the | ||
`createElement()` function: | ||
Single DOM elements and entire element trees (incl. event handler setup and | ||
custom attributes) can be created via the `createElement()` function: | ||
```zig | ||
const dom = @import("wasmdom"); | ||
const dom = @import("dom"); | ||
// snippet taken from the zig-todo-list example project | ||
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", | ||
}, | ||
}, | ||
}, | ||
}, | ||
// element name | ||
.tag = "div", | ||
// CSS classes | ||
.class = "flex flex-column mb3", | ||
// nested child elements | ||
.children = &.{ | ||
.{ .tag = "h3", .text = "Add new task" }, | ||
.{ | ||
.tag = "input", | ||
// element's ID attribute | ||
.id = "newtask", | ||
// attribute & event listener definitions | ||
.attribs = &.{ | ||
dom.Attrib.string("placeholder", "What needs to be done?"), | ||
dom.Attrib.flag("autofocus", true), | ||
// event listener setup: | ||
// .ctx is an optional opaque pointer to arbitrary user state/context | ||
dom.Attrib.event("keydown", .{ .callback = onKeydown, .ctx = &STATE }), | ||
dom.Attrib.event("input", .{ .callback = onInput }), | ||
}, | ||
}, | ||
.{ | ||
.tag = "button", | ||
// Element .innerText content | ||
.text = "Add Task", | ||
.attribs = &.{ | ||
dom.Attrib.event("click", .{ .callback = onAddTask }), | ||
}, | ||
}, | ||
}, | ||
}); | ||
@@ -156,4 +168,6 @@ ``` | ||
Attributes can be provided as part of the `CreateElementOpts` and/or accessed imperatively: | ||
As already shown above, attributes can be provided as part of the `CreateElementOpts` and/or accessed imperatively: | ||
Zig example: | ||
```zig | ||
@@ -163,5 +177,8 @@ // creating & configuring an <input type="range"> element | ||
.tag = "input", | ||
.parent = toolbar, | ||
.parent = dom.body, | ||
// optional attrib declarations | ||
.attribs = &.{ | ||
// string attrib | ||
dom.Attrib.string("type", "range"), | ||
// numeric attribs | ||
dom.Attrib.number("min", 0), | ||
@@ -171,2 +188,4 @@ dom.Attrib.number("max", 100), | ||
dom.Attrib.number("value", 20), | ||
// boolean attrib (only will be created if true) | ||
dom.Attrib.flag("disabled", true), | ||
}, | ||
@@ -190,16 +209,15 @@ }); | ||
A more advanced version of the following click counter button component (written | ||
in Zig) can be seen in action in the | ||
A more advanced version of the following click counter button component can be | ||
seen in action in the | ||
[zig-counter](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-counter) | ||
example project. | ||
example project. Also check other supplied Zig examples for more realworld | ||
[usage examples](#usage-examples). | ||
```zig | ||
const wasm = @import("wasmapi"); | ||
const dom = @import("wasmdom"); | ||
const dom = @import("dom"); | ||
/// Simple click counter component | ||
const Counter = struct { | ||
listener: dom.EventListener, | ||
elementID: i32, | ||
listenerID: u16, | ||
clicks: usize, | ||
@@ -217,9 +235,11 @@ step: usize, | ||
.tag = "button", | ||
.class = "db w5 ma2 tc", | ||
// Tachyons CSS class names | ||
.class = "db w5 ma2 pa2 tc bn", | ||
.text = "click me!", | ||
.parent = parent, | ||
.attribs = &.{ | ||
// define & add click event listener w/ user context arg | ||
dom.Attrib.event("click", .{ .callback = onClick, .ctx = self }), | ||
}, | ||
}); | ||
// define & add click event listener w/ user context arg | ||
self.listener = .{ .callback = onClick, .ctx = self }; | ||
self.listenerID = try dom.addListener(self.elementID, "click", &self.listener); | ||
} | ||
@@ -230,3 +250,3 @@ | ||
var buf: [32]u8 = undefined; | ||
var label = std.fmt.bufPrint(&buf, "clicks: {d:0>4}", .{self.clicks}) catch return; | ||
var label = std.fmt.bufPrintZ(&buf, "clicks: {d:0>4}", .{self.clicks}) catch return; | ||
// update DOM element | ||
@@ -276,3 +296,3 @@ dom.setInnerText(self.elementID, label); | ||
Package sizes (gzipped, pre-treeshake): ESM: 4.19 KB | ||
Package sizes (gzipped, pre-treeshake): ESM: 4.59 KB | ||
@@ -283,5 +303,6 @@ ## Dependencies | ||
- [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api) | ||
- [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks) | ||
- [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/develop/packages/errors) | ||
- [@thi.ng/prefixes](https://github.com/thi-ng/umbrella/tree/develop/packages/prefixes) | ||
- [@thi.ng/wasm-api](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api) | ||
- [@thi.ng/wasm-api-bindgen](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-bindgen) | ||
@@ -296,6 +317,7 @@ ## Usage examples | ||
| Screenshot | Description | Live demo | Source | | ||
|:-------------------------------------------------------------------------------------------------------------------|:--------------------------------------------|:--------------------------------------------------|:-------------------------------------------------------------------------------| | ||
| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-canvas.png" width="240"/> | Zig-based DOM creation & canvas drawing app | [Demo](https://demo.thi.ng/umbrella/zig-canvas/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-canvas) | | ||
| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-counter.png" width="240"/> | Simple Zig/WASM click counter DOM component | [Demo](https://demo.thi.ng/umbrella/zig-counter/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-counter) | | ||
| Screenshot | Description | Live demo | Source | | ||
|:---------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------|:----------------------------------------------------|:---------------------------------------------------------------------------------| | ||
| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-canvas.png" width="240"/> | Zig-based DOM creation & canvas drawing app | [Demo](https://demo.thi.ng/umbrella/zig-canvas/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-canvas) | | ||
| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-counter.png" width="240"/> | Simple Zig/WASM click counter DOM component | [Demo](https://demo.thi.ng/umbrella/zig-counter/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-counter) | | ||
| <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-todo-list.png" width="240"/> | Zig-based To-Do list, DOM creation, local storage task persistence | [Demo](https://demo.thi.ng/umbrella/zig-todo-list/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-todo-list) | | ||
@@ -302,0 +324,0 @@ ## API |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
117601
347
6
16
2010
+ Added@thi.ng/prefixes@^2.1.13
+ Added@thi.ng/prefixes@2.3.31(transitive)
+ Added@thi.ng/wasm-api@1.6.7(transitive)
+ Added@thi.ng/wasm-api-bindgen@0.1.0(transitive)
- Removed@thi.ng/checks@^3.3.2
- Removed@thi.ng/wasm-api@0.18.1(transitive)
Updated@thi.ng/adapt-dpi@^2.2.2
Updated@thi.ng/api@^8.4.6
Updated@thi.ng/errors@^2.2.4
Updated@thi.ng/wasm-api@^1.0.0