flexily 0.5.2 → 0.6.0

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.
Files changed (50) hide show
  1. package/dist/chunk-CBBoxR_p.mjs +26 -0
  2. package/dist/constants-BNURa6H7.mjs +65 -0
  3. package/dist/constants-BNURa6H7.mjs.map +1 -0
  4. package/dist/constants-D7ythAJC.d.mts +64 -0
  5. package/dist/constants-D7ythAJC.d.mts.map +1 -0
  6. package/{src/classic/node.ts → dist/index-classic.d.mts} +118 -619
  7. package/dist/index-classic.d.mts.map +1 -0
  8. package/dist/index-classic.mjs +1909 -0
  9. package/dist/index-classic.mjs.map +1 -0
  10. package/dist/index.d.mts +195 -0
  11. package/dist/index.d.mts.map +1 -0
  12. package/dist/index.mjs +3279 -0
  13. package/dist/index.mjs.map +1 -0
  14. package/dist/node-zero-75maLs2s.d.mts +762 -0
  15. package/dist/node-zero-75maLs2s.d.mts.map +1 -0
  16. package/dist/src-BWyhokNZ.mjs +692 -0
  17. package/dist/src-BWyhokNZ.mjs.map +1 -0
  18. package/dist/src-DdSLylRA.mjs +816 -0
  19. package/dist/src-DdSLylRA.mjs.map +1 -0
  20. package/dist/testing.d.mts +55 -0
  21. package/dist/testing.d.mts.map +1 -0
  22. package/dist/testing.mjs +154 -0
  23. package/dist/testing.mjs.map +1 -0
  24. package/dist/types--IozHd4V.mjs +283 -0
  25. package/dist/types--IozHd4V.mjs.map +1 -0
  26. package/dist/types-DG1H4DVR.d.mts +157 -0
  27. package/dist/types-DG1H4DVR.d.mts.map +1 -0
  28. package/package.json +33 -24
  29. package/src/CLAUDE.md +0 -527
  30. package/src/classic/layout.ts +0 -1843
  31. package/src/constants.ts +0 -82
  32. package/src/create-flexily.ts +0 -153
  33. package/src/index-classic.ts +0 -110
  34. package/src/index.ts +0 -133
  35. package/src/layout-flex-lines.ts +0 -413
  36. package/src/layout-helpers.ts +0 -160
  37. package/src/layout-measure.ts +0 -259
  38. package/src/layout-stats.ts +0 -41
  39. package/src/layout-traversal.ts +0 -70
  40. package/src/layout-zero.ts +0 -2219
  41. package/src/logger.ts +0 -68
  42. package/src/monospace-measurer.ts +0 -68
  43. package/src/node-zero.ts +0 -1508
  44. package/src/pretext-measurer.ts +0 -86
  45. package/src/test-measurer.ts +0 -219
  46. package/src/testing.ts +0 -215
  47. package/src/text-layout.ts +0 -75
  48. package/src/trace.ts +0 -252
  49. package/src/types.ts +0 -236
  50. package/src/utils.ts +0 -243
