@symbiote-native/components 3.1.0 → 3.1.2
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 +17 -18
- package/build/behaviors/activity-indicator/shared.js +29 -71
- package/build/behaviors/button.d.ts +0 -9
- package/build/behaviors/button.js +48 -234
- package/build/behaviors/image-background.js +21 -81
- package/build/behaviors/image.js +3 -10
- package/build/behaviors/input-accessory-view.js +10 -51
- package/build/behaviors/pressable.d.ts +0 -48
- package/build/behaviors/pressable.js +59 -149
- package/build/behaviors/scroll-view/index.android.js +10 -30
- package/build/behaviors/scroll-view/shared.js +55 -186
- package/build/behaviors/scroll-view/sticky.d.ts +0 -8
- package/build/behaviors/scroll-view/sticky.js +54 -142
- package/build/behaviors/text-input.d.ts +0 -8
- package/build/behaviors/text-input.js +57 -156
- package/build/behaviors/touchable-highlight.js +14 -54
- package/build/behaviors/touchable-native-feedback.js +9 -32
- package/build/behaviors/touchable-opacity.js +3 -18
- package/build/behaviors/touchable-without-feedback.js +6 -24
- package/build/bootstrap/index.d.ts +1 -0
- package/build/bootstrap/index.js +2 -1
- package/build/component-names/index.android.js +6 -8
- package/build/component-names/shared.js +12 -42
- package/build/index.js +13 -19
- package/build/scroll-view-commands.js +9 -17
- package/build/state/pressable.js +18 -43
- package/build/state/sticky-header-reducer.js +103 -149
- package/build/state/touchable.js +3 -5
- package/build/state/virtualized-list-reducer.js +21 -48
- package/build/state/virtualized-list.js +63 -148
- package/build/text-props.js +3 -13
- package/build/view/render-button.js +13 -53
- package/build/view/render-input-accessory-view.js +8 -24
- package/build/view/render-pressable/index.js +3 -4
- package/build/view/render-scroll-view.js +13 -23
- package/build/view/render-touchable-native-feedback.js +5 -14
- package/host-primitives.cjs +33 -207
- package/package.json +3 -3
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
// ScrollView
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// the content-size dedupe are all platform- and framework-invariant, so they live here. The
|
|
5
|
-
// adapter owns the lifecycle (refs/state/effects) and the element assembly; it calls these pure
|
|
6
|
-
// helpers from prepareScrollView. What diverges per platform, and how a RefreshControl
|
|
7
|
-
// integrates, stays in the adapter's .ios/.android files.
|
|
1
|
+
// ScrollView's render half (framework-agnostic): picking intrinsics, reading layout dimensions,
|
|
2
|
+
// and the content-size dedupe are platform-invariant, so they live here; the adapter owns
|
|
3
|
+
// lifecycle + element assembly, calling these pure helpers from prepareScrollView.
|
|
8
4
|
import { readLayoutField } from './layout-event.js';
|
|
9
5
|
// Thin re-export kept for the existing public surface (adapters import this name from
|
|
10
6
|
// `@symbiote-native/components`); the actual field read is shared with render-scroll-sticky's
|
|
@@ -31,20 +27,16 @@ export function selectScrollIntrinsics(isHorizontal, contentContainerStyle) {
|
|
|
31
27
|
contentStyle,
|
|
32
28
|
};
|
|
33
29
|
}
|
|
34
|
-
// When sticky headers are active the scroll offset must reach the AnimatedValue; RN raises the
|
|
35
|
-
// event rate for it (ScrollView.js:1798): throttle 1 on the native driver
|
|
36
|
-
//
|
|
37
|
-
// throttle passes through untouched. These two magic numbers were copied into all three adapters.
|
|
30
|
+
// When sticky headers are active, the scroll offset must reach the AnimatedValue; RN raises the
|
|
31
|
+
// scroll event rate for it (ScrollView.js:1798): throttle 1 on the native driver, 16 on the JS
|
|
32
|
+
// fallback. Without sticky headers the user's throttle passes through untouched.
|
|
38
33
|
const STICKY_NATIVE_SCROLL_THROTTLE = 1;
|
|
39
34
|
const STICKY_JS_SCROLL_THROTTLE = 16;
|
|
40
|
-
// maintainVisibleContentPosition
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
// Named and exported rather than inlined because the host behavior's slot fold needs the same
|
|
46
|
-
// answer from a different place — it has the owner's raw props and none of the sticky inputs
|
|
47
|
-
// `resolveScrollForwarding` also takes. One exported function is what keeps the two from drifting.
|
|
35
|
+
// maintainVisibleContentPosition/snapToAlignment anchor against mounted cells; Android Fabric
|
|
36
|
+
// view-flattens layout-only cells away, so RN keeps them real with collapsableChildren={false}
|
|
37
|
+
// (ScrollView.js:1731). No-op on iOS.
|
|
38
|
+
// Exported rather than inlined: the host behavior's slot fold needs the same answer from a
|
|
39
|
+
// place with only the owner's raw props, none of `resolveScrollForwarding`'s sticky inputs.
|
|
48
40
|
export function preservesContentChildren(maintainVisibleContentPosition, snapToAlignment) {
|
|
49
41
|
return (maintainVisibleContentPosition !== undefined ||
|
|
50
42
|
snapToAlignment !== undefined);
|
|
@@ -75,10 +67,8 @@ export function resolveScrollForwarding(inputs) {
|
|
|
75
67
|
collapsableChildren,
|
|
76
68
|
};
|
|
77
69
|
}
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
// pass; RN dedupes so the user handler only sees real size changes (ScrollView.js). First
|
|
81
|
-
// measurement (last === null) always counts as a change.
|
|
70
|
+
// onContentSizeChange synthesizes from the content view's onLayout, which fires on every layout
|
|
71
|
+
// pass — RN dedupes so the user handler only sees real size changes.
|
|
82
72
|
export function didContentSizeChange(last, next) {
|
|
83
73
|
if (last === null)
|
|
84
74
|
return true;
|
|
@@ -27,20 +27,11 @@ export function selectableBackgroundBorderless(rippleRadius) {
|
|
|
27
27
|
export function rippleBackground(color, borderless, rippleRadius) {
|
|
28
28
|
return { type: 'RippleAndroid', color, borderless, rippleRadius };
|
|
29
29
|
}
|
|
30
|
-
// `backgroundProps`
|
|
31
|
-
//
|
|
32
|
-
// `
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
// the package barrel. Found by asking what each `Platform.OS` branch still left in
|
|
36
|
-
// `core/components` is FOR. Its C++ twin is covered on both arms —
|
|
37
|
-
// `android-rules.android.itest.ts` asserts the foreground slot and `clone-onto-child-payload.itest.ts`
|
|
38
|
-
// asserts its absence off Android — and break-testing the gate fires exactly one of them.
|
|
39
|
-
//
|
|
40
|
-
// `canUseNativeForeground` below stays, and the distinction is the reusable half: it is a QUESTION
|
|
41
|
-
// an app asks the platform (`TouchableNativeFeedback.canUseNativeForeground()` is RN's own public
|
|
42
|
-
// static), not a rule that decides a payload. Same class as the slider reading a folded
|
|
43
|
-
// `accessibilityState` — asking is not reimplementing.
|
|
30
|
+
// `backgroundProps` mapped the resolved background + `useForeground` onto the native slot — now
|
|
31
|
+
// the `#ifdef ANDROID` tail of `foldCloneOntoChild` in `SymbioteFabricProps.cpp`, covered by
|
|
32
|
+
// `android-rules.android.itest.ts` and `clone-onto-child-payload.itest.ts`.
|
|
33
|
+
// `canUseNativeForeground` below stays: it's a QUESTION an app asks the platform (RN's own public
|
|
34
|
+
// static), not a rule deciding a payload — same class as reading a folded `accessibilityState`.
|
|
44
35
|
/**
|
|
45
36
|
* RN's four statics, under RN's own spelling — `TouchableNativeFeedback.Ripple(color, borderless)`.
|
|
46
37
|
*
|
package/host-primitives.cjs
CHANGED
|
@@ -1,87 +1,39 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// WHAT IT DOES NOT UNIFY, stated here so nobody hunts for a contradiction that is not one: this
|
|
10
|
-
// says WHAT a fold is, never WHICH LAYER applies it. Svelte folds Text's defaults in its DOM shim;
|
|
11
|
-
// Vue and Solid apply the same fold in their renderers. Both are correct.
|
|
12
|
-
//
|
|
13
|
-
// Four transforms carried their own copy of this before it existed, and it had already produced a
|
|
14
|
-
// real behaviour split — the `aliases` note below is what is left of it.
|
|
1
|
+
// Which intrinsic tag each primitive is, what it folds, which of its two Fabric views a prop
|
|
2
|
+
// selects. Data only. A `.cjs` (not `src/`) because build-tool consumers like
|
|
3
|
+
// `adapters/vue/intrinsic-tags.cjs` run before any TS exists and can't import from `src/`.
|
|
4
|
+
|
|
5
|
+
// `intrinsicWhen`: BOUNDARY IS IDENTITY not truthiness — only literal `true` picks the
|
|
6
|
+
// alternative view. Kept in sync by hand with `host-primitives.d.cts`; this `.cjs` compiles fine
|
|
7
|
+
// if they drift, only the one TS consumer's `tsc` catches it.
|
|
15
8
|
|
|
16
|
-
// `intrinsicWhen` DECLARED AHEAD OF ITS FIRST ENTRY.
|
|
17
|
-
//
|
|
18
|
-
// WHAT IT IS FOR. `TextInput` is the first primitive whose TAG depends on a prop: `multiline`
|
|
19
|
-
// selects between two different Fabric views, `text-input` and
|
|
20
|
-
// `text-input-multiline` (`src/view/render-text-input.ts:33`), not between two values of
|
|
21
|
-
// one view. `src/resolve-intrinsic.ts` reads it at element creation, where the value is known.
|
|
22
|
-
//
|
|
23
|
-
// ONE selector and ONE alternative, deliberately — not a map and not a list. There is exactly one
|
|
24
|
-
// such prop in the whole surface, and a wider field would be invented rather than needed. Absent
|
|
25
|
-
// `intrinsicWhen` means "one tag", so no existing entry changes.
|
|
26
|
-
//
|
|
27
|
-
// THE BOUNDARY IS IDENTITY, not truthiness: only `true` picks the alternative, and a truthy
|
|
28
|
-
// non-boolean like `multiline={1}` does not. The spec types the selector as a boolean; guessing
|
|
29
|
-
// past that commits the wrong native view, and no later prop write moves a node between views.
|
|
30
|
-
//
|
|
31
|
-
// THE TYPEDEF BELOW IS NOT WHAT TYPESCRIPT READS. `host-primitives.d.cts` is a hand-written
|
|
32
|
-
// declaration file, and a field added here and not there compiles fine in every `.cjs` transform
|
|
33
|
-
// while failing `tsc` in the one consumer written in TypeScript — measured 2026-08-31, when
|
|
34
|
-
// `intrinsicWhen` landed here alone and reddened Svelte's preprocessor with the whole suite green
|
|
35
|
-
// (vitest does not typecheck). The reverse is worse and silent: a field in the `.d.cts` and not
|
|
36
|
-
// here typechecks everywhere and arrives `undefined` in all five transforms. Change both, together.
|
|
37
9
|
/**
|
|
38
10
|
* @typedef {{ prop: string, intrinsic: string }} IIntrinsicWhen
|
|
39
11
|
* @typedef {{ intrinsic: string, intrinsicWhen?: IIntrinsicWhen }} IHostPrimitive
|
|
40
12
|
*/
|
|
41
13
|
|
|
42
|
-
// `
|
|
43
|
-
//
|
|
44
|
-
// (`core/engine/cpp/tests/js/id-alias-coverage.itest.ts`), which reaches every node rather than
|
|
45
|
-
// every REGISTERED one, and resolves precedence the way upstream does instead of by write order.
|
|
14
|
+
// `id` -> `nativeID` aliasing is NOT here: it is `routeProp`'s, for every node rather than only
|
|
15
|
+
// a registered one (`core/engine/cpp/tests/js/id-alias-coverage.itest.ts`).
|
|
46
16
|
|
|
47
17
|
/** @type {Record<string, IHostPrimitive>} */
|
|
48
18
|
const HOST_PRIMITIVES = {
|
|
49
19
|
View: {
|
|
50
20
|
intrinsic: 'view',
|
|
51
21
|
},
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
// — the tag exists only so the host-behavior registry, which is keyed by TAG and never by
|
|
55
|
-
// resolved name, can find the press machine.
|
|
56
|
-
//
|
|
57
|
-
// No defaults: the tag forwards its props untouched, and the machine reads them off `node.props`
|
|
58
|
-
// at event time.
|
|
22
|
+
// Same `RCTView` as `view` — the tag exists only so the host-behavior registry (keyed by TAG)
|
|
23
|
+
// can find the press machine.
|
|
59
24
|
Pressable: {
|
|
60
25
|
intrinsic: 'pressable',
|
|
61
|
-
//
|
|
62
|
-
// a tag with no machine — the whole reason this entry landed last.
|
|
26
|
+
// Needed for a render-prop style/child; without it the machine never attaches.
|
|
63
27
|
observesState: true,
|
|
64
28
|
},
|
|
65
|
-
// The three that were WITHHELD until their wrappers collapsed, landed 2026-09-11 now that none
|
|
66
|
-
// exists on any adapter. The condition the old note stated — "it lands when the wrappers collapse
|
|
67
|
-
// to a forwarder over the tag" — was met by deleting them outright.
|
|
68
|
-
//
|
|
69
|
-
// What the entry buys, since the tag and its behavior already worked without one: a hand-written
|
|
70
|
-
// `<scroll-view>` was resolving as a COMPONENT on Vue, because `adapters/vue/intrinsic-tags.cjs`
|
|
71
|
-
// derives its element set from this table and both Vue compilers read it. Missing here, the tag
|
|
72
|
-
// cost a dev-mode resolve warning per element plus the component codegen path — a slot closure
|
|
73
|
-
// instead of `_createElementBlock`.
|
|
74
29
|
TouchableOpacity: {
|
|
75
30
|
intrinsic: 'touchable-opacity',
|
|
76
31
|
},
|
|
77
32
|
TouchableHighlight: {
|
|
78
33
|
intrinsic: 'touchable-highlight',
|
|
79
34
|
},
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
// one view (`behaviors/scroll-view/shared.ts:131`). So an app may write either spelling and
|
|
83
|
-
// `resolveIntrinsicTag` picks the view, which is also what puts `horizontal-scroll-view` into
|
|
84
|
-
// Vue's element set — a tag apps write directly and which would otherwise resolve as a component.
|
|
35
|
+
// Axis is a separate native ViewManager, not a runtime flag (`<ScrollView horizontal>` in RN
|
|
36
|
+
// terms) — an app may write either tag spelling.
|
|
85
37
|
ScrollView: {
|
|
86
38
|
intrinsic: 'scroll-view',
|
|
87
39
|
intrinsicWhen: {
|
|
@@ -89,188 +41,62 @@ const HOST_PRIMITIVES = {
|
|
|
89
41
|
intrinsic: 'horizontal-scroll-view',
|
|
90
42
|
},
|
|
91
43
|
},
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
// kept because the SEQUENCE is the reusable part — an entry here is a switch for four transforms
|
|
95
|
-
// at once, so it goes in after every side is ready, never to prove the transforms work.
|
|
96
|
-
//
|
|
97
|
-
// `registerTextInputBehavior()` is called by every adapter's `src/register.ts`: the tag is the
|
|
98
|
-
// only path an app has, so the machine has exactly one owner per node.
|
|
44
|
+
// `multiline` picks between two SEPARATE native views, decided at compile time — a wrong
|
|
45
|
+
// view here is uncorrectable by any later prop write.
|
|
99
46
|
TextInput: {
|
|
100
47
|
intrinsic: 'text-input',
|
|
101
|
-
// `multiline` picks between two SEPARATE native views, not one view with a flag, so the tag is
|
|
102
|
-
// decided at compile time and a runtime selector must refuse — a wrong view here is
|
|
103
|
-
// uncorrectable by any later prop write.
|
|
104
48
|
intrinsicWhen: {
|
|
105
49
|
prop: 'multiline',
|
|
106
50
|
intrinsic: 'text-input-multiline',
|
|
107
51
|
},
|
|
108
52
|
},
|
|
109
|
-
// Landed 2026-09-01, same order as TextInput and Image: runtime half built
|
|
110
|
-
// (`core/components/src/behaviors/switch.ts`), registered by every adapter, proven
|
|
111
|
-
// against the wrapper's payload (positive + negative controls, a break-tested async-timing case)
|
|
112
|
-
// before this key existed.
|
|
113
|
-
//
|
|
114
|
-
// The behavior carries a machine: it mirrors the last value native reported and sends a platform
|
|
115
|
-
// snap-back command on disagreement.
|
|
116
|
-
//
|
|
117
|
-
// ITS FOLD IS NOT IDEMPOTENT, and that is safe for a reason Image's entry does not share. Switch
|
|
118
|
-
// maps `trackColor`/`thumbColor`/`ios_backgroundColor` onto native prop names keyed on
|
|
119
|
-
// `Platform.OS`, so running it twice would NOT be a no-op — the question never arises because a
|
|
120
|
-
// payload reaches this fold once, on the one tag that carries the behavior. Unreachable by
|
|
121
|
-
// construction, not idempotent by property.
|
|
122
|
-
//
|
|
123
|
-
// No `observesState`: nothing in Switch's public surface is a function-valued style or a
|
|
124
|
-
// render-prop child (`style?: IStyleProp<IViewStyle>`, never a callback), so neither
|
|
125
|
-
// `stateInTemplate` nor `renderPropChild` applies — unlike Pressable, whose machine is what
|
|
126
|
-
// forced that flag.
|
|
127
53
|
Switch: {
|
|
128
54
|
intrinsic: 'switch',
|
|
129
55
|
},
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
// and differs per platform (iOS a sibling before the content view, Android the scroll view's
|
|
133
|
-
// PARENT), and a per-node behavior cannot own a decision another component makes.
|
|
134
|
-
//
|
|
135
|
-
// That is settled and it was settled elsewhere: the ScrollView states the placement as DATA
|
|
136
|
-
// (`claimedChildren: { [REFRESH_CONTROL]: platform.claimMode }`, `behaviors/scroll-view/shared.ts`)
|
|
137
|
-
// and the ENGINE moves the node in `appendChild`. So the claim needs nothing from this primitive's
|
|
138
|
-
// own behavior — verified on a bare node with no wrapper anywhere, both platforms, in
|
|
139
|
-
// `behaviors/refresh-control.test.ts`.
|
|
140
|
-
//
|
|
141
|
-
// What the behavior owes is therefore only the CONTROLLED HANDSHAKE, and no fold at all: four of
|
|
142
|
-
// the five wrappers folded exactly `resolveAccessibilityProps`, which the engine already runs at
|
|
143
|
-
// `fabricProps` on every path — the same reason SafeAreaView has no behavior file.
|
|
144
|
-
//
|
|
145
|
-
// `id` is declared on all five wrappers, which it was not until 2026-09-01: upstream's
|
|
146
|
-
// RefreshControl spreads `...ViewProps` (RefreshControl.js:70), so its absence was a parity gap
|
|
147
|
-
// rather than a decision. The rename itself is the engine's.
|
|
56
|
+
// Placement is decided by the owning ScrollView (`claimedChildren`,
|
|
57
|
+
// `behaviors/scroll-view/shared.ts`), not by this primitive — no behavior file needed.
|
|
148
58
|
RefreshControl: {
|
|
149
59
|
intrinsic: 'refresh-control',
|
|
150
|
-
// None. RN seeds nothing: `refreshing` is required, and every other prop is per-platform
|
|
151
|
-
// styling the native view defaults itself.
|
|
152
60
|
},
|
|
153
|
-
// `defaults
|
|
154
|
-
//
|
|
155
|
-
// eighteen entries had declared `{}` for months.
|
|
156
|
-
//
|
|
157
|
-
// It was the THIRD copy of that one rule. `applyTextDefaults` (`core/engine/src/fabric-props.ts`)
|
|
158
|
-
// and its C++ twin apply both to every node committing as `RCTText`, whoever authored it and
|
|
159
|
-
// whatever tag it carries — strictly WIDER than this table, which reached only a registered
|
|
160
|
-
// primitive. Proven redundant rather than argued: emptying it turned ZERO itests red, and the
|
|
161
|
-
// itests are the side that reads a real committed payload.
|
|
61
|
+
// No `defaults`: RN's Text defaults are `applyTextDefaults` (`core/engine/src/fabric-props.ts`)
|
|
62
|
+
// plus its C++ twin, applied to every `RCTText` regardless of tag.
|
|
162
63
|
Text: {
|
|
163
64
|
intrinsic: 'text',
|
|
164
65
|
},
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
// `mapImageProps` is idempotent, asserted in `behaviors/image.test.ts` and break-tested, so a bag
|
|
169
|
-
// that reaches the fold twice folds to no effect.
|
|
170
|
-
//
|
|
171
|
-
// The runtime half has to land before this key does: without the fold a raw `src` reaches Fabric,
|
|
172
|
-
// a key no ViewConfig declares, which throws nothing and paints nothing.
|
|
173
|
-
// THE ENTRY IS NOT OPTIONAL HERE, and the reason has nothing to do with folds: this table is what
|
|
174
|
-
// `adapters/vue/intrinsic-tags.cjs` derives element-vs-component from, and a hyphenated tag it
|
|
175
|
-
// does not name compiles to `resolveComponent("image-background")` — children become a slot the
|
|
176
|
-
// element path never reads, so the subtree renders BLANK with no error. `image`/`view`/`text` are
|
|
177
|
-
// real SVG element names and survive that gap; this one is not.
|
|
66
|
+
// MUST be registered here: unnamed, `adapters/vue/intrinsic-tags.cjs` compiles this hyphenated
|
|
67
|
+
// tag as a component and its children silently render BLANK.
|
|
178
68
|
ImageBackground: {
|
|
179
69
|
intrinsic: 'image-background',
|
|
180
|
-
// None. The absolute-fill style, the box-dimension proxy and the Image mapping are all derived
|
|
181
|
-
// from live props at commit, which a compile-time seed cannot express.
|
|
182
70
|
},
|
|
183
71
|
Image: {
|
|
184
72
|
intrinsic: 'image',
|
|
185
|
-
// None. Every default RN's Image applies is already inside the shared mapping (the source
|
|
186
|
-
// array shape, the width/height style fold, `alt` -> accessibilityLabel), which the behavior
|
|
187
|
-
// runs at commit — so there is nothing left for a compile-time seed to do.
|
|
188
73
|
},
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
// platform-invariant on purpose; what it does NOT do is repair what sits underneath, where
|
|
192
|
-
// upstream RN renders nothing at all off iOS. That divergence is with the owner as its own
|
|
193
|
-
// decision.
|
|
74
|
+
// Only primitive whose intrinsic resolves to a DIFFERENT Fabric component per platform:
|
|
75
|
+
// `RCTInputAccessoryView` on iOS, plain `RCTView` on Android.
|
|
194
76
|
InputAccessoryView: {
|
|
195
77
|
intrinsic: 'input-accessory-view',
|
|
196
|
-
// None. The mapping has no aliasing and no derived value — every consumed name leaves under the
|
|
197
|
-
// same name — so there is nothing for a compile-time seed to do.
|
|
198
78
|
},
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
// against the wrapper's payload; SafeAreaView has no runtime half to build. All five adapters fold
|
|
202
|
-
// exactly one thing, `resolveAccessibilityProps`, and that fold already runs in the engine at
|
|
203
|
-
// `fabricProps` on both commit paths (the `aria-bag-fold` row). So there is no
|
|
204
|
-
// `behaviors/safe-area-view.ts`, and a reader who assumes one exists will go looking for a file
|
|
205
|
-
// that was never needed.
|
|
206
|
-
//
|
|
207
|
-
// Counted before writing, which is the only thing standing behind that claim: five
|
|
208
|
-
// implementations, zero shared, none synthesizing a node — each renders ONE
|
|
209
|
-
// `safe-area-view` with children on its framework's own channel (React's third argument,
|
|
210
|
-
// a Vue slot, a Solid JSX child, Angular's `<ng-content>`, a Svelte snippet). That clears the
|
|
211
|
-
// disqualifier in `.claude/rules/host-primitive-tier.md`.
|
|
79
|
+
// No `behaviors/safe-area-view.ts`: its one fold (`resolveAccessibilityProps`) already runs
|
|
80
|
+
// in the engine at `fabricProps` on every commit path.
|
|
212
81
|
SafeAreaView: {
|
|
213
82
|
intrinsic: 'safe-area-view',
|
|
214
83
|
},
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
// child instead — RN's own shape (TouchableNativeFeedback.js:289,339). Entered in the same commit
|
|
218
|
-
// that deletes the five wrappers, because the registry is keyed by TAG: a wrapper still emitting
|
|
219
|
-
// its own `pressable` while the behavior is registered would put two press machines on one tree.
|
|
220
|
-
//
|
|
221
|
-
// No `defaults`: RN's TNF seeds nothing at all — every value it derives (`accessible`,
|
|
222
|
-
// `focusable`, `accessibilityState`, the ripple background) depends on ANOTHER prop or on a
|
|
223
|
-
// listener, which is a fold and not a default.
|
|
84
|
+
// Commits NO node of its own: the intrinsic resolves to the engine's anchor, and the behavior
|
|
85
|
+
// clones the owner's props onto the single child instead (RN's own shape).
|
|
224
86
|
TouchableNativeFeedback: {
|
|
225
87
|
intrinsic: 'touchable-native-feedback',
|
|
226
88
|
},
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
// copied only when SET (:281), there is no ripple, and `onBlur`/`onFocus` are cloned where TNF
|
|
230
|
-
// drops them — read `src/behaviors/touchable-without-feedback.ts`'s header rather than inheriting
|
|
231
|
-
// the neighbour's fold.
|
|
232
|
-
//
|
|
233
|
-
// Upstream's passthrough loop lets an explicit `nativeID` win over `id` here (:280-284, unlike
|
|
234
|
-
// TNF's :373); NOT reproduced — the engine resolves that precedence once, on the way in, and a
|
|
235
|
-
// per-primitive exception to it would be invisible from anywhere the app can see. No `defaults`:
|
|
236
|
-
// every value TWF derives depends on another prop or on a listener, which is a fold and not a
|
|
237
|
-
// default.
|
|
89
|
+
// Same anchor shape as TouchableNativeFeedback, a DIFFERENT clone list — see
|
|
90
|
+
// `behaviors/touchable-without-feedback.ts`'s header rather than assuming TNF's.
|
|
238
91
|
TouchableWithoutFeedback: {
|
|
239
92
|
intrinsic: 'touchable-without-feedback',
|
|
240
93
|
},
|
|
241
|
-
//
|
|
242
|
-
// string prop (Button.js:363-388) — so the behavior owns the whole subtree and the tag is the
|
|
243
|
-
// only spelling. Entered in the same commit that deletes the five wrappers: the registry is keyed
|
|
244
|
-
// by TAG, and a wrapper still building its own View/Text under a registered `button` would give
|
|
245
|
-
// every existing Button a second copy of the subtree.
|
|
246
|
-
//
|
|
247
|
-
// `id` used to be PLATFORM-DEPENDENT here, and the record is worth keeping because it is what a
|
|
248
|
-
// per-primitive rename costs. Button's touchable is swapped by platform (Button.js:281-284), only
|
|
249
|
-
// the iOS arm renamed `id` itself, and with no entry in this table the committed payload read:
|
|
250
|
-
//
|
|
251
|
-
// iOS nativeID: 'from-id' id: absent the touchable-opacity fold
|
|
252
|
-
// Android nativeID: undefined id: 'from-id' a key no ViewConfig declares -> dropped
|
|
253
|
-
//
|
|
254
|
-
// A rename that runs once, for every node, on the way in cannot produce that shape at all.
|
|
255
|
-
//
|
|
256
|
-
// No `defaults`: every value RN's Button seeds is derived from another prop or from a listener
|
|
257
|
-
// (`accessible`, `focusable`, the greyed label, the uppercased title), which is a fold, not a
|
|
258
|
-
// default.
|
|
94
|
+
// Owns its whole subtree (View wrapping Text); RN's Button takes no children, only `title`.
|
|
259
95
|
Button: {
|
|
260
96
|
intrinsic: 'button',
|
|
261
97
|
},
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
// commit that deletes the five wrappers: the registry is keyed by TAG, and a wrapper still
|
|
265
|
-
// painting its own spinner while the behavior is registered would give every indicator two.
|
|
266
|
-
//
|
|
267
|
-
// `id` renames on the OWNER, before `slotPropsExcept` routes the survivor down — so the key that
|
|
268
|
-
// reaches the spinner is `nativeID`, which is where RN's `...restProps` puts it too
|
|
269
|
-
// (ActivityIndicator.js:99).
|
|
270
|
-
//
|
|
271
|
-
// No `defaults`: `animating` and `hidesWhenStopped` ARE `notFalse` folds, but they belong to the
|
|
272
|
-
// SPINNER, and this table's ops are applied to the tag's own bag before any slot routing. They
|
|
273
|
-
// live in the behavior's spinner fold instead, which is the only layer that can see that node.
|
|
98
|
+
// This tag IS RN's centering wrapper View (ActivityIndicator.js:112); the behavior builds
|
|
99
|
+
// `activity-indicator-spinner` under it.
|
|
274
100
|
ActivityIndicator: {
|
|
275
101
|
intrinsic: 'activity-indicator',
|
|
276
102
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/components",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.2",
|
|
4
4
|
"description": "Framework-agnostic component logic (state machines + render functions) for SymbioteNative — written once, inherited by every adapter (React, Vue, Angular, ...).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -59,11 +59,11 @@
|
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"react-native": ">=0.86",
|
|
62
|
-
"@symbiote-native/engine": "^1.
|
|
62
|
+
"@symbiote-native/engine": "^1.3.1"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@types/node": "^26.0.0",
|
|
66
|
-
"@symbiote-native/engine": "1.
|
|
66
|
+
"@symbiote-native/engine": "1.3.1"
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
69
|
"typecheck": "tsc --build",
|