taffy-wasm 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,18 @@
1
- # Taffy WebAssembly Bindings
1
+ # Taffy-JS
2
2
 
3
- > **High-performance Flexbox and CSS Grid layout for JavaScript/TypeScript, powered by Rust and WebAssembly.**
3
+ [![npm version](https://badge.fury.io/js/taffy-js.svg)](https://www.npmjs.com/package/taffy-js)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
5
 
5
- ![License](https://img.shields.io/npm/l/taffy-js?style=flat-square) ![Version](https://img.shields.io/npm/v/taffy-js?style=flat-square) ![WASM](https://img.shields.io/badge/platform-wasm-blueviolet?style=flat-square)
6
-
7
- **Taffy** is a generic, high-performance UI layout library written in Rust. This package (`taffy-js`) provides WebAssembly bindings, allowing you to use Taffy's standards-compliant Flexbox and Grid algorithms directly in your web or Node.js applications with near-native performance.
6
+ High-performance WebAssembly bindings for the [Taffy](https://github.com/DioxusLabs/taffy) layout engine, bringing CSS Flexbox and Grid layout algorithms to JavaScript with near-native performance.
8
7
 
9
8
  ## ✨ Features
10
9
 
11
- - **🚀 High Performance**: Leverages the speed of Rust and WebAssembly for complex layout computations.
12
- - **📦 Tiny Footprint**: Highly optimized WASM binary size.
13
- - **🎨 Modern Layouts**: Full support for **Flexbox** and **CSS Grid** specifications.
14
- - **🛠 Framework Agnostic**: Use it with React, Vue, Svelte, vanilla JS, or even in Node.js for server-side layout calculation.
15
- - **🔒 Type-Safe**: Fully typed API with TypeScript definitions included.
10
+ - **🚀 High Performance**: WebAssembly-powered layout calculations
11
+ - **📦 Complete CSS Support**: Full Flexbox and CSS Grid implementation
12
+ - **🔧 Custom Measurement**: Support for custom text/content measurement callbacks
13
+ - **📝 TypeScript Ready**: Complete type definitions included
14
+ - **🌳 Tree-Based API**: Efficient tree structure for complex layouts
15
+ - **💡 Familiar API**: CSS-like property names and values
16
16
 
17
17
  ## 📦 Installation
18
18
 
@@ -20,159 +20,434 @@
20
20
  npm install taffy-js
21
21
  ```
22
22
 
23
- > **Note**: This package relies on WebAssembly. Ensure your runtime (modern browser or Node.js) supports WASM.
24
-
25
- ## 🚀 Usage
23
+ ## 🚀 Quick Start
26
24
 
27
- Here is a complete example showing how to create a layout tree, style nodes, and compute results.
28
-
29
- ```typescript
30
- import init, {
31
- new_leaf,
32
- new_with_children,
33
- compute_layout,
34
- get_layout,
25
+ ```javascript
26
+ import {
27
+ loadTaffy,
28
+ TaffyTree,
29
+ Style,
35
30
  Display,
36
31
  FlexDirection,
37
32
  AlignItems,
38
- JustifyContent,
39
- Style,
40
33
  } from "taffy-js";
41
34
 
42
- async function run() {
43
- // 1. Initialize the WASM module
44
- await init();
45
-
46
- // 2. Define Styles
47
- // Styles match CSS properties. You can mix specific units like Pixels, Percent, or Auto.
48
- const boxStyle: Style = {
49
- display: Display.Flex,
50
- width: { value: 100, unit: "Pixels" },
51
- height: { value: 100, unit: "Pixels" },
52
- justify_content: JustifyContent.Center,
53
- align_items: AlignItems.Center,
35
+ async function main() {
36
+ // Initialize WebAssembly module
37
+ await loadTaffy();
38
+
39
+ // Create a layout tree
40
+ const tree = new TaffyTree();
41
+
42
+ // Create container style
43
+ const containerStyle = new Style();
44
+ containerStyle.display = Display.Flex;
45
+ containerStyle.flexDirection = FlexDirection.Column;
46
+ containerStyle.alignItems = AlignItems.Center;
47
+ containerStyle.size = { width: { Length: 300 }, height: { Length: 200 } };
48
+ containerStyle.padding = {
49
+ left: { Length: 10 },
50
+ right: { Length: 10 },
51
+ top: { Length: 10 },
52
+ bottom: { Length: 10 },
54
53
  };
55
54
 
56
- const rootStyle: Style = {
57
- display: Display.Flex,
58
- // layout 500x500 container
59
- width: { value: 500, unit: "Pixels" },
60
- height: { value: 500, unit: "Pixels" },
61
- flex_direction: FlexDirection.Row,
62
- justify_content: JustifyContent.SpaceAround,
63
- align_items: AlignItems.Center,
64
- gap: {
65
- width: { value: 20, unit: "Pixels" },
66
- height: { value: 0, unit: "Pixels" },
67
- }, // Example of potential gap usage if supported
68
- };
55
+ // Create child styles
56
+ const childStyle = new Style();
57
+ childStyle.flexGrow = 1;
58
+ childStyle.size = { width: { Percent: 100 }, height: "Auto" };
59
+
60
+ // Create nodes
61
+ const child1 = tree.newLeaf(childStyle);
62
+ const child2 = tree.newLeaf(childStyle);
63
+ const container = tree.newWithChildren(
64
+ containerStyle,
65
+ BigUint64Array.from([child1, child2]),
66
+ );
67
+
68
+ // Compute layout
69
+ tree.computeLayout(container, {
70
+ width: { Definite: 300 },
71
+ height: { Definite: 200 },
72
+ });
69
73
 
70
- // 3. Create Tree Nodes
71
- // Leaf nodes
72
- const child1 = new_leaf(boxStyle);
73
- const child2 = new_leaf(boxStyle);
74
+ // Read computed layouts
75
+ const containerLayout = tree.getLayout(container);
76
+ const child1Layout = tree.getLayout(child1);
77
+ const child2Layout = tree.getLayout(child2);
78
+
79
+ console.log(`Container: ${containerLayout.width}x${containerLayout.height}`);
80
+ console.log(
81
+ `Child 1: ${child1Layout.width}x${child1Layout.height} at (${child1Layout.x}, ${child1Layout.y})`,
82
+ );
83
+ console.log(
84
+ `Child 2: ${child2Layout.width}x${child2Layout.height} at (${child2Layout.x}, ${child2Layout.y})`,
85
+ );
86
+ }
74
87
 
75
- // Root node with children
76
- const root = new_with_children(rootStyle, [child1, child2]);
88
+ main();
89
+ ```
77
90
 
78
- // 4. Compute Layout
79
- // You can pass available space constraints (width/height).
80
- // Passing values corresponds to "Definite" size, null corresponds to "MaxContent/Available".
81
- compute_layout(root, {
82
- width: 500,
83
- height: 500,
84
- });
91
+ ## 📖 API Reference
85
92
 
86
- // 5. Retrieve Results
87
- const rootLayout = get_layout(root);
88
- const child1Layout = get_layout(child1);
89
- const child2Layout = get_layout(child2);
93
+ ### TaffyTree
90
94
 
91
- console.log("Root:", rootLayout); // { x: 0, y: 0, width: 500, height: 500 }
92
- console.log("Child 1:", child1Layout); // { x: ..., y: ..., width: 100, height: 100 }
93
- console.log("Child 2:", child2Layout); // { x: ..., y: ..., width: 100, height: 100 }
95
+ The main class for managing layout trees.
96
+
97
+ ```typescript
98
+ class TaffyTree {
99
+ // Construction
100
+ constructor();
101
+ static withCapacity(capacity: number): TaffyTree;
102
+
103
+ // Node Creation (throws TaffyError on failure)
104
+ newLeaf(style: Style): bigint;
105
+ newLeafWithContext(style: Style, context: any): bigint;
106
+ newWithChildren(style: Style, children: BigUint64Array): bigint;
107
+
108
+ // Tree Operations
109
+ clear(): void;
110
+ remove(node: bigint): bigint; // throws TaffyError
111
+ totalNodeCount(): number;
112
+
113
+ // Child Management (throws TaffyError on failure)
114
+ addChild(parent: bigint, child: bigint): void;
115
+ removeChild(parent: bigint, child: bigint): bigint;
116
+ setChildren(parent: bigint, children: BigUint64Array): void;
117
+ children(parent: bigint): BigUint64Array;
118
+ childCount(parent: bigint): number;
119
+ parent(child: bigint): bigint | undefined;
120
+
121
+ // Style Management (throws TaffyError on failure)
122
+ setStyle(node: bigint, style: Style): void;
123
+ getStyle(node: bigint): Style;
124
+
125
+ // Layout Computation (throws TaffyError on failure)
126
+ computeLayout(node: bigint, availableSpace: Size<AvailableSpace>): void;
127
+ computeLayoutWithMeasure(
128
+ node: bigint,
129
+ availableSpace: Size<AvailableSpace>,
130
+ measureFunc: MeasureFunction,
131
+ ): void;
132
+
133
+ // Layout Results (throws TaffyError on failure)
134
+ getLayout(node: bigint): Layout;
135
+ unroundedLayout(node: bigint): Layout;
136
+
137
+ // Dirty Tracking (throws TaffyError on failure)
138
+ markDirty(node: bigint): void;
139
+ dirty(node: bigint): boolean;
140
+
141
+ // Configuration
142
+ enableRounding(): void;
143
+ disableRounding(): void;
94
144
  }
145
+ ```
95
146
 
96
- run();
147
+ ### Style
148
+
149
+ Configuration object for node layout properties.
150
+
151
+ ```typescript
152
+ class Style {
153
+ constructor();
154
+
155
+ // Layout Mode
156
+ display: Display; // Block, Flex, Grid, None
157
+ position: Position; // Relative, Absolute
158
+
159
+ // Flexbox
160
+ flexDirection: FlexDirection; // Row, Column, RowReverse, ColumnReverse
161
+ flexWrap: FlexWrap; // NoWrap, Wrap, WrapReverse
162
+ flexGrow: number; // Growth factor (default: 0)
163
+ flexShrink: number; // Shrink factor (default: 1)
164
+ flexBasis: Dimension; // Initial size
165
+
166
+ // Alignment
167
+ alignItems: AlignItems | undefined;
168
+ alignSelf: AlignSelf | undefined;
169
+ alignContent: AlignContent | undefined;
170
+ justifyContent: JustifyContent | undefined;
171
+
172
+ // Sizing
173
+ size: Size<Dimension>; // Width and height
174
+ minSize: Size<Dimension>; // Minimum constraints
175
+ maxSize: Size<Dimension>; // Maximum constraints
176
+ aspectRatio: number | undefined; // Width/height ratio
177
+ boxSizing: BoxSizing; // BorderBox, ContentBox
178
+
179
+ // Spacing
180
+ margin: Rect<LengthPercentageAuto>;
181
+ padding: Rect<LengthPercentage>;
182
+ border: Rect<LengthPercentage>;
183
+ gap: Size<LengthPercentage>; // Row and column gap
184
+ inset: Rect<LengthPercentageAuto>; // For absolute positioning
185
+
186
+ // Overflow
187
+ overflow: Point<Overflow>;
188
+ }
97
189
  ```
98
190
 
99
- ## 📐 Architecture
191
+ ### Layout
192
+
193
+ Read-only computed layout result.
194
+
195
+ ```typescript
196
+ class Layout {
197
+ // Position (relative to parent)
198
+ readonly x: number;
199
+ readonly y: number;
200
+
201
+ // Size
202
+ readonly width: number;
203
+ readonly height: number;
204
+
205
+ // Content size (for scrollable content)
206
+ readonly contentWidth: number;
207
+ readonly contentHeight: number;
208
+
209
+ // Spacing
210
+ readonly paddingTop: number;
211
+ readonly paddingRight: number;
212
+ readonly paddingBottom: number;
213
+ readonly paddingLeft: number;
214
+
215
+ readonly borderTop: number;
216
+ readonly borderRight: number;
217
+ readonly borderBottom: number;
218
+ readonly borderLeft: number;
219
+
220
+ readonly marginTop: number;
221
+ readonly marginRight: number;
222
+ readonly marginBottom: number;
223
+ readonly marginLeft: number;
224
+
225
+ // Scrollbars
226
+ readonly scrollbarWidth: number;
227
+ readonly scrollbarHeight: number;
228
+
229
+ // Rendering order
230
+ readonly order: number;
231
+ }
232
+ ```
233
+
234
+ ### Enums
235
+
236
+ ```typescript
237
+ enum Display {
238
+ Block,
239
+ Flex,
240
+ Grid,
241
+ None,
242
+ }
243
+ enum Position {
244
+ Relative,
245
+ Absolute,
246
+ }
247
+ enum FlexDirection {
248
+ Row,
249
+ Column,
250
+ RowReverse,
251
+ ColumnReverse,
252
+ }
253
+ enum FlexWrap {
254
+ NoWrap,
255
+ Wrap,
256
+ WrapReverse,
257
+ }
258
+ enum AlignItems {
259
+ Start,
260
+ End,
261
+ FlexStart,
262
+ FlexEnd,
263
+ Center,
264
+ Baseline,
265
+ Stretch,
266
+ }
267
+ enum AlignSelf {
268
+ Auto,
269
+ Start,
270
+ End,
271
+ FlexStart,
272
+ FlexEnd,
273
+ Center,
274
+ Baseline,
275
+ Stretch,
276
+ }
277
+ enum AlignContent {
278
+ Start,
279
+ End,
280
+ FlexStart,
281
+ FlexEnd,
282
+ Center,
283
+ Stretch,
284
+ SpaceBetween,
285
+ SpaceAround,
286
+ SpaceEvenly,
287
+ }
288
+ enum JustifyContent {
289
+ Start,
290
+ End,
291
+ FlexStart,
292
+ FlexEnd,
293
+ Center,
294
+ Stretch,
295
+ SpaceBetween,
296
+ SpaceAround,
297
+ SpaceEvenly,
298
+ }
299
+ enum Overflow {
300
+ Visible,
301
+ Hidden,
302
+ Scroll,
303
+ Auto,
304
+ }
305
+ enum BoxSizing {
306
+ BorderBox,
307
+ ContentBox,
308
+ }
309
+ ```
100
310
 
101
- `taffy-js` acts as a thin wrapper around the [Taffy](https://github.com/DioxusLabs/taffy) Rust crate.
311
+ ### Types
102
312
 
103
- 1. **Node Tree**: You build a flat tree of nodes in the WASM memory space using integer IDs (`u32`).
104
- 2. **Style Transfer**: Styles are serialized from JS objects to Rust structs via `serde-wasm-bindgen`.
105
- 3. **Computation**: Rust runs the layout algorithms (Flexbox/Grid).
106
- 4. **Readout**: You query the final computed geometry (x, y, width, height) back to JS.
313
+ ```typescript
314
+ // Dimension values
315
+ type Dimension = { Length: number } | { Percent: number } | "Auto";
316
+ type LengthPercentage = { Length: number } | { Percent: number };
317
+ type LengthPercentageAuto = { Length: number } | { Percent: number } | "Auto";
318
+
319
+ // Geometry
320
+ interface Size<T> {
321
+ width: T;
322
+ height: T;
323
+ }
324
+ interface Rect<T> {
325
+ left: T;
326
+ right: T;
327
+ top: T;
328
+ bottom: T;
329
+ }
330
+ interface Point<T> {
331
+ x: T;
332
+ y: T;
333
+ }
107
334
 
108
- ## 📚 API Reference
335
+ // Available space for layout computation
336
+ type AvailableSpace = { Definite: number } | "MinContent" | "MaxContent";
337
+
338
+ // Measure function for custom content measurement
339
+ type MeasureFunction = (
340
+ knownDimensions: Size<number | null>,
341
+ availableSpace: Size<AvailableSpace>,
342
+ node: bigint,
343
+ context: any,
344
+ style: Style,
345
+ ) => Size<number>;
346
+ ```
109
347
 
110
- ### Lifecycle
348
+ ## 📐 Custom Text Measurement
349
+
350
+ For text nodes or other content that needs dynamic measurement:
351
+
352
+ ```javascript
353
+ const textNode = tree.newLeafWithContext(textStyle, { text: "Hello, World!" });
354
+
355
+ tree.computeLayoutWithMeasure(
356
+ rootNode,
357
+ { width: { Definite: 800 }, height: "MaxContent" },
358
+ (known, available, node, context, style) => {
359
+ if (context?.text) {
360
+ // Your text measurement logic here
361
+ const width = measureTextWidth(context.text);
362
+ const height = measureTextHeight(context.text, available.width);
363
+ return { width, height };
364
+ }
365
+ return { width: 0, height: 0 };
366
+ },
367
+ );
368
+ ```
111
369
 
112
- | Function | Description |
113
- | :------- | :------------------------------------------------------------------------------ |
114
- | `init()` | Initializes the WASM module. Must be awaited before calling any other function. |
115
- | `free()` | (Optional) Manually free memory if required by your specific WASM loader setup. |
370
+ ## 🔧 Error Handling
116
371
 
117
- ### Node Management
372
+ Methods that can fail throw a `TaffyError` as a JavaScript exception. Use try-catch to handle errors:
118
373
 
119
- | Function | Signature | Description |
120
- | :------------------ | :--------------------------------------------- | :-------------------------------------- |
121
- | `new_leaf` | `(style: Style) -> number` | Creates a leaf node (no children). |
122
- | `new_with_children` | `(style: Style, children: number[]) -> number` | Creates a node containing child nodes. |
123
- | `add_child` | `(parent: number, child: number) -> void` | Appends a child to a parent. |
124
- | `remove_child` | `(parent: number, child: number) -> void` | Removes a specific child from a parent. |
125
- | `set_children` | `(parent: number, children: number[]) -> void` | Replaces all children of a node. |
126
- | `remove_node` | `(node: number) -> void` | Deletes a node and frees its memory. |
374
+ ```javascript
375
+ try {
376
+ const nodeId = tree.newLeaf(style);
377
+ console.log("Created node:", nodeId);
378
+ } catch (error) {
379
+ // error is a TaffyError instance
380
+ console.error("Error:", error.message);
381
+ }
382
+ ```
127
383
 
128
- ### Layout & Style
384
+ ## 🌐 Browser Support
129
385
 
130
- | Function | Signature | Description |
131
- | :--------------- | :---------------------------------------------- | :-------------------------------------------------------------------------- |
132
- | `set_style` | `(node: number, style: Style) -> void` | Updates the style properties of a node. |
133
- | `compute_layout` | `(root: number, space: AvailableSpace) -> void` | Triggers the layout calculation algorithm. |
134
- | `get_layout` | `(node: number) -> Layout` | Returns `{x, y, width, height}` for a node. |
135
- | `mark_dirty` | `(node: number) -> void` | Manually validates a node (usually handled automatically by style setters). |
386
+ Taffy-JS works in all modern browsers that support WebAssembly:
136
387
 
137
- ### Type Definitions
388
+ - Chrome 57+
389
+ - Firefox 52+
390
+ - Safari 11+
391
+ - Edge 16+
138
392
 
139
- #### `Style`
393
+ ## 📚 Examples
140
394
 
141
- A comprehensive object mirroring CSS properties.
395
+ ### Flexbox Row Layout
142
396
 
143
- - **Display**: `Display.Flex`, `Display.Grid`, `Display.None`
144
- - **Dimensions**: `{ value: number, unit: "Pixels" | "Percent" | "Auto" }`
145
- - **Flexbox**: `flex_direction`, `justify_content`, `align_items`, `flex_wrap`, etc.
146
- - **Grid**: `grid_template_rows`, `grid_template_columns`, etc.
397
+ ```javascript
398
+ const rowStyle = new Style();
399
+ rowStyle.display = Display.Flex;
400
+ rowStyle.flexDirection = FlexDirection.Row;
401
+ rowStyle.justifyContent = JustifyContent.SpaceBetween;
402
+ rowStyle.gap = { width: { Length: 10 }, height: { Length: 0 } };
403
+ ```
147
404
 
148
- #### `AvailableSpace`
405
+ ### Absolute Positioning
406
+
407
+ ```javascript
408
+ const absoluteStyle = new Style();
409
+ absoluteStyle.position = Position.Absolute;
410
+ absoluteStyle.inset = {
411
+ left: { Length: 10 },
412
+ top: { Length: 10 },
413
+ right: "Auto",
414
+ bottom: "Auto",
415
+ };
416
+ absoluteStyle.size = { width: { Length: 100 }, height: { Length: 50 } };
417
+ ```
149
418
 
150
- Used when triggering computation.
419
+ ### Percentage Sizing
151
420
 
152
- ```typescript
153
- interface AvailableSpace {
154
- width: number | null; // null = unlimited/content-based
155
- height: number | null; // null = unlimited/content-based
156
- }
421
+ ```javascript
422
+ const percentStyle = new Style();
423
+ percentStyle.size = {
424
+ width: { Percent: 50 }, // 50% of parent
425
+ height: { Percent: 100 }, // 100% of parent
426
+ };
157
427
  ```
158
428
 
159
- ## 🛠 Building from Source
429
+ ## 🏗️ Building from Source
430
+
431
+ ```bash
432
+ # Clone the repository
433
+ git clone https://github.com/user/taffy-js.git
434
+ cd taffy-js
435
+
436
+ # Install dependencies
437
+ npm install
160
438
 
161
- If you want to contribute or build the WASM binary yourself:
439
+ # Build the WebAssembly module
440
+ npm run build
162
441
 
163
- 1. **Prerequisites**: Install Rust and `wasm-pack`.
164
- ```bash
165
- curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
166
- ```
167
- 2. **Build**:
168
- ```bash
169
- npm install
170
- npm run build
171
- ```
172
- The artifacts will be generated in the `pkg/` directory.
442
+ # Run tests
443
+ npm test
444
+ ```
173
445
 
174
446
  ## 📄 License
175
447
 
176
- MIT License © 2024 ByteLand Technology
448
+ MIT License - see [LICENSE](LICENSE) for details.
449
+
450
+ ## 🙏 Acknowledgments
177
451
 
178
- This project wraps [Taffy](https://github.com/DioxusLabs/taffy), which is also MIT licensed.
452
+ - [Taffy](https://github.com/DioxusLabs/taffy) - The Rust layout engine this project wraps
453
+ - [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) - Rust/WebAssembly interoperability
package/package.json CHANGED
@@ -2,16 +2,14 @@
2
2
  "name": "taffy-wasm",
3
3
  "type": "module",
4
4
  "collaborators": [
5
- "Alice Cecile <alice.i.cecile@gmail.com>",
6
- "Johnathan Kelley <jkelleyrtp@gmail.com>",
7
- "Nico Burns <nico@nicoburns.com>"
5
+ "ByteLandTechnology <github@byteland.app>"
8
6
  ],
9
7
  "description": "WebAssembly bindings for Taffy layout library",
10
- "version": "0.9.2",
8
+ "version": "0.9.4",
11
9
  "license": "MIT",
12
10
  "repository": {
13
11
  "type": "git",
14
- "url": "https://github.com/DioxusLabs/taffy"
12
+ "url": "https://github.com/ByteLandTechnology/taffy-js"
15
13
  },
16
14
  "files": [
17
15
  "taffy_wasm_bg.wasm",