@rlabs-inc/tui 0.1.0 → 0.2.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 (38) hide show
  1. package/README.md +126 -13
  2. package/index.ts +11 -5
  3. package/package.json +2 -2
  4. package/src/api/mount.ts +42 -27
  5. package/src/engine/arrays/core.ts +13 -21
  6. package/src/engine/arrays/dimensions.ts +22 -32
  7. package/src/engine/arrays/index.ts +88 -86
  8. package/src/engine/arrays/interaction.ts +34 -48
  9. package/src/engine/arrays/layout.ts +67 -92
  10. package/src/engine/arrays/spacing.ts +37 -52
  11. package/src/engine/arrays/text.ts +23 -31
  12. package/src/engine/arrays/visual.ts +56 -75
  13. package/src/engine/inheritance.ts +18 -18
  14. package/src/engine/registry.ts +15 -0
  15. package/src/pipeline/frameBuffer.ts +26 -26
  16. package/src/pipeline/layout/index.ts +2 -2
  17. package/src/pipeline/layout/titan-engine.ts +112 -84
  18. package/src/primitives/animation.ts +194 -0
  19. package/src/primitives/box.ts +74 -86
  20. package/src/primitives/each.ts +87 -0
  21. package/src/primitives/index.ts +7 -0
  22. package/src/primitives/scope.ts +215 -0
  23. package/src/primitives/show.ts +77 -0
  24. package/src/primitives/text.ts +63 -59
  25. package/src/primitives/types.ts +1 -1
  26. package/src/primitives/when.ts +102 -0
  27. package/src/renderer/append-region.ts +303 -0
  28. package/src/renderer/index.ts +4 -2
  29. package/src/renderer/output.ts +11 -34
  30. package/src/state/focus.ts +16 -5
  31. package/src/state/global-keys.ts +184 -0
  32. package/src/state/index.ts +44 -8
  33. package/src/state/input.ts +534 -0
  34. package/src/state/keyboard.ts +98 -674
  35. package/src/state/mouse.ts +163 -340
  36. package/src/state/scroll.ts +7 -9
  37. package/src/types/index.ts +6 -0
  38. package/src/renderer/input.ts +0 -518
@@ -4,8 +4,11 @@
4
4
  * All component state lives in these parallel arrays.
5
5
  * Each array index corresponds to one component.
6
6
  *
7
- * Components write directly to these arrays.
8
- * Deriveds read from these arrays and RETURN computed values.
7
+ * Components write directly to these arrays using setSource().
8
+ * Deriveds read from these arrays directly (no unwrap needed).
9
+ *
10
+ * All arrays use slotArray for stable reactive cells that NEVER get replaced.
11
+ * This fixes the bind() tracking bug where deriveds miss updates.
9
12
  *
10
13
  * Array categories:
11
14
  * - core: Component type, parent, visibility
@@ -25,7 +28,7 @@ export * as visual from './visual'
25
28
  export * as text from './text'
26
29
  export * as interaction from './interaction'
27
30
 
28
- import { disconnectBinding } from '@rlabs-inc/signals'
31
+ import { type SlotArray } from '@rlabs-inc/signals'
29
32
  import * as core from './core'
30
33
  import * as dimensions from './dimensions'
31
34
  import * as spacing from './spacing'
@@ -62,12 +65,11 @@ export function clearAllAtIndex(index: number): void {
62
65
  interaction.clearAtIndex(index)
63
66
  }
64
67
 