package/src/node-zero.ts DELETED
@@ -1,1508 +0,0 @@
1
- /**
2
- * Flexily Node
3
- *
4
- * Yoga-compatible Node class for flexbox layout.
5
- */
6
-
7
- import * as C from "./constants.js"
8
- import { computeLayout, countNodes, markSubtreeLayoutSeen } from "./layout-zero.js"
9
- import {
10
- type BaselineFunc,
11
- type FlexInfo,
12
- type Layout,
13
- type LayoutCacheEntry,
14
- type MeasureEntry,
15
- type MeasureFunc,
16
- type Style,
17
- type Value,
18
- createDefaultStyle,
19
- } from "./types.js"
20
- import { setEdgeValue, setEdgeBorder, getEdgeValue, getEdgeBorderValue, traversalStack } from "./utils.js"
21
- import { log } from "./logger.js"
22
- import { getTrace } from "./trace.js"
23
-
24
- /**
25
- * A layout node in the flexbox tree.
26
- */
27
- export class Node {
28
- // Tree structure
29
- private _parent: Node | null = null
30
- private _children: Node[] = []
31
-
32
- // Style
33
- private _style: Style = createDefaultStyle()
34
-
35
- // Measure function for intrinsic sizing
36
- private _measureFunc: MeasureFunc | null = null
37
-
38
- // Baseline function for baseline alignment
39
- private _baselineFunc: BaselineFunc | null = null
40
-
41
- // Measure cache - 4-entry numeric cache (faster than Map<string,...>)
42
- // Each entry stores: w, wm, h, hm, rw, rh
43
- // Cleared when markDirty() is called since content may have changed
44
- private _m0?: MeasureEntry
45
- private _m1?: MeasureEntry
46
- private _m2?: MeasureEntry
47
- private _m3?: MeasureEntry
48
-
49
- // Layout cache - 2-entry cache for sizing pass (availW, availH -> computedW, computedH)
50
- // Cleared at start of each calculateLayout pass via resetLayoutCache()
51
- // This avoids redundant recursive layout calls during intrinsic sizing
52
- private _lc0?: LayoutCacheEntry
53
- private _lc1?: LayoutCacheEntry
54
-
55
- // Stable result objects for zero-allocation cache returns
56
- // These are mutated in place instead of creating new objects on each cache hit
57
- private _measureResult: { width: number; height: number } = {
58
- width: 0,
59
- height: 0,
60
- }
61
- private _layoutResult: { width: number; height: number } = {
62
- width: 0,
63
- height: 0,
64
- }
65
-
66
- // Static counters for cache statistics (reset per layout pass)
67
- static measureCalls = 0
68
- static measureCacheHits = 0
69
-
70
- /**
71
- * Reset measure statistics (call before calculateLayout).
72
- */
73
- static resetMeasureStats(): void {
74
- Node.measureCalls = 0
75
- Node.measureCacheHits = 0
76
- }
77
-
78
- // Computed layout
79
- private _layout: Layout = { left: 0, top: 0, width: 0, height: 0 }
80
-
81
- // Per-node flex calculation state (reused across layout passes to avoid allocation)
82
- private _flex: FlexInfo = {
83
- mainSize: 0,
84
- baseSize: 0,
85
- mainMargin: 0,
86
- flexGrow: 0,
87
- flexShrink: 0,
88
- minMain: 0,
89
- maxMain: Infinity,
90
- mainStartMarginAuto: false,
91
- mainEndMarginAuto: false,
92
- mainStartMarginValue: 0,
93
- mainEndMarginValue: 0,
94
- marginL: 0,
95
- marginT: 0,
96
- marginR: 0,
97
- marginB: 0,
98
- frozen: false,
99
- lineIndex: 0,
100
- relativeIndex: -1,
101
- baseline: 0,
102
- // Constraint fingerprinting
103
- lastAvailW: NaN,
104
- lastAvailH: NaN,
105
- lastOffsetX: NaN,
106
- lastOffsetY: NaN,
107
- lastAbsX: NaN,
108
- lastAbsY: NaN,
109
- layoutValid: false,
110
- lastDir: 0,
111
- }
112
-
113
- // Dirty flags
114
- private _isDirty = true
115
- private _hasNewLayout = false
116
-
117
- // Last calculateLayout() inputs (for constraint-aware skip)
118
- private _lastCalcW: number = NaN
119
- private _lastCalcH: number = NaN
120
- private _lastCalcDir: number = 0
121
-
122
- // ============================================================================
123
- // Static Factory
124
- // ============================================================================
125
-
126
- /**
127
- * Create a new layout node.
128
- *
129
- * @returns A new Node instance
130
- * @example
131
- * ```typescript
132
- * const root = Node.create();
133
- * root.setWidth(100);
134
- * root.setHeight(200);
135
- * ```
136
- */
137
- static create(): Node {
138
- return new Node()
139
- }
140
-
141
- // ============================================================================
142
- // Tree Operations
143
- // ============================================================================
144
-
145
- /**
146
- * Get the number of child nodes.
147
- *
148
- * @returns The number of children
149
- */
150
- getChildCount(): number {
151
- return this._children.length
152
- }
153
-
154
- /**
155
- * Get a child node by index.
156
- *
157
- * @param index - Zero-based child index
158
- * @returns The child node at the given index, or undefined if index is out of bounds
159
- */
160
- getChild(index: number): Node | undefined {
161
- return this._children[index]
162
- }
163
-
164
- /**
165
- * Get the parent node.
166
- *
167
- * @returns The parent node, or null if this is a root node
168
- */
169
- getParent(): Node | null {
170
- return this._parent
171
- }
172
-
173
- /**
174
- * Insert a child node at the specified index.
175
- * If the child already has a parent, it will be removed from that parent first.
176
- * Marks the node as dirty to trigger layout recalculation.
177
- *
178
- * @param child - The child node to insert
179
- * @param index - The index at which to insert the child
180
- * @example
181
- * ```typescript
182
- * const parent = Node.create();
183
- * const child1 = Node.create();
184
- * const child2 = Node.create();
185
- * parent.insertChild(child1, 0);
186
- * parent.insertChild(child2, 1);
187
- * ```
188
- */
189
- insertChild(child: Node, index: number): void {
190
- // Cycle guard: prevent self-insertion or insertion of an ancestor (would create infinite loop)
191
- if (child === this) {
192
- throw new Error("Cannot insert a node as a child of itself")
193
- }
194
- let ancestor: Node | null = this._parent
195
- while (ancestor !== null) {
196
- if (ancestor === child) {
197
- throw new Error("Cannot insert an ancestor as a child (would create a cycle)")
198
- }
199
- ancestor = ancestor._parent
200
- }
201
-
202
- if (child._parent !== null) {
203
- child._parent.removeChild(child)
204
- }
205
- child._parent = this
206
- // Clamp index to valid range to ensure deterministic behavior
207
- const clampedIndex = Math.max(0, Math.min(index, this._children.length))
208
- this._children.splice(clampedIndex, 0, child)
209
- // Invalidate layoutValid for siblings after the insertion point
210
- // Their positions may change due to the insertion
211
- for (let i = clampedIndex + 1; i < this._children.length; i++) {
212
- this._children[i]!._flex.layoutValid = false
213
- }
214
- this.markDirty()
215
- }
216
-
217
- /**
218
- * Remove a child node from this node.
219
- * The child's parent reference will be cleared.
220
- * Marks the node as dirty to trigger layout recalculation.
221
- * Invalidates layout validity of remaining siblings whose positions may change.
222
- *
223
- * @param child - The child node to remove
224
- */
225
- removeChild(child: Node): void {
226
- const index = this._children.indexOf(child)
227
- if (index !== -1) {
228
- this._children.splice(index, 1)
229
- child._parent = null
230
- // Invalidate layoutValid for remaining siblings after the removal point
231
- // Their positions may change due to the removal
232
- for (let i = index; i < this._children.length; i++) {
233
- this._children[i]!._flex.layoutValid = false
234
- }
235
- this.markDirty()
236
- }
237
- }
238
-
239
- /**
240
- * Free this node and clean up all references.
241
- * Removes the node from its parent, clears all children, and removes the measure function.
242
- * This does not recursively free child nodes.
243
- */
244
- free(): void {
245
- // Remove from parent
246
- if (this._parent !== null) {
247
- this._parent.removeChild(this)
248
- }
249
- // Clear children
250
- for (const child of this._children) {
251
- child._parent = null
252
- }
253
- this._children = []
254
- this._measureFunc = null
255
- this._baselineFunc = null
256
- }
257
-
258
- /**
259
- * Free this node and all descendants recursively.
260
- * Each node is detached from its parent and cleaned up.
261
- * Uses iterative traversal to avoid stack overflow on deep trees.
262
- */
263
- freeRecursive(): void {
264
- // Collect all descendants first (iterative to avoid stack overflow)
265
- const nodes: Node[] = []
266
- traversalStack.length = 0
267
- traversalStack.push(this)
268
- while (traversalStack.length > 0) {
269
- const current = traversalStack.pop() as Node
270
- nodes.push(current)
271
- for (const child of current._children) {
272
- traversalStack.push(child)
273
- }
274
- }
275
- // Free in reverse order (leaves first) to avoid parent.removeChild on already-freed nodes
276
- for (let i = nodes.length - 1; i >= 0; i--) {
277
- nodes[i]!.free()
278
- }
279
- }
280
-
281
- /**
282
- * Dispose the node (calls free)
283
- */
284
- [Symbol.dispose](): void {
285
- this.free()
286
- }
287
-
288
- // ============================================================================
289
- // Measure Function
290
- // ============================================================================
291
-
292
- /**
293
- * Set a measure function for intrinsic sizing.
294
- * The measure function is called during layout to determine the node's natural size.
295
- * Typically used for text nodes or other content that has an intrinsic size.
296
- * Marks the node as dirty to trigger layout recalculation.
297
- *
298
- * @param measureFunc - Function that returns width and height given available space and constraints
299
- * @example
300
- * ```typescript
301
- * const textNode = Node.create();
302
- * textNode.setMeasureFunc((width, widthMode, height, heightMode) => {
303
- * // Measure text and return dimensions
304
- * return { width: 50, height: 20 };
305
- * });
306
- * ```
307
- */
308
- setMeasureFunc(measureFunc: MeasureFunc): void {
309
- this._measureFunc = measureFunc
310
- this.markDirty()
311
- }
312
-
313
- /**
314
- * Remove the measure function from this node.
315
- * Marks the node as dirty to trigger layout recalculation.
316
- */
317
- unsetMeasureFunc(): void {
318
- this._measureFunc = null
319
- this.markDirty()
320
- }
321
-
322
- /**
323
- * Check if this node has a measure function.
324
- *
325
- * @returns True if a measure function is set
326
- */
327
- hasMeasureFunc(): boolean {
328
- return this._measureFunc !== null
329
- }
330
-
331
- // ============================================================================
332
- // Baseline Function
333
- // ============================================================================
334
-
335
- /**
336
- * Set a baseline function to determine where this node's text baseline is.
337
- * Used for ALIGN_BASELINE to align text across siblings with different heights.
338
- *
339
- * @param baselineFunc - Function that returns baseline offset from top given width and height
340
- * @example
341
- * ```typescript
342
- * textNode.setBaselineFunc((width, height) => {
343
- * // For a text node, baseline might be at 80% of height
344
- * return height * 0.8;
345
- * });
346
- * ```
347
- */
348
- setBaselineFunc(baselineFunc: BaselineFunc): void {
349
- this._baselineFunc = baselineFunc
350
- this.markDirty()
351
- }
352
-
353
- /**
354
- * Remove the baseline function from this node.
355
- * Marks the node as dirty to trigger layout recalculation.
356
- */
357
- unsetBaselineFunc(): void {
358
- this._baselineFunc = null
359
- this.markDirty()
360
- }
361
-
362
- /**
363
- * Check if this node has a baseline function.
364
- *
365
- * @returns True if a baseline function is set
366
- */
367
- hasBaselineFunc(): boolean {
368
- return this._baselineFunc !== null
369
- }
370
-
371
- /**
372
- * Call the measure function with caching.
373
- * Uses a 4-entry numeric cache for fast lookup without allocations.
374
- * Cache is cleared when markDirty() is called.
375
- *
376
- * @returns Measured dimensions or null if no measure function
377
- */
378
- cachedMeasure(w: number, wm: number, h: number, hm: number): { width: number; height: number } | null {
379
- if (!this._measureFunc) return null
380
-
381
- Node.measureCalls++
382
-
383
- // Check 4-entry cache (most recent first)
384
- // Returns stable _measureResult object to avoid allocation on cache hit
385
- const m0 = this._m0
386
- if (m0 && m0.w === w && m0.wm === wm && m0.h === h && m0.hm === hm) {
387
- Node.measureCacheHits++
388
- this._measureResult.width = m0.rw
389
- this._measureResult.height = m0.rh
390
- getTrace()?.measureCacheHit(0, w, h, m0.rw, m0.rh)
391
- return this._measureResult
392
- }
393
- const m1 = this._m1
394
- if (m1 && m1.w === w && m1.wm === wm && m1.h === h && m1.hm === hm) {
395
- Node.measureCacheHits++
396
- this._measureResult.width = m1.rw
397
- this._measureResult.height = m1.rh
398
- getTrace()?.measureCacheHit(0, w, h, m1.rw, m1.rh)
399
- return this._measureResult
400
- }
401
- const m2 = this._m2
402
- if (m2 && m2.w === w && m2.wm === wm && m2.h === h && m2.hm === hm) {
403
- Node.measureCacheHits++
404
- this._measureResult.width = m2.rw
405
- this._measureResult.height = m2.rh
406
- getTrace()?.measureCacheHit(0, w, h, m2.rw, m2.rh)
407
- return this._measureResult
408
- }
409
- const m3 = this._m3
410
- if (m3 && m3.w === w && m3.wm === wm && m3.h === h && m3.hm === hm) {
411
- Node.measureCacheHits++
412
- this._measureResult.width = m3.rw
413
- this._measureResult.height = m3.rh
414
- getTrace()?.measureCacheHit(0, w, h, m3.rw, m3.rh)
415
- return this._measureResult
416
- }
417
-
418
- // Cache miss
419
- getTrace()?.measureCacheMiss(0, w, h)
420
-
421
- // Call actual measure function
422
- const result = this._measureFunc(w, wm, h, hm)
423
-
424
- // Zero-allocation: rotate entries by copying values, lazily allocate on first use
425
- // Rotate: m3 <- m2 <- m1 <- m0 <- new values
426
- if (this._m2) {
427
- if (!this._m3) this._m3 = { w: 0, wm: 0, h: 0, hm: 0, rw: 0, rh: 0 }
428
- this._m3.w = this._m2.w
429
- this._m3.wm = this._m2.wm
430
- this._m3.h = this._m2.h
431
- this._m3.hm = this._m2.hm
432
- this._m3.rw = this._m2.rw
433
- this._m3.rh = this._m2.rh
434
- }
435
- if (this._m1) {
436
- if (!this._m2) this._m2 = { w: 0, wm: 0, h: 0, hm: 0, rw: 0, rh: 0 }
437
- this._m2.w = this._m1.w
438
- this._m2.wm = this._m1.wm
439
- this._m2.h = this._m1.h
440
- this._m2.hm = this._m1.hm
441
- this._m2.rw = this._m1.rw
442
- this._m2.rh = this._m1.rh
443
- }
444
- if (this._m0) {
445
- if (!this._m1) this._m1 = { w: 0, wm: 0, h: 0, hm: 0, rw: 0, rh: 0 }
446
- this._m1.w = this._m0.w
447
- this._m1.wm = this._m0.wm
448
- this._m1.h = this._m0.h
449
- this._m1.hm = this._m0.hm
450
- this._m1.rw = this._m0.rw
451
- this._m1.rh = this._m0.rh
452
- }
453
- if (!this._m0) this._m0 = { w: 0, wm: 0, h: 0, hm: 0, rw: 0, rh: 0 }
454
- this._m0.w = w
455
- this._m0.wm = wm
456
- this._m0.h = h
457
- this._m0.hm = hm
458
- this._m0.rw = result.width
459
- this._m0.rh = result.height
460
-
461
- // Return stable result object (same as cache hits)
462
- this._measureResult.width = result.width
463
- this._measureResult.height = result.height
464
- return this._measureResult
465
- }
466
-
467
- // ============================================================================
468
- // Layout Caching (for intrinsic sizing pass)
469
- // ============================================================================
470
-
471
- /**
472
- * Check layout cache for a previously computed size with same available dimensions.
473
- * Returns cached (width, height) or null if not found.
474
- *
475
- * NaN dimensions are handled specially via Object.is (NaN === NaN is false, but Object.is(NaN, NaN) is true).
476
- */
477
- getCachedLayout(availW: number, availH: number): { width: number; height: number } | null {
478
- // Never return cached layout for dirty nodes - content may have changed
479
- if (this._isDirty) {
480
- return null
481
- }
482
- // Returns stable _layoutResult object to avoid allocation on cache hit
483
- const lc0 = this._lc0
484
- if (lc0 && Object.is(lc0.availW, availW) && Object.is(lc0.availH, availH)) {
485
- this._layoutResult.width = lc0.computedW
486
- this._layoutResult.height = lc0.computedH
487
- return this._layoutResult
488
- }
489
- const lc1 = this._lc1
490
- if (lc1 && Object.is(lc1.availW, availW) && Object.is(lc1.availH, availH)) {
491
- this._layoutResult.width = lc1.computedW
492
- this._layoutResult.height = lc1.computedH
493
- return this._layoutResult
494
- }
495
- return null
496
- }
497
-
498
- /**
499
- * Cache a computed layout result for the given available dimensions.
500
- * Zero-allocation: lazily allocates cache entries once, then reuses.
501
- */
502
- setCachedLayout(availW: number, availH: number, computedW: number, computedH: number): void {
503
- // Rotate entries: copy _lc0 values to _lc1, then update _lc0
504
- if (this._lc0) {
505
- // Lazily allocate _lc1 on first rotation
506
- if (!this._lc1) {
507
- this._lc1 = { availW: NaN, availH: NaN, computedW: 0, computedH: 0 }
508
- }
509
- this._lc1.availW = this._lc0.availW
510
- this._lc1.availH = this._lc0.availH
511
- this._lc1.computedW = this._lc0.computedW
512
- this._lc1.computedH = this._lc0.computedH
513
- }
514
- // Lazily allocate _lc0 on first use
515
- if (!this._lc0) {
516
- this._lc0 = { availW: 0, availH: 0, computedW: 0, computedH: 0 }
517
- }
518
- this._lc0.availW = availW
519
- this._lc0.availH = availH
520
- this._lc0.computedW = computedW
521
- this._lc0.computedH = computedH
522
- }
523
-
524
- /**
525
- * Clear layout cache for this node and all descendants.
526
- * Called at the start of each calculateLayout pass.
527
- * Zero-allocation: invalidates entries (availW = NaN) rather than deallocating.
528
- * Uses iterative traversal to avoid stack overflow on deep trees.
529
- */
530
- resetLayoutCache(): void {
531
- traversalStack.length = 0
532
- traversalStack.push(this)
533
- while (traversalStack.length > 0) {
534
- const node = traversalStack.pop() as Node
535
- // Invalidate using -1 sentinel (not NaN — NaN is a legitimate "unconstrained" query
536
- // value and Object.is(NaN, NaN) === true would cause false cache hits)
537
- if (node._lc0) node._lc0.availW = -1
538
- if (node._lc1) node._lc1.availW = -1
539
- for (const child of node._children) {
540
- traversalStack.push(child)
541
- }
542
- }
543
- }
544
-
545
- // ============================================================================
546
- // Dirty Tracking
547
- // ============================================================================
548
-
549
- /**
550
- * Check if this node needs layout recalculation.
551
- *
552
- * @returns True if the node is dirty and needs layout
553
- */
554
- isDirty(): boolean {
555
- return this._isDirty
556
- }
557
-
558
- /**
559
- * Mark this node and all ancestors as dirty.
560
- * A dirty node needs layout recalculation.
561
- * This is automatically called by all style setters and tree operations.
562
- * Uses iterative approach to avoid stack overflow on deep trees.
563
- */
564
- markDirty(): void {
565
- let current: Node | null = this
566
- while (current !== null) {
567
- // Always clear caches - even if already dirty, a child's content change
568
- // may invalidate cached layout results that used the old child size
569
- current._m0 = current._m1 = current._m2 = current._m3 = undefined
570
- current._lc0 = current._lc1 = undefined
571
- // Skip setting dirty flag if already dirty (but still cleared caches above)
572
- if (current._isDirty) break
573
- current._isDirty = true
574
- // Invalidate layout fingerprint
575
- current._flex.layoutValid = false
576
- current = current._parent
577
- }
578
- }
579
-
580
- /**
581
- * Check if this node has new layout results since the last check.
582
- *
583
- * @returns True if layout was recalculated since the last call to markLayoutSeen
584
- */
585
- hasNewLayout(): boolean {
586
- return this._hasNewLayout
587
- }
588
-
589
- /**
590
- * Mark that the current layout has been seen/processed.
591
- * Clears the hasNewLayout flag.
592
- */
593
- markLayoutSeen(): void {
594
- this._hasNewLayout = false
595
- }
596
-
597
- // ============================================================================
598
- // Layout Calculation
599
- // ============================================================================
600
-
601
- /**
602
- * Calculate layout for this node and all descendants.
603
- * This runs the flexbox layout algorithm to compute positions and sizes.
604
- * Only recalculates if the node is marked as dirty.
605
- *
606
- * @param width - Available width for layout
607
- * @param height - Available height for layout
608
- * @param _direction - Text direction (LTR or RTL), defaults to LTR
609
- * @example
610
- * ```typescript
611
- * const root = Node.create();
612
- * root.setFlexDirection(FLEX_DIRECTION_ROW);
613
- * root.setWidth(100);
614
- * root.setHeight(50);
615
- *
616
- * const child = Node.create();
617
- * child.setFlexGrow(1);
618
- * root.insertChild(child, 0);
619
- *
620
- * root.calculateLayout(100, 50, DIRECTION_LTR);
621
- *
622
- * // Now you can read computed layout
623
- * console.log(child.getComputedWidth());
624
- * ```
625
- */
626
- calculateLayout(width?: number, height?: number, direction: number = C.DIRECTION_LTR): void {
627
- // Treat undefined as unconstrained (NaN signals content-based sizing)
628
- const availableWidth = width ?? NaN
629
- const availableHeight = height ?? NaN
630
-
631
- // Skip if not dirty AND constraints unchanged (use Object.is for NaN equality)
632
- if (
633
- !this._isDirty &&
634
- Object.is(this._lastCalcW, availableWidth) &&
635
- Object.is(this._lastCalcH, availableHeight) &&
636
- this._lastCalcDir === direction
637
- ) {
638
- log.debug?.("layout skip (not dirty, constraints unchanged)")
639
- return
640
- }
641
-
642
- // Track constraints for future skip check
643
- this._lastCalcW = availableWidth
644
- this._lastCalcH = availableHeight
645
- this._lastCalcDir = direction
646
-
647
- // Only compute debug stats when debug logging is enabled (avoid O(n) traversal in production)
648
- const start = log.debug ? Date.now() : 0
649
- const nodeCount = log.debug ? countNodes(this) : 0
650
-
651
- // Reset measure statistics for this layout pass
652
- Node.resetMeasureStats()
653
-
654
- // Run the layout algorithm
655
- computeLayout(this, availableWidth, availableHeight, direction)
656
-
657
- // Mark layout computed
658
- this._isDirty = false
659
- this._hasNewLayout = true
660
- markSubtreeLayoutSeen(this)
661
-
662
- log.debug?.(
663
- "layout: %dx%d, %d nodes in %dms (measure: calls=%d hits=%d)",
664
- width,
665
- height,
666
- nodeCount,
667
- Date.now() - start,
668
- Node.measureCalls,
669
- Node.measureCacheHits,
670
- )
671
- }
672
-
673
- // ============================================================================
674
- // Layout Results
675
- // ============================================================================
676
-
677
- /**
678
- * Get the computed left position after layout.
679
- *
680
- * @returns The left position in points
681
- */
682
- getComputedLeft(): number {
683
- return this._layout.left
684
- }
685
-
686
- /**
687
- * Get the computed top position after layout.
688
- *
689
- * @returns The top position in points
690
- */
691
- getComputedTop(): number {
692
- return this._layout.top
693
- }
694
-
695
- /**
696
- * Get the computed width after layout.
697
- *
698
- * @returns The width in points
699
- */
700
- getComputedWidth(): number {
701
- return this._layout.width
702
- }
703
-
704
- /**
705
- * Get the computed height after layout.
706
- *
707
- * @returns The height in points
708
- */
709
- getComputedHeight(): number {
710
- return this._layout.height
711
- }
712
-
713
- /**
714
- * Get the computed right edge position after layout (left + width).
715
- *
716
- * @returns The right edge position in points
717
- */
718
- getComputedRight(): number {
719
- return this._layout.left + this._layout.width
720
- }
721
-
722
- /**
723
- * Get the computed bottom edge position after layout (top + height).
724
- *
725
- * @returns The bottom edge position in points
726
- */
727
- getComputedBottom(): number {
728
- return this._layout.top + this._layout.height
729
- }
730
-
731
- /**
732
- * Get the computed padding for a specific edge after layout.
733
- * Returns the resolved padding value (percentage and logical edges resolved).
734
- *
735
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
736
- * @returns Padding value in points
737
- */
738
- getComputedPadding(edge: number): number {
739
- return getEdgeValue(this._style.padding, edge).value
740
- }
741
-
742
- /**
743
- * Get the computed margin for a specific edge after layout.
744
- * Returns the resolved margin value (percentage and logical edges resolved).
745
- *
746
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
747
- * @returns Margin value in points
748
- */
749
- getComputedMargin(edge: number): number {
750
- return getEdgeValue(this._style.margin, edge).value
751
- }
752
-
753
- /**
754
- * Get the computed border width for a specific edge after layout.
755
- *
756
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
757
- * @returns Border width in points
758
- */
759
- getComputedBorder(edge: number): number {
760
- return getEdgeBorderValue(this._style.border, edge)
761
- }
762
-
763
- // ============================================================================
764
- // Internal Accessors (for layout algorithm)
765
- // ============================================================================
766
-
767
- get children(): readonly Node[] {
768
- return this._children
769
- }
770
-
771
- get style(): Style {
772
- return this._style
773
- }
774
-
775
- get layout(): Layout {
776
- return this._layout
777
- }
778
-
779
- get measureFunc(): MeasureFunc | null {
780
- return this._measureFunc
781
- }
782
-
783
- get baselineFunc(): BaselineFunc | null {
784
- return this._baselineFunc
785
- }
786
-
787
- get flex(): FlexInfo {
788
- return this._flex
789
- }
790
-
791
- // ============================================================================
792
- // Width Setters
793
- // ============================================================================
794
-
795
- /**
796
- * Set the width to a fixed value in points.
797
- *
798
- * @param value - Width in points
799
- */
800
- setWidth(value: number): void {
801
- // NaN means "auto" in Yoga API
802
- if (Number.isNaN(value)) {
803
- this._style.width = { value: 0, unit: C.UNIT_AUTO }
804
- } else {
805
- this._style.width = { value, unit: C.UNIT_POINT }
806
- }
807
- this.markDirty()
808
- }
809
-
810
- /**
811
- * Set the width as a percentage of the parent's width.
812
- *
813
- * @param value - Width as a percentage (0-100)
814
- */
815
- setWidthPercent(value: number): void {
816
- this._style.width = { value, unit: C.UNIT_PERCENT }
817
- this.markDirty()
818
- }
819
-
820
- /**
821
- * Set the width to auto (determined by layout algorithm).
822
- */
823
- setWidthAuto(): void {
824
- this._style.width = { value: 0, unit: C.UNIT_AUTO }
825
- this.markDirty()
826
- }
827
-
828
- // ============================================================================
829
- // Height Setters
830
- // ============================================================================
831
-
832
- /**
833
- * Set the height to a fixed value in points.
834
- *
835
- * @param value - Height in points
836
- */
837
- setHeight(value: number): void {
838
- // NaN means "auto" in Yoga API
839
- if (Number.isNaN(value)) {
840
- this._style.height = { value: 0, unit: C.UNIT_AUTO }
841
- } else {
842
- this._style.height = { value, unit: C.UNIT_POINT }
843
- }
844
- this.markDirty()
845
- }
846
-
847
- /**
848
- * Set the height as a percentage of the parent's height.
849
- *
850
- * @param value - Height as a percentage (0-100)
851
- */
852
- setHeightPercent(value: number): void {
853
- this._style.height = { value, unit: C.UNIT_PERCENT }
854
- this.markDirty()
855
- }
856
-
857
- /**
858
- * Set the height to auto (determined by layout algorithm).
859
- */
860
- setHeightAuto(): void {
861
- this._style.height = { value: 0, unit: C.UNIT_AUTO }
862
- this.markDirty()
863
- }
864
-
865
- // ============================================================================
866
- // Min/Max Size Setters
867
- // ============================================================================
868
-
869
- /**
870
- * Set the minimum width in points.
871
- *
872
- * @param value - Minimum width in points
873
- */
874
- setMinWidth(value: number): void {
875
- this._style.minWidth = { value, unit: C.UNIT_POINT }
876
- this.markDirty()
877
- }
878
-
879
- /**
880
- * Set the minimum width as a percentage of the parent's width.
881
- *
882
- * @param value - Minimum width as a percentage (0-100)
883
- */
884
- setMinWidthPercent(value: number): void {
885
- this._style.minWidth = { value, unit: C.UNIT_PERCENT }
886
- this.markDirty()
887
- }
888
-
889
- /**
890
- * Set the minimum height in points.
891
- *
892
- * @param value - Minimum height in points
893
- */
894
- setMinHeight(value: number): void {
895
- this._style.minHeight = { value, unit: C.UNIT_POINT }
896
- this.markDirty()
897
- }
898
-
899
- /**
900
- * Set the minimum height as a percentage of the parent's height.
901
- *
902
- * @param value - Minimum height as a percentage (0-100)
903
- */
904
- setMinHeightPercent(value: number): void {
905
- this._style.minHeight = { value, unit: C.UNIT_PERCENT }
906
- this.markDirty()
907
- }
908
-
909
- /**
910
- * Set the maximum width in points.
911
- *
912
- * @param value - Maximum width in points
913
- */
914
- setMaxWidth(value: number): void {
915
- this._style.maxWidth = { value, unit: C.UNIT_POINT }
916
- this.markDirty()
917
- }
918
-
919
- /**
920
- * Set the maximum width as a percentage of the parent's width.
921
- *
922
- * @param value - Maximum width as a percentage (0-100)
923
- */
924
- setMaxWidthPercent(value: number): void {
925
- this._style.maxWidth = { value, unit: C.UNIT_PERCENT }
926
- this.markDirty()
927
- }
928
-
929
- /**
930
- * Set the maximum height in points.
931
- *
932
- * @param value - Maximum height in points
933
- */
934
- setMaxHeight(value: number): void {
935
- this._style.maxHeight = { value, unit: C.UNIT_POINT }
936
- this.markDirty()
937
- }
938
-
939
- /**
940
- * Set the maximum height as a percentage of the parent's height.
941
- *
942
- * @param value - Maximum height as a percentage (0-100)
943
- */
944
- setMaxHeightPercent(value: number): void {
945
- this._style.maxHeight = { value, unit: C.UNIT_PERCENT }
946
- this.markDirty()
947
- }
948
-
949
- /**
950
- * Set the aspect ratio of the node.
951
- * When set, the node's width/height relationship is constrained.
952
- * If width is defined, height = width / aspectRatio.
953
- * If height is defined, width = height * aspectRatio.
954
- *
955
- * @param value - Aspect ratio (width/height). Use NaN to unset.
956
- */
957
- setAspectRatio(value: number): void {
958
- this._style.aspectRatio = value
959
- this.markDirty()
960
- }
961
-
962
- // ============================================================================
963
- // Flex Setters
964
- // ============================================================================
965
-
966
- /**
967
- * Set the flex grow factor.
968
- * Determines how much the node will grow relative to siblings when there is extra space.
969
- *
970
- * @param value - Flex grow factor (typically 0 or 1+)
971
- * @example
972
- * ```typescript
973
- * const child = Node.create();
974
- * child.setFlexGrow(1); // Will grow to fill available space
975
- * ```
976
- */
977
- setFlexGrow(value: number): void {
978
- this._style.flexGrow = value
979
- this.markDirty()
980
- }
981
-
982
- /**
983
- * Set the flex shrink factor.
984
- * Determines how much the node will shrink relative to siblings when there is insufficient space.
985
- *
986
- * @param value - Flex shrink factor (default is 1)
987
- */
988
- setFlexShrink(value: number): void {
989
- this._style.flexShrink = value
990
- this.markDirty()
991
- }
992
-
993
- /**
994
- * Set the flex basis to a fixed value in points.
995
- * The initial size of the node before flex grow/shrink is applied.
996
- *
997
- * @param value - Flex basis in points
998
- */
999
- setFlexBasis(value: number): void {
1000
- this._style.flexBasis = { value, unit: C.UNIT_POINT }
1001
- this.markDirty()
1002
- }
1003
-
1004
- /**
1005
- * Set the flex basis as a percentage of the parent's size.
1006
- *
1007
- * @param value - Flex basis as a percentage (0-100)
1008
- */
1009
- setFlexBasisPercent(value: number): void {
1010
- this._style.flexBasis = { value, unit: C.UNIT_PERCENT }
1011
- this.markDirty()
1012
- }
1013
-
1014
- /**
1015
- * Set the flex basis to auto (based on the node's width/height).
1016
- */
1017
- setFlexBasisAuto(): void {
1018
- this._style.flexBasis = { value: 0, unit: C.UNIT_AUTO }
1019
- this.markDirty()
1020
- }
1021
-
1022
- /**
1023
- * Set the flex direction (main axis direction).
1024
- *
1025
- * @param direction - FLEX_DIRECTION_ROW, FLEX_DIRECTION_COLUMN, FLEX_DIRECTION_ROW_REVERSE, or FLEX_DIRECTION_COLUMN_REVERSE
1026
- * @example
1027
- * ```typescript
1028
- * const container = Node.create();
1029
- * container.setFlexDirection(FLEX_DIRECTION_ROW); // Lay out children horizontally
1030
- * ```
1031
- */
1032
- setFlexDirection(direction: number): void {
1033
- this._style.flexDirection = direction
1034
- this.markDirty()
1035
- }
1036
-
1037
- /**
1038
- * Set the flex wrap behavior.
1039
- *
1040
- * @param wrap - WRAP_NO_WRAP, WRAP_WRAP, or WRAP_WRAP_REVERSE
1041
- */
1042
- setFlexWrap(wrap: number): void {
1043
- this._style.flexWrap = wrap
1044
- this.markDirty()
1045
- }
1046
-
1047
- // ============================================================================
1048
- // Alignment Setters
1049
- // ============================================================================
1050
-
1051
- /**
1052
- * Set how children are aligned along the cross axis.
1053
- *
1054
- * @param align - ALIGN_FLEX_START, ALIGN_CENTER, ALIGN_FLEX_END, ALIGN_STRETCH, or ALIGN_BASELINE
1055
- * @example
1056
- * ```typescript
1057
- * const container = Node.create();
1058
- * container.setFlexDirection(FLEX_DIRECTION_ROW);
1059
- * container.setAlignItems(ALIGN_CENTER); // Center children vertically
1060
- * ```
1061
- */
1062
- setAlignItems(align: number): void {
1063
- this._style.alignItems = align
1064
- this.markDirty()
1065
- }
1066
-
1067
- /**
1068
- * Set how this node is aligned along the parent's cross axis.
1069
- * Overrides the parent's alignItems for this specific child.
1070
- *
1071
- * @param align - ALIGN_AUTO, ALIGN_FLEX_START, ALIGN_CENTER, ALIGN_FLEX_END, ALIGN_STRETCH, or ALIGN_BASELINE
1072
- */
1073
- setAlignSelf(align: number): void {
1074
- this._style.alignSelf = align
1075
- this.markDirty()
1076
- }
1077
-
1078
- /**
1079
- * Set how lines are aligned in a multi-line flex container.
1080
- * Only affects containers with wrap enabled and multiple lines.
1081
- *
1082
- * @param align - ALIGN_FLEX_START, ALIGN_CENTER, ALIGN_FLEX_END, ALIGN_STRETCH, ALIGN_SPACE_BETWEEN, or ALIGN_SPACE_AROUND
1083
- */
1084
- setAlignContent(align: number): void {
1085
- this._style.alignContent = align
1086
- this.markDirty()
1087
- }
1088
-
1089
- /**
1090
- * Set how children are distributed along the main axis.
1091
- *
1092
- * @param justify - JUSTIFY_FLEX_START, JUSTIFY_CENTER, JUSTIFY_FLEX_END, JUSTIFY_SPACE_BETWEEN, JUSTIFY_SPACE_AROUND, or JUSTIFY_SPACE_EVENLY
1093
- * @example
1094
- * ```typescript
1095
- * const container = Node.create();
1096
- * container.setJustifyContent(JUSTIFY_SPACE_BETWEEN); // Space children evenly with edges at start/end
1097
- * ```
1098
- */
1099
- setJustifyContent(justify: number): void {
1100
- this._style.justifyContent = justify
1101
- this.markDirty()
1102
- }
1103
-
1104
- // ============================================================================
1105
- // Spacing Setters
1106
- // ============================================================================
1107
-
1108
- /**
1109
- * Set padding for one or more edges.
1110
- *
1111
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
1112
- * @param value - Padding in points
1113
- * @example
1114
- * ```typescript
1115
- * node.setPadding(EDGE_ALL, 10); // Set 10pt padding on all edges
1116
- * node.setPadding(EDGE_HORIZONTAL, 5); // Set 5pt padding on left and right
1117
- * ```
1118
- */
1119
- setPadding(edge: number, value: number): void {
1120
- setEdgeValue(this._style.padding, edge, value, C.UNIT_POINT)
1121
- this.markDirty()
1122
- }
1123
-
1124
- /**
1125
- * Set padding as a percentage of the parent's width.
1126
- * Per CSS spec, percentage padding always resolves against the containing block's width.
1127
- *
1128
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
1129
- * @param value - Padding as a percentage (0-100)
1130
- */
1131
- setPaddingPercent(edge: number, value: number): void {
1132
- setEdgeValue(this._style.padding, edge, value, C.UNIT_PERCENT)
1133
- this.markDirty()
1134
- }
1135
-
1136
- /**
1137
- * Set margin for one or more edges.
1138
- *
1139
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
1140
- * @param value - Margin in points
1141
- * @example
1142
- * ```typescript
1143
- * node.setMargin(EDGE_ALL, 5); // Set 5pt margin on all edges
1144
- * node.setMargin(EDGE_TOP, 10); // Set 10pt margin on top only
1145
- * ```
1146
- */
1147
- setMargin(edge: number, value: number): void {
1148
- setEdgeValue(this._style.margin, edge, value, C.UNIT_POINT)
1149
- this.markDirty()
1150
- }
1151
-
1152
- /**
1153
- * Set margin as a percentage of the parent's size.
1154
- *
1155
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
1156
- * @param value - Margin as a percentage (0-100)
1157
- */
1158
- setMarginPercent(edge: number, value: number): void {
1159
- setEdgeValue(this._style.margin, edge, value, C.UNIT_PERCENT)
1160
- this.markDirty()
1161
- }
1162
-
1163
- /**
1164
- * Set margin to auto (for centering items with margin: auto).
1165
- *
1166
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
1167
- */
1168
- setMarginAuto(edge: number): void {
1169
- setEdgeValue(this._style.margin, edge, 0, C.UNIT_AUTO)
1170
- this.markDirty()
1171
- }
1172
-
1173
- /**
1174
- * Set border width for one or more edges.
1175
- *
1176
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
1177
- * @param value - Border width in points
1178
- */
1179
- setBorder(edge: number, value: number): void {
1180
- setEdgeBorder(this._style.border, edge, value)
1181
- this.markDirty()
1182
- }
1183
-
1184
- /**
1185
- * Set gap between flex items.
1186
- *
1187
- * @param gutter - GUTTER_COLUMN (horizontal gap), GUTTER_ROW (vertical gap), or GUTTER_ALL (both)
1188
- * @param value - Gap size in points
1189
- * @example
1190
- * ```typescript
1191
- * container.setGap(GUTTER_ALL, 8); // Set 8pt gap between all items
1192
- * container.setGap(GUTTER_COLUMN, 10); // Set 10pt horizontal gap only
1193
- * ```
1194
- */
1195
- setGap(gutter: number, value: number): void {
1196
- if (gutter === C.GUTTER_COLUMN) {
1197
- this._style.gap[0] = value
1198
- } else if (gutter === C.GUTTER_ROW) {
1199
- this._style.gap[1] = value
1200
- } else if (gutter === C.GUTTER_ALL) {
1201
- this._style.gap[0] = value
1202
- this._style.gap[1] = value
1203
- }
1204
- this.markDirty()
1205
- }
1206
-
1207
- // ============================================================================
1208
- // Position Setters
1209
- // ============================================================================
1210
-
1211
- /**
1212
- * Set the position type.
1213
- *
1214
- * @param positionType - POSITION_TYPE_STATIC, POSITION_TYPE_RELATIVE, or POSITION_TYPE_ABSOLUTE
1215
- * @example
1216
- * ```typescript
1217
- * node.setPositionType(POSITION_TYPE_ABSOLUTE);
1218
- * node.setPosition(EDGE_LEFT, 10);
1219
- * node.setPosition(EDGE_TOP, 20);
1220
- * ```
1221
- */
1222
- setPositionType(positionType: number): void {
1223
- this._style.positionType = positionType
1224
- this.markDirty()
1225
- }
1226
-
1227
- /**
1228
- * Set position offset for one or more edges.
1229
- * Only applies when position type is ABSOLUTE or RELATIVE.
1230
- *
1231
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
1232
- * @param value - Position offset in points
1233
- */
1234
- setPosition(edge: number, value: number): void {
1235
- // NaN means "auto" (unset) in Yoga API
1236
- if (Number.isNaN(value)) {
1237
- setEdgeValue(this._style.position, edge, 0, C.UNIT_UNDEFINED)
1238
- } else {
1239
- setEdgeValue(this._style.position, edge, value, C.UNIT_POINT)
1240
- }
1241
- this.markDirty()
1242
- }
1243
-
1244
- /**
1245
- * Set position offset as a percentage.
1246
- *
1247
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
1248
- * @param value - Position offset as a percentage of parent's corresponding dimension
1249
- */
1250
- setPositionPercent(edge: number, value: number): void {
1251
- setEdgeValue(this._style.position, edge, value, C.UNIT_PERCENT)
1252
- this.markDirty()
1253
- }
1254
-
1255
- // ============================================================================
1256
- // Other Setters
1257
- // ============================================================================
1258
-
1259
- /**
1260
- * Set the display type.
1261
- *
1262
- * @param display - DISPLAY_FLEX or DISPLAY_NONE
1263
- */
1264
- setDisplay(display: number): void {
1265
- this._style.display = display
1266
- this.markDirty()
1267
- }
1268
-
1269
- /**
1270
- * Set the overflow behavior.
1271
- *
1272
- * @param overflow - OVERFLOW_VISIBLE, OVERFLOW_HIDDEN, or OVERFLOW_SCROLL
1273
- */
1274
- setOverflow(overflow: number): void {
1275
- this._style.overflow = overflow
1276
- this.markDirty()
1277
- }
1278
-
1279
- // ============================================================================
1280
- // Style Getters
1281
- // ============================================================================
1282
-
1283
- /**
1284
- * Get the width style value.
1285
- *
1286
- * @returns Width value with unit (points, percent, or auto)
1287
- */
1288
- getWidth(): Value {
1289
- return this._style.width
1290
- }
1291
-
1292
- /**
1293
- * Get the height style value.
1294
- *
1295
- * @returns Height value with unit (points, percent, or auto)
1296
- */
1297
- getHeight(): Value {
1298
- return this._style.height
1299
- }
1300
-
1301
- /**
1302
- * Get the minimum width style value.
1303
- *
1304
- * @returns Minimum width value with unit
1305
- */
1306
- getMinWidth(): Value {
1307
- return this._style.minWidth
1308
- }
1309
-
1310
- /**
1311
- * Get the minimum height style value.
1312
- *
1313
- * @returns Minimum height value with unit
1314
- */
1315
- getMinHeight(): Value {
1316
- return this._style.minHeight
1317
- }
1318
-
1319
- /**
1320
- * Get the maximum width style value.
1321
- *
1322
- * @returns Maximum width value with unit
1323
- */
1324
- getMaxWidth(): Value {
1325
- return this._style.maxWidth
1326
- }
1327
-
1328
- /**
1329
- * Get the maximum height style value.
1330
- *
1331
- * @returns Maximum height value with unit
1332
- */
1333
- getMaxHeight(): Value {
1334
- return this._style.maxHeight
1335
- }
1336
-
1337
- /**
1338
- * Get the aspect ratio.
1339
- *
1340
- * @returns Aspect ratio value (NaN if not set)
1341
- */
1342
- getAspectRatio(): number {
1343
- return this._style.aspectRatio
1344
- }
1345
-
1346
- /**
1347
- * Get the flex grow factor.
1348
- *
1349
- * @returns Flex grow value
1350
- */
1351
- getFlexGrow(): number {
1352
- return this._style.flexGrow
1353
- }
1354
-
1355
- /**
1356
- * Get the flex shrink factor.
1357
- *
1358
- * @returns Flex shrink value
1359
- */
1360
- getFlexShrink(): number {
1361
- return this._style.flexShrink
1362
- }
1363
-
1364
- /**
1365
- * Get the flex basis style value.
1366
- *
1367
- * @returns Flex basis value with unit
1368
- */
1369
- getFlexBasis(): Value {
1370
- return this._style.flexBasis
1371
- }
1372
-
1373
- /**
1374
- * Get the flex direction.
1375
- *
1376
- * @returns Flex direction constant
1377
- */
1378
- getFlexDirection(): number {
1379
- return this._style.flexDirection
1380
- }
1381
-
1382
- /**
1383
- * Get the flex wrap setting.
1384
- *
1385
- * @returns Flex wrap constant
1386
- */
1387
- getFlexWrap(): number {
1388
- return this._style.flexWrap
1389
- }
1390
-
1391
- /**
1392
- * Get the align items setting.
1393
- *
1394
- * @returns Align items constant
1395
- */
1396
- getAlignItems(): number {
1397
- return this._style.alignItems
1398
- }
1399
-
1400
- /**
1401
- * Get the align self setting.
1402
- *
1403
- * @returns Align self constant
1404
- */
1405
- getAlignSelf(): number {
1406
- return this._style.alignSelf
1407
- }
1408
-
1409
- /**
1410
- * Get the align content setting.
1411
- *
1412
- * @returns Align content constant
1413
- */
1414
- getAlignContent(): number {
1415
- return this._style.alignContent
1416
- }
1417
-
1418
- /**
1419
- * Get the justify content setting.
1420
- *
1421
- * @returns Justify content constant
1422
- */
1423
- getJustifyContent(): number {
1424
- return this._style.justifyContent
1425
- }
1426
-
1427
- /**
1428
- * Get the padding for a specific edge.
1429
- *
1430
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
1431
- * @returns Padding value with unit
1432
- */
1433
- getPadding(edge: number): Value {
1434
- return getEdgeValue(this._style.padding, edge)
1435
- }
1436
-
1437
- /**
1438
- * Get the margin for a specific edge.
1439
- *
1440
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
1441
- * @returns Margin value with unit
1442
- */
1443
- getMargin(edge: number): Value {
1444
- return getEdgeValue(this._style.margin, edge)
1445
- }
1446
-
1447
- /**
1448
- * Get the border width for a specific edge.
1449
- *
1450
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
1451
- * @returns Border width in points
1452
- */
1453
- getBorder(edge: number): number {
1454
- return getEdgeBorderValue(this._style.border, edge)
1455
- }
1456
-
1457
- /**
1458
- * Get the position offset for a specific edge.
1459
- *
1460
- * @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
1461
- * @returns Position value with unit
1462
- */
1463
- getPosition(edge: number): Value {
1464
- return getEdgeValue(this._style.position, edge)
1465
- }
1466
-
1467
- /**
1468
- * Get the position type.
1469
- *
1470
- * @returns Position type constant
1471
- */
1472
- getPositionType(): number {
1473
- return this._style.positionType
1474
- }
1475
-
1476
- /**
1477
- * Get the display type.
1478
- *
1479
- * @returns Display constant
1480
- */
1481
- getDisplay(): number {
1482
- return this._style.display
1483
- }
1484
-
1485
- /**
1486
- * Get the overflow setting.
1487
- *
1488
- * @returns Overflow constant
1489
- */
1490
- getOverflow(): number {
1491
- return this._style.overflow
1492
- }
1493
-
1494
- /**
1495
- * Get the gap for column or row.
1496
- *
1497
- * @param gutter - GUTTER_COLUMN or GUTTER_ROW
1498
- * @returns Gap size in points
1499
- */
1500
- getGap(gutter: number): number {
1501
- if (gutter === C.GUTTER_COLUMN) {
1502
- return this._style.gap[0]
1503
- } else if (gutter === C.GUTTER_ROW) {
1504
- return this._style.gap[1]
1505
- }
1506
- return this._style.gap[0] // Default to column gap
1507
- }
1508
- }