taffy-wasm 0.9.2 → 0.9.5

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,421 @@
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,
54
- };
55
-
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
- };
69
-
70
- // 3. Create Tree Nodes
71
- // Leaf nodes
72
- const child1 = new_leaf(boxStyle);
73
- const child2 = new_leaf(boxStyle);
74
-
75
- // Root node with children
76
- const root = new_with_children(rootStyle, [child1, child2]);
77
-
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
- });
85
-
86
- // 5. Retrieve Results
87
- const rootLayout = get_layout(root);
88
- const child1Layout = get_layout(child1);
89
- const child2Layout = get_layout(child2);
90
-
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 }
94
- }
95
-
96
- run();
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: 300, height: 200 };
48
+ containerStyle.padding = { left: 10, right: 10, top: 10, bottom: 10 };
49
+
50
+ // Create child styles
51
+ const childStyle = new Style();
52
+ childStyle.flexGrow = 1;
53
+ childStyle.size = { width: "100%", height: "auto" };
54
+
55
+ // Create nodes
56
+ const child1 = tree.newLeaf(childStyle);
57
+ const child2 = tree.newLeaf(childStyle);
58
+ const container = tree.newWithChildren(
59
+ containerStyle,
60
+ BigUint64Array.from([child1, child2]),
61
+ );
62
+
63
+ // Compute layout
64
+ tree.computeLayout(container, { width: 300, height: 200 });
65
+
66
+ // Read computed layouts
67
+ const containerLayout = tree.getLayout(container);
68
+ const child1Layout = tree.getLayout(child1);
69
+ const child2Layout = tree.getLayout(child2);
70
+
71
+ console.log(`Container: ${containerLayout.width}x${containerLayout.height}`);
72
+ console.log(
73
+ `Child 1: ${child1Layout.width}x${child1Layout.height} at (${child1Layout.x}, ${child1Layout.y})`,
74
+ );
75
+ console.log(
76
+ `Child 2: ${child2Layout.width}x${child2Layout.height} at (${child2Layout.x}, ${child2Layout.y})`,
77
+ );
78
+ }
79
+
80
+ main();
97
81
  ```
98
82
 
99
- ## 📐 Architecture
83
+ ## 📖 API Reference
100
84
 
101
- `taffy-js` acts as a thin wrapper around the [Taffy](https://github.com/DioxusLabs/taffy) Rust crate.
85
+ ### TaffyTree
102
86
 
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.
87
+ The main class for managing layout trees.
107
88
 
108
- ## 📚 API Reference
89
+ ```typescript
90
+ class TaffyTree {
91
+ // Construction
92
+ constructor();
93
+ static withCapacity(capacity: number): TaffyTree;
94
+
95
+ // Node Creation (throws TaffyError on failure)
96
+ newLeaf(style: Style): bigint;
97
+ newLeafWithContext(style: Style, context: any): bigint;
98
+ newWithChildren(style: Style, children: BigUint64Array): bigint;
99
+
100
+ // Tree Operations
101
+ clear(): void;
102
+ remove(node: bigint): bigint; // throws TaffyError
103
+ totalNodeCount(): number;
104
+
105
+ // Child Management (throws TaffyError on failure)
106
+ addChild(parent: bigint, child: bigint): void;
107
+ removeChild(parent: bigint, child: bigint): bigint;
108
+ setChildren(parent: bigint, children: BigUint64Array): void;
109
+ children(parent: bigint): BigUint64Array;
110
+ childCount(parent: bigint): number;
111
+ parent(child: bigint): bigint | undefined;
112
+
113
+ // Style Management (throws TaffyError on failure)
114
+ setStyle(node: bigint, style: Style): void;
115
+ getStyle(node: bigint): Style;
116
+
117
+ // Layout Computation (throws TaffyError on failure)
118
+ computeLayout(node: bigint, availableSpace: Size<AvailableSpace>): void;
119
+ computeLayoutWithMeasure(
120
+ node: bigint,
121
+ availableSpace: Size<AvailableSpace>,
122
+ measureFunc: MeasureFunction,
123
+ ): void;
124
+
125
+ // Layout Results (throws TaffyError on failure)
126
+ getLayout(node: bigint): Layout;
127
+ unroundedLayout(node: bigint): Layout;
128
+
129
+ // Dirty Tracking (throws TaffyError on failure)
130
+ markDirty(node: bigint): void;
131
+ dirty(node: bigint): boolean;
132
+
133
+ // Configuration
134
+ enableRounding(): void;
135
+ disableRounding(): void;
136
+ }
137
+ ```
109
138
 
110
- ### Lifecycle
139
+ ### Style
140
+
141
+ Configuration object for node layout properties.
142
+
143
+ ```typescript
144
+ class Style {
145
+ constructor();
146
+
147
+ // Layout Mode
148
+ display: Display; // Block, Flex, Grid, None
149
+ position: Position; // Relative, Absolute
150
+
151
+ // Flexbox
152
+ flexDirection: FlexDirection; // Row, Column, RowReverse, ColumnReverse
153
+ flexWrap: FlexWrap; // NoWrap, Wrap, WrapReverse
154
+ flexGrow: number; // Growth factor (default: 0)
155
+ flexShrink: number; // Shrink factor (default: 1)
156
+ flexBasis: Dimension; // Initial size
157
+
158
+ // Alignment
159
+ alignItems: AlignItems | undefined;
160
+ alignSelf: AlignSelf | undefined;
161
+ alignContent: AlignContent | undefined;
162
+ justifyContent: JustifyContent | undefined;
163
+
164
+ // Sizing
165
+ size: Size<Dimension>; // Width and height
166
+ minSize: Size<Dimension>; // Minimum constraints
167
+ maxSize: Size<Dimension>; // Maximum constraints
168
+ aspectRatio: number | undefined; // Width/height ratio
169
+ boxSizing: BoxSizing; // BorderBox, ContentBox
170
+
171
+ // Spacing
172
+ margin: Rect<LengthPercentageAuto>;
173
+ padding: Rect<LengthPercentage>;
174
+ border: Rect<LengthPercentage>;
175
+ gap: Size<LengthPercentage>; // Row and column gap
176
+ inset: Rect<LengthPercentageAuto>; // For absolute positioning
177
+
178
+ // Overflow
179
+ overflow: Point<Overflow>;
180
+ }
181
+ ```
111
182
 
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. |
183
+ ### Layout
116
184
 
117
- ### Node Management
185
+ Read-only computed layout result.
118
186
 
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. |
187
+ ```typescript
188
+ class Layout {
189
+ // Position (relative to parent)
190
+ readonly x: number;
191
+ readonly y: number;
192
+
193
+ // Size
194
+ readonly width: number;
195
+ readonly height: number;
196
+
197
+ // Content size (for scrollable content)
198
+ readonly contentWidth: number;
199
+ readonly contentHeight: number;
200
+
201
+ // Spacing
202
+ readonly paddingTop: number;
203
+ readonly paddingRight: number;
204
+ readonly paddingBottom: number;
205
+ readonly paddingLeft: number;
206
+
207
+ readonly borderTop: number;
208
+ readonly borderRight: number;
209
+ readonly borderBottom: number;
210
+ readonly borderLeft: number;
211
+
212
+ readonly marginTop: number;
213
+ readonly marginRight: number;
214
+ readonly marginBottom: number;
215
+ readonly marginLeft: number;
216
+
217
+ // Scrollbars
218
+ readonly scrollbarWidth: number;
219
+ readonly scrollbarHeight: number;
220
+
221
+ // Rendering order
222
+ readonly order: number;
223
+ }
224
+ ```
127
225
 
128
- ### Layout & Style
226
+ ### Enums
129
227
 
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). |
228
+ ```typescript
229
+ enum Display {
230
+ Block,
231
+ Flex,
232
+ Grid,
233
+ None,
234
+ }
235
+ enum Position {
236
+ Relative,
237
+ Absolute,
238
+ }
239
+ enum FlexDirection {
240
+ Row,
241
+ Column,
242
+ RowReverse,
243
+ ColumnReverse,
244
+ }
245
+ enum FlexWrap {
246
+ NoWrap,
247
+ Wrap,
248
+ WrapReverse,
249
+ }
250
+ enum AlignItems {
251
+ Start,
252
+ End,
253
+ FlexStart,
254
+ FlexEnd,
255
+ Center,
256
+ Baseline,
257
+ Stretch,
258
+ }
259
+ enum AlignSelf {
260
+ Auto,
261
+ Start,
262
+ End,
263
+ FlexStart,
264
+ FlexEnd,
265
+ Center,
266
+ Baseline,
267
+ Stretch,
268
+ }
269
+ enum AlignContent {
270
+ Start,
271
+ End,
272
+ FlexStart,
273
+ FlexEnd,
274
+ Center,
275
+ Stretch,
276
+ SpaceBetween,
277
+ SpaceAround,
278
+ SpaceEvenly,
279
+ }
280
+ enum JustifyContent {
281
+ Start,
282
+ End,
283
+ FlexStart,
284
+ FlexEnd,
285
+ Center,
286
+ Stretch,
287
+ SpaceBetween,
288
+ SpaceAround,
289
+ SpaceEvenly,
290
+ }
291
+ enum Overflow {
292
+ Visible,
293
+ Hidden,
294
+ Scroll,
295
+ Auto,
296
+ }
297
+ enum BoxSizing {
298
+ BorderBox,
299
+ ContentBox,
300
+ }
301
+ ```
136
302
 
137
- ### Type Definitions
303
+ ### Types
138
304
 
139
- #### `Style`
305
+ ```typescript
306
+ // Dimension values (CSS-like syntax)
307
+ type Dimension = number | `${number}%` | "auto"; // e.g., 100, "50%", "auto"
308
+ type LengthPercentage = number | `${number}%`; // e.g., 10, "25%"
309
+ type LengthPercentageAuto = number | `${number}%` | "auto";
310
+
311
+ // Geometry
312
+ interface Size<T> {
313
+ width: T;
314
+ height: T;
315
+ }
316
+ interface Rect<T> {
317
+ left: T;
318
+ right: T;
319
+ top: T;
320
+ bottom: T;
321
+ }
322
+ interface Point<T> {
323
+ x: T;
324
+ y: T;
325
+ }
140
326
 