65
- /** Disconnect all bindings in an array before truncating */
66
- function disconnectArray(arr: unknown[]): void {
68
+ /** Clear all slots in a SlotArray (slots are stable, just reset to defaults) */
69
+ function clearSlotArray<T>(arr: SlotArray<T>): void {
67
70
  for (let i = 0; i < arr.length; i++) {
68
- disconnectBinding(arr[i] as any)
71
+ arr.clear(i)
69
72
  }
70
- arr.length = 0
71
73
  }
72
74
 
73
75
  /**
@@ -75,92 +77,92 @@ function disconnectArray(arr: unknown[]): void {
75
77
  * Called automatically when all components are destroyed (allocatedIndices.size === 0).
76
78
  * This is the "reset on zero" cleanup - no manual API needed!
77
79
  *
78
- * IMPORTANT: Disconnects all bindings before truncating to break circular refs.
80
+ * SlotArrays are stable - we just clear them to defaults.
79
81
  */
80
82
  export function resetAllArrays(): void {
81
- // Core arrays
83
+ // Core arrays (componentType is plain array, rest are slotArrays)
82
84
  core.componentType.length = 0
83
- disconnectArray(core.parentIndex)
84
- disconnectArray(core.visible)
85
- disconnectArray(core.componentId)
85
+ clearSlotArray(core.parentIndex)
86
+ clearSlotArray(core.visible)
87
+ clearSlotArray(core.componentId)
86
88
 
87
- // Dimension arrays
88
- disconnectArray(dimensions.width)
89
- disconnectArray(dimensions.height)
90
- disconnectArray(dimensions.minWidth)
91
- disconnectArray(dimensions.minHeight)
92
- disconnectArray(dimensions.maxWidth)
93
- disconnectArray(dimensions.maxHeight)
89
+ // Dimension arrays (all slotArrays)
90
+ clearSlotArray(dimensions.width)
91
+ clearSlotArray(dimensions.height)
92
+ clearSlotArray(dimensions.minWidth)
93
+ clearSlotArray(dimensions.minHeight)
94
+ clearSlotArray(dimensions.maxWidth)
95
+ clearSlotArray(dimensions.maxHeight)
94
96
 
95
- // Spacing arrays
96
- disconnectArray(spacing.marginTop)
97
- disconnectArray(spacing.marginRight)
98
- disconnectArray(spacing.marginBottom)
99
- disconnectArray(spacing.marginLeft)
100
- disconnectArray(spacing.paddingTop)
101
- disconnectArray(spacing.paddingRight)
102
- disconnectArray(spacing.paddingBottom)
103
- disconnectArray(spacing.paddingLeft)
104
- disconnectArray(spacing.gap)
105
- disconnectArray(spacing.rowGap)
106
- disconnectArray(spacing.columnGap)
97
+ // Spacing arrays (all slotArrays)
98
+ clearSlotArray(spacing.marginTop)
99
+ clearSlotArray(spacing.marginRight)
100
+ clearSlotArray(spacing.marginBottom)
101
+ clearSlotArray(spacing.marginLeft)
102
+ clearSlotArray(spacing.paddingTop)
103
+ clearSlotArray(spacing.paddingRight)
104
+ clearSlotArray(spacing.paddingBottom)
105
+ clearSlotArray(spacing.paddingLeft)
106
+ clearSlotArray(spacing.gap)
107
+ clearSlotArray(spacing.rowGap)
108
+ clearSlotArray(spacing.columnGap)
107
109
 
108
- // Layout arrays
109
- disconnectArray(layout.flexDirection)
110
- disconnectArray(layout.flexWrap)
111
- disconnectArray(layout.justifyContent)
112
- disconnectArray(layout.alignItems)
113
- disconnectArray(layout.alignContent)
114
- disconnectArray(layout.flexGrow)
115
- disconnectArray(layout.flexShrink)
116
- disconnectArray(layout.flexBasis)
117
- disconnectArray(layout.alignSelf)
118
- disconnectArray(layout.order)
119
- disconnectArray(layout.position)
120
- disconnectArray(layout.top)
121
- disconnectArray(layout.right)
122
- disconnectArray(layout.bottom)
123
- disconnectArray(layout.left)
124
- disconnectArray(layout.borderTop)
125
- disconnectArray(layout.borderRight)
126
- disconnectArray(layout.borderBottom)
127
- disconnectArray(layout.borderLeft)
128
- disconnectArray(layout.zIndex)
129
- disconnectArray(layout.overflow)
110
+ // Layout arrays (all slotArrays)
111
+ clearSlotArray(layout.flexDirection)
112
+ clearSlotArray(layout.flexWrap)
113
+ clearSlotArray(layout.justifyContent)
114
+ clearSlotArray(layout.alignItems)
115
+ clearSlotArray(layout.alignContent)
116
+ clearSlotArray(layout.flexGrow)
117
+ clearSlotArray(layout.flexShrink)
118
+ clearSlotArray(layout.flexBasis)
119
+ clearSlotArray(layout.alignSelf)
120
+ clearSlotArray(layout.order)
121
+ clearSlotArray(layout.position)
122
+ clearSlotArray(layout.top)
123
+ clearSlotArray(layout.right)
124
+ clearSlotArray(layout.bottom)
125
+ clearSlotArray(layout.left)
126
+ clearSlotArray(layout.borderTop)
127
+ clearSlotArray(layout.borderRight)
128
+ clearSlotArray(layout.borderBottom)
129
+ clearSlotArray(layout.borderLeft)
130
+ clearSlotArray(layout.zIndex)
131
+ clearSlotArray(layout.overflow)
130
132
 
131
- // Visual arrays
132
- disconnectArray(visual.fgColor)
133
- disconnectArray(visual.bgColor)
134
- disconnectArray(visual.opacity)
135
- disconnectArray(visual.borderStyle)
136
- disconnectArray(visual.borderColor)
137
- disconnectArray(visual.borderTop)
138
- disconnectArray(visual.borderRight)
139
- disconnectArray(visual.borderBottom)
140
- disconnectArray(visual.borderLeft)
141
- disconnectArray(visual.borderColorTop)
142
- disconnectArray(visual.borderColorRight)
143
- disconnectArray(visual.borderColorBottom)
144
- disconnectArray(visual.borderColorLeft)
145
- disconnectArray(visual.showFocusRing)
146
- disconnectArray(visual.focusRingColor)
133
+ // Visual arrays (all slotArrays)
134
+ clearSlotArray(visual.fgColor)
135
+ clearSlotArray(visual.bgColor)
136
+ clearSlotArray(visual.opacity)
137
+ clearSlotArray(visual.borderStyle)
138
+ clearSlotArray(visual.borderColor)
139
+ clearSlotArray(visual.borderTop)
140
+ clearSlotArray(visual.borderRight)
141
+ clearSlotArray(visual.borderBottom)
142
+ clearSlotArray(visual.borderLeft)
143
+ clearSlotArray(visual.borderColorTop)
144
+ clearSlotArray(visual.borderColorRight)
145
+ clearSlotArray(visual.borderColorBottom)
146
+ clearSlotArray(visual.borderColorLeft)
147
+ clearSlotArray(visual.showFocusRing)
148
+ clearSlotArray(visual.focusRingColor)
147
149
 
148
- // Text arrays
149
- disconnectArray(text.textContent)
150
- disconnectArray(text.textAttrs)
151
- disconnectArray(text.textAlign)
152
- disconnectArray(text.textWrap)
153
- disconnectArray(text.ellipsis)
150
+ // Text arrays (all slotArrays)
151
+ clearSlotArray(text.textContent)
152
+ clearSlotArray(text.textAttrs)
153
+ clearSlotArray(text.textAlign)
154
+ clearSlotArray(text.textWrap)
155
+ clearSlotArray(text.ellipsis)
154
156
 
155
- // Interaction arrays
156
- disconnectArray(interaction.scrollOffsetX)
157
- disconnectArray(interaction.scrollOffsetY)
158
- disconnectArray(interaction.focusable)
159
- disconnectArray(interaction.tabIndex)
160
- disconnectArray(interaction.hovered)
161
- disconnectArray(interaction.pressed)
162
- disconnectArray(interaction.mouseEnabled)
163
- disconnectArray(interaction.cursorPosition)
164
- disconnectArray(interaction.selectionStart)
165
- disconnectArray(interaction.selectionEnd)
157
+ // Interaction arrays (all slotArrays)
158
+ clearSlotArray(interaction.scrollOffsetX)
159
+ clearSlotArray(interaction.scrollOffsetY)
160
+ clearSlotArray(interaction.focusable)
161
+ clearSlotArray(interaction.tabIndex)
162
+ clearSlotArray(interaction.hovered)
163
+ clearSlotArray(interaction.pressed)
164
+ clearSlotArray(interaction.mouseEnabled)
165
+ clearSlotArray(interaction.cursorPosition)
166
+ clearSlotArray(interaction.selectionStart)
167
+ clearSlotArray(interaction.selectionEnd)
166
168
  }
@@ -3,8 +3,7 @@
3
3
  *
4
4
  * State for scroll, focus, and mouse interactions.
5
5
  *
6
- * CRITICAL: Use regular arrays (NOT state!) to preserve binding getters.
7
- * state() proxies snapshot getter values, breaking reactivity.
6
+ * Uses slotArray for stable reactive cells that NEVER get replaced.
8
7
  *
9
8
  * Exception: focusedIndex is a single signal, not an array.
10
9
  *
@@ -12,17 +11,17 @@
12
11
  * and live in ComputedLayout, not here. Only scroll OFFSETS (user state) are here.
13
12
  */
14
13
 
15
- import { signal, bind, disconnectBinding, type Binding } from '@rlabs-inc/signals'
14
+ import { signal, slotArray, type SlotArray } from '@rlabs-inc/signals'
16
15
 
17
16
  // =============================================================================
18
17
  // SCROLL STATE (user-controlled position only)
19
18
  // =============================================================================
20
19
 
21
20
  /** Current vertical scroll offset (user state) */
22
- export const scrollOffsetY: Binding<number>[] = []
21
+ export const scrollOffsetY: SlotArray<number> = slotArray<number>(0)
23
22
 
24
23
  /** Current horizontal scroll offset (user state) */
25
- export const scrollOffsetX: Binding<number>[] = []
24
+ export const scrollOffsetX: SlotArray<number> = slotArray<number>(0)
26
25
 
27
26
  // NOTE: scrollable, maxScrollX, maxScrollY are COMPUTED values from TITAN.
28
27
  // Read them from layoutDerived.value, not from interaction arrays.
@@ -32,10 +31,10 @@ export const scrollOffsetX: Binding<number>[] = []
32
31
  // =============================================================================
33
32
 
34
33
  /** Can this component receive focus */
35
- export const focusable: Binding<number>[] = [] // 0=no, 1=yes
34
+ export const focusable: SlotArray<number> = slotArray<number>(0) // 0=no, 1=yes
36
35
 
37
36
  /** Tab order for focus navigation (-1 = not in tab order) */
38
- export const tabIndex: Binding<number>[] = []
37
+ export const tabIndex: SlotArray<number> = slotArray<number>(-1)
39
38
 
40
39
  /** Currently focused component index (-1 = none) - SINGLE SIGNAL, not array */
41
40
  export const focusedIndex = signal(-1)
@@ -45,68 +44,55 @@ export const focusedIndex = signal(-1)
45
44
  // =============================================================================
46
45
 
47
46
  /** Is mouse currently hovering over this component */
48
- export const hovered: Binding<number>[] = [] // 0=no, 1=yes
47
+ export const hovered: SlotArray<number> = slotArray<number>(0) // 0=no, 1=yes
49
48
 
50
49
  /** Is mouse button currently pressed on this component */
51
- export const pressed: Binding<number>[] = [] // 0=no, 1=yes
50
+ export const pressed: SlotArray<number> = slotArray<number>(0) // 0=no, 1=yes
52
51
 
53
52
  /** Is mouse events enabled for this component */
54
- export const mouseEnabled: Binding<number>[] = [] // 0=no, 1=yes
53
+ export const mouseEnabled: SlotArray<number> = slotArray<number>(0) // 0=no, 1=yes
55
54
 
56
55
  // =============================================================================
57
56
  // CURSOR STATE (for input components)
58
57
  // =============================================================================
59
58
 
60
59
  /** Cursor position in text content */
61
- export const cursorPosition: Binding<number>[] = []
60
+ export const cursorPosition: SlotArray<number> = slotArray<number>(0)
62
61
 
63
62
  /** Selection start position (-1 = no selection) */
64
- export const selectionStart: Binding<number>[] = []
63
+ export const selectionStart: SlotArray<number> = slotArray<number>(-1)
65
64
 
66
65
  /** Selection end position */
67
- export const selectionEnd: Binding<number>[] = []
66
+ export const selectionEnd: SlotArray<number> = slotArray<number>(-1)
68
67
 
69
68
  // =============================================================================
70
69
  // CAPACITY MANAGEMENT
71
70
  // =============================================================================
72
71
 
73
- /** LAZY BINDING: Push undefined, primitives create bindings for used props only */
72
+ /** Ensure capacity for all interaction arrays */
74
73
  export function ensureCapacity(index: number): void {
75
- while (scrollOffsetY.length <= index) {
76
- scrollOffsetY.push(undefined as any)
77
- scrollOffsetX.push(undefined as any)
78
- focusable.push(undefined as any)
79
- tabIndex.push(undefined as any)
80
- hovered.push(undefined as any)
81
- pressed.push(undefined as any)
82
- mouseEnabled.push(undefined as any)
83
- cursorPosition.push(undefined as any)
84
- selectionStart.push(undefined as any)
85
- selectionEnd.push(undefined as any)
86
- }
74
+ scrollOffsetY.ensureCapacity(index)
75
+ scrollOffsetX.ensureCapacity(index)
76
+ focusable.ensureCapacity(index)
77
+ tabIndex.ensureCapacity(index)
78
+ hovered.ensureCapacity(index)
79
+ pressed.ensureCapacity(index)
80
+ mouseEnabled.ensureCapacity(index)
81
+ cursorPosition.ensureCapacity(index)
82
+ selectionStart.ensureCapacity(index)
83
+ selectionEnd.ensureCapacity(index)
87
84
  }
88
85
 
86
+ /** Clear slot at index (reset to default) */
89
87
  export function clearAtIndex(index: number): void {
90
- if (index < scrollOffsetY.length) {
91
- disconnectBinding(scrollOffsetY[index])
92
- disconnectBinding(scrollOffsetX[index])
93
- disconnectBinding(focusable[index])
94
- disconnectBinding(tabIndex[index])
95
- disconnectBinding(hovered[index])
96
- disconnectBinding(pressed[index])
97
- disconnectBinding(mouseEnabled[index])
98
- disconnectBinding(cursorPosition[index])
99
- disconnectBinding(selectionStart[index])
100
- disconnectBinding(selectionEnd[index])
101
- scrollOffsetY[index] = undefined as any
102
- scrollOffsetX[index] = undefined as any
103
- focusable[index] = undefined as any
104
- tabIndex[index] = undefined as any
105
- hovered[index] = undefined as any
106
- pressed[index] = undefined as any
107
- mouseEnabled[index] = undefined as any
108
- cursorPosition[index] = undefined as any
109
- selectionStart[index] = undefined as any
110
- selectionEnd[index] = undefined as any
111
- }
88
+ scrollOffsetY.clear(index)
89
+ scrollOffsetX.clear(index)
90
+ focusable.clear(index)
91
+ tabIndex.clear(index)
92
+ hovered.clear(index)
93
+ pressed.clear(index)
94
+ mouseEnabled.clear(index)
95
+ cursorPosition.clear(index)
96
+ selectionStart.clear(index)
97
+ selectionEnd.clear(index)
112
98
  }
@@ -4,8 +4,7 @@
4
4
  * Flexbox properties, positioning, and stacking.
5
5
  * Uses numeric enums for compact storage.
6
6
  *
7
- * CRITICAL: Use regular arrays (NOT state!) to preserve binding getters.
8
- * state() proxies snapshot getter values, breaking reactivity.
7
+ * Uses slotArray for stable reactive cells that NEVER get replaced.
9
8
  *
10
9
  * Flex direction: 0=column, 1=row, 2=column-reverse, 3=row-reverse
11
10
  * Flex wrap: 0=nowrap, 1=wrap, 2=wrap-reverse
@@ -15,161 +14,137 @@
15
14
  * Overflow: 0=visible, 1=hidden, 2=scroll
16
15
  */
17
16
 
18
- import { bind, disconnectBinding, type Binding } from '@rlabs-inc/signals'
17
+ import { slotArray, type SlotArray } from '@rlabs-inc/signals'
19
18
 
20
19
  // =============================================================================
21
20
  // FLEX CONTAINER PROPERTIES
22
21
  // =============================================================================
23
22
 
24
23
  /** Main axis direction: 0=column, 1=row, 2=column-reverse, 3=row-reverse */
25
- export const flexDirection: Binding<number>[] = []
24
+ export const flexDirection: SlotArray<number> = slotArray<number>(0)
26
25
 
27
26
  /** Wrap behavior: 0=nowrap, 1=wrap, 2=wrap-reverse */
28
- export const flexWrap: Binding<number>[] = []
27
+ export const flexWrap: SlotArray<number> = slotArray<number>(0)
29
28
 
30
29
  /** Main axis alignment: 0=flex-start, 1=center, 2=flex-end, 3=space-between, 4=space-around, 5=space-evenly */
31
- export const justifyContent: Binding<number>[] = []
30
+ export const justifyContent: SlotArray<number> = slotArray<number>(0)
32
31
 
33
32
  /** Cross axis alignment for items: 0=stretch, 1=flex-start, 2=center, 3=flex-end, 4=baseline */
34
- export const alignItems: Binding<number>[] = []
33
+ export const alignItems: SlotArray<number> = slotArray<number>(0)
35
34
 
36
35
  /** Cross axis alignment for wrapped lines: 0=stretch, 1=flex-start, 2=center, 3=flex-end */
37
- export const alignContent: Binding<number>[] = []
36
+ export const alignContent: SlotArray<number> = slotArray<number>(0)
38
37
 
39
38
  // =============================================================================
40
39
  // FLEX ITEM PROPERTIES
41
40
  // =============================================================================
42
41
 
43
42
  /** Grow factor - how much item grows to fill space (0 = don't grow) */
44
- export const flexGrow: Binding<number>[] = []
43
+ export const flexGrow: SlotArray<number> = slotArray<number>(0)
45
44
 
46
45
  /** Shrink factor - how much item shrinks when overflow (1 = default, 0 = don't shrink) */
47
- export const flexShrink: Binding<number>[] = []
46
+ export const flexShrink: SlotArray<number> = slotArray<number>(1)
48
47
 
49
48
  /** Initial size before growing/shrinking (0 = use width/height) */
50
- export const flexBasis: Binding<number>[] = []
49
+ export const flexBasis: SlotArray<number> = slotArray<number>(0)
51
50
 
52
51
  /** Override alignItems for this item: 0=auto, 1=stretch, 2=flex-start, 3=center, 4=flex-end */
53
- export const alignSelf: Binding<number>[] = []
52
+ export const alignSelf: SlotArray<number> = slotArray<number>(0)
54
53
 
55
54
  /** Visual order override (not used yet) */
56
- export const order: Binding<number>[] = []
55
+ export const order: SlotArray<number> = slotArray<number>(0)
57
56
 
58
57
  // =============================================================================
59
58
  // POSITIONING
60
59
  // =============================================================================
61
60
 
62
61
  /** Position mode: 0=relative (in flow), 1=absolute (out of flow) */
63
- export const position: Binding<number>[] = []
62
+ export const position: SlotArray<number> = slotArray<number>(0)
64
63
 
65
64
  /** Offset from top of positioned ancestor (for absolute) or normal position (for relative) */
66
- export const top: Binding<number>[] = []
65
+ export const top: SlotArray<number> = slotArray<number>(0)
67
66
 
68
67
  /** Offset from right of positioned ancestor */
69
- export const right: Binding<number>[] = []
68
+ export const right: SlotArray<number> = slotArray<number>(0)
70
69
 
71
70
  /** Offset from bottom of positioned ancestor */
72
- export const bottom: Binding<number>[] = []
71
+ export const bottom: SlotArray<number> = slotArray<number>(0)
73
72
 
74
73
  /** Offset from left of positioned ancestor */
75
- export const left: Binding<number>[] = []
74
+ export const left: SlotArray<number> = slotArray<number>(0)
76
75
 
77
76
  // =============================================================================
78
77
  // BORDER (for layout calculations)
79
78
  // =============================================================================
80
79
 
81
80
  /** Has top border? 0=no, 1+=yes (takes 1 cell) */
82
- export const borderTop: Binding<number>[] = []
81
+ export const borderTop: SlotArray<number> = slotArray<number>(0)
83
82
 
84
83
  /** Has right border? 0=no, 1+=yes (takes 1 cell) */
85
- export const borderRight: Binding<number>[] = []
84
+ export const borderRight: SlotArray<number> = slotArray<number>(0)
86
85
 
87
86
  /** Has bottom border? 0=no, 1+=yes (takes 1 cell) */
88
- export const borderBottom: Binding<number>[] = []
87
+ export const borderBottom: SlotArray<number> = slotArray<number>(0)
89
88
 
90
89
  /** Has left border? 0=no, 1+=yes (takes 1 cell) */
91
- export const borderLeft: Binding<number>[] = []
90
+ export const borderLeft: SlotArray<number> = slotArray<number>(0)
92
91
 
93
92
  // =============================================================================
94
93
  // STACKING & OVERFLOW
95
94
  // =============================================================================
96
95
 
97
96
  /** Z-index for overlapping components (higher = on top) */
98
- export const zIndex: Binding<number>[] = []
97
+ export const zIndex: SlotArray<number> = slotArray<number>(0)
99
98
 
100
99
  /** Overflow handling: 0=visible, 1=hidden, 2=scroll, 3=auto */
101
- export const overflow: Binding<number>[] = []
100
+ export const overflow: SlotArray<number> = slotArray<number>(0)
102
101
 
103
- /** LAZY BINDING: Push undefined, primitives create bindings for used props only */
102
+ /** Ensure capacity for all layout arrays */
104
103
  export function ensureCapacity(index: number): void {
105
- while (flexDirection.length <= index) {
106
- flexDirection.push(undefined as any)
107
- flexWrap.push(undefined as any)
108
- justifyContent.push(undefined as any)
109
- alignItems.push(undefined as any)
110
- alignContent.push(undefined as any)
111
- flexGrow.push(undefined as any)
112
- flexShrink.push(undefined as any)
113
- flexBasis.push(undefined as any)
114
- alignSelf.push(undefined as any)
115
- order.push(undefined as any)
116
- position.push(undefined as any)
117
- top.push(undefined as any)
118
- right.push(undefined as any)
119
- bottom.push(undefined as any)
120
- left.push(undefined as any)
121
- borderTop.push(undefined as any)
122
- borderRight.push(undefined as any)
123
- borderBottom.push(undefined as any)
124
- borderLeft.push(undefined as any)
125
- zIndex.push(undefined as any)
126
- overflow.push(undefined as any)
127
- }
104
+ flexDirection.ensureCapacity(index)
105
+ flexWrap.ensureCapacity(index)
106
+ justifyContent.ensureCapacity(index)
107
+ alignItems.ensureCapacity(index)
108
+ alignContent.ensureCapacity(index)
109
+ flexGrow.ensureCapacity(index)
110
+ flexShrink.ensureCapacity(index)
111
+ flexBasis.ensureCapacity(index)
112
+ alignSelf.ensureCapacity(index)
113
+ order.ensureCapacity(index)
114
+ position.ensureCapacity(index)
115
+ top.ensureCapacity(index)
116
+ right.ensureCapacity(index)
117
+ bottom.ensureCapacity(index)
118
+ left.ensureCapacity(index)
119
+ borderTop.ensureCapacity(index)
120
+ borderRight.ensureCapacity(index)
121
+ borderBottom.ensureCapacity(index)
122
+ borderLeft.ensureCapacity(index)
123
+ zIndex.ensureCapacity(index)
124
+ overflow.ensureCapacity(index)
128
125
  }
129
126
 
127
+ /** Clear slot at index (reset to default) */
130
128
  export function clearAtIndex(index: number): void {
131
- if (index < flexDirection.length) {
132
- disconnectBinding(flexDirection[index])
133
- disconnectBinding(flexWrap[index])
134
- disconnectBinding(justifyContent[index])
135
- disconnectBinding(alignItems[index])
136
- disconnectBinding(alignContent[index])
137
- disconnectBinding(flexGrow[index])
138
- disconnectBinding(flexShrink[index])
139
- disconnectBinding(flexBasis[index])
140
- disconnectBinding(alignSelf[index])
141
- disconnectBinding(order[index])
142
- disconnectBinding(position[index])
143
- disconnectBinding(top[index])
144
- disconnectBinding(right[index])
145
- disconnectBinding(bottom[index])
146
- disconnectBinding(left[index])
147
- disconnectBinding(borderTop[index])
148
- disconnectBinding(borderRight[index])
149
- disconnectBinding(borderBottom[index])
150
- disconnectBinding(borderLeft[index])
151
- disconnectBinding(zIndex[index])
152
- disconnectBinding(overflow[index])
153
- flexDirection[index] = undefined as any
154
- flexWrap[index] = undefined as any
155
- justifyContent[index] = undefined as any
156
- alignItems[index] = undefined as any
157
- alignContent[index] = undefined as any
158
- flexGrow[index] = undefined as any
159
- flexShrink[index] = undefined as any
160
- flexBasis[index] = undefined as any
161
- alignSelf[index] = undefined as any
162
- order[index] = undefined as any
163
- position[index] = undefined as any
164
- top[index] = undefined as any
165
- right[index] = undefined as any
166
- bottom[index] = undefined as any
167
- left[index] = undefined as any
168
- borderTop[index] = undefined as any
169
- borderRight[index] = undefined as any
170
- borderBottom[index] = undefined as any
171
- borderLeft[index] = undefined as any
172
- zIndex[index] = undefined as any
173
- overflow[index] = undefined as any
174
- }
129
+ flexDirection.clear(index)
130
+ flexWrap.clear(index)
131
+ justifyContent.clear(index)
132
+ alignItems.clear(index)
133
+ alignContent.clear(index)
134
+ flexGrow.clear(index)
135
+ flexShrink.clear(index)
136
+ flexBasis.clear(index)
137
+ alignSelf.clear(index)
138
+ order.clear(index)
139
+ position.clear(index)
140
+ top.clear(index)
141
+ right.clear(index)
142
+ bottom.clear(index)
143
+ left.clear(index)
144
+ borderTop.clear(index)
145
+ borderRight.clear(index)
146
+ borderBottom.clear(index)
147
+ borderLeft.clear(index)
148
+ zIndex.clear(index)
149
+ overflow.clear(index)
175
150
  }