141
- A comprehensive object mirroring CSS properties.
327
+ // Available space for layout computation
328
+ type AvailableSpace = number | "MinContent" | "MaxContent";
329
+
330
+ // Measure function for custom content measurement
331
+ type MeasureFunction = (
332
+ knownDimensions: Size<number | null>,
333
+ availableSpace: Size<AvailableSpace>,
334
+ node: bigint,
335
+ context: any,
336
+ style: Style,
337
+ ) => Size<number>;
338
+ ```
142
339
 
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.
340
+ ## 📐 Custom Text Measurement
341
+
342
+ For text nodes or other content that needs dynamic measurement:
343
+
344
+ ```javascript
345
+ const textNode = tree.newLeafWithContext(textStyle, { text: "Hello, World!" });
346
+
347
+ tree.computeLayoutWithMeasure(
348
+ rootNode,
349
+ { width: 800, height: "MaxContent" },
350
+ (known, available, node, context, style) => {
351
+ if (context?.text) {
352
+ // Your text measurement logic here
353
+ const width = measureTextWidth(context.text);
354
+ const height = measureTextHeight(context.text, available.width);
355
+ return { width, height };
356
+ }
357
+ return { width: 0, height: 0 };
358
+ },
359
+ );
360
+ ```
147
361
 
148
- #### `AvailableSpace`
362
+ ## 🔧 Error Handling
149
363
 
150
- Used when triggering computation.
364
+ Methods that can fail throw a `TaffyError` as a JavaScript exception. Use try-catch to handle errors:
151
365
 
152
- ```typescript
153
- interface AvailableSpace {
154
- width: number | null; // null = unlimited/content-based
155
- height: number | null; // null = unlimited/content-based
366
+ ```javascript
367
+ try {
368
+ const nodeId = tree.newLeaf(style);
369
+ console.log("Created node:", nodeId);
370
+ } catch (error) {
371
+ // error is a TaffyError instance
372
+ console.error("Error:", error.message);
156
373
  }
157
374
  ```
158
375
 
159
- ## 🛠 Building from Source
376
+ ## 🌐 Browser Support
377
+
378
+ Taffy-JS works in all modern browsers that support WebAssembly:
379
+
380
+ - Chrome 57+
381
+ - Firefox 52+
382
+ - Safari 11+
383
+ - Edge 16+
384
+
385
+ ## 📚 Examples
386
+
387
+ ### Flexbox Row Layout
388
+
389
+ ```javascript
390
+ const rowStyle = new Style();
391
+ rowStyle.display = Display.Flex;
392
+ rowStyle.flexDirection = FlexDirection.Row;
393
+ rowStyle.justifyContent = JustifyContent.SpaceBetween;
394
+ rowStyle.gap = { width: 10, height: 0 };
395
+ ```
396
+
397
+ ### Absolute Positioning
398
+
399
+ ```javascript
400
+ const absoluteStyle = new Style();
401
+ absoluteStyle.position = Position.Absolute;
402
+ absoluteStyle.inset = { left: 10, top: 10, right: "auto", bottom: "auto" };
403
+ absoluteStyle.size = { width: 100, height: 50 };
404
+ ```
405
+
406
+ ### Percentage Sizing
407
+
408
+ ```javascript
409
+ const percentStyle = new Style();
410
+ percentStyle.size = {
411
+ width: "50%", // 50% of parent
412
+ height: "100%", // 100% of parent
413
+ };
414
+ ```
415
+
416
+ ## 🏗️ Building from Source
417
+
418
+ ```bash
419
+ # Clone the repository
420
+ git clone https://github.com/ByteLandTechnology/taffy-js.git
421
+ cd taffy-js
160
422
 
161
- If you want to contribute or build the WASM binary yourself:
423
+ # Install dependencies
424
+ npm install
162
425
 
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.
426
+ # Build the WebAssembly module
427
+ npm run build
428
+
429
+ # Run tests
430
+ npm test
431
+ ```
173
432
 
174
433
  ## 📄 License
175
434
 
176
- MIT License © 2024 ByteLand Technology
435
+ MIT License - see [LICENSE](LICENSE) for details.
436
+
437
+ ## 🙏 Acknowledgments
177
438
 
178
- This project wraps [Taffy](https://github.com/DioxusLabs/taffy), which is also MIT licensed.
439
+ - [Taffy](https://github.com/DioxusLabs/taffy) - The Rust layout engine this project wraps
440
+ - [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.5",
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",