@symbiote-native/components 2.0.0 → 3.0.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.
- package/README.md +8 -9
- package/build/accessibility-props.d.ts +1 -1
- package/build/accessibility-props.js +2 -2
- package/build/behaviors/activity-indicator/shared.js +41 -70
- package/build/behaviors/button.d.ts +11 -0
- package/build/behaviors/button.js +101 -89
- package/build/behaviors/image-background.d.ts +1 -0
- package/build/behaviors/image-background.js +97 -81
- package/build/behaviors/image.d.ts +0 -1
- package/build/behaviors/image.js +24 -105
- package/build/behaviors/input-accessory-view.d.ts +0 -1
- package/build/behaviors/input-accessory-view.js +47 -53
- package/build/behaviors/pressable.d.ts +1 -1
- package/build/behaviors/pressable.js +68 -97
- package/build/behaviors/refresh-control.js +15 -2
- package/build/behaviors/scroll-view/index.android.js +25 -37
- package/build/behaviors/scroll-view/index.d.ts +1 -0
- package/build/behaviors/scroll-view/index.js +3 -0
- package/build/behaviors/scroll-view/responder.d.ts +4 -0
- package/build/behaviors/scroll-view/responder.js +202 -0
- package/build/behaviors/scroll-view/shared.d.ts +1 -3
- package/build/behaviors/scroll-view/shared.js +95 -95
- package/build/behaviors/scroll-view/sticky.d.ts +1 -0
- package/build/behaviors/scroll-view/sticky.js +62 -49
- package/build/behaviors/switch.js +43 -86
- package/build/behaviors/text-input.js +219 -107
- package/build/behaviors/touchable-highlight.js +74 -61
- package/build/behaviors/touchable-native-feedback.js +43 -122
- package/build/behaviors/touchable-opacity.js +71 -59
- package/build/behaviors/touchable-without-feedback.js +35 -100
- package/build/component-names/index.android.js +7 -11
- package/build/component-names/index.ios.js +0 -7
- package/build/component-names/shared.d.ts +1 -1
- package/build/index.d.ts +15 -21
- package/build/index.js +21 -28
- package/build/resolve-intrinsic.js +3 -9
- package/build/scroll-view-commands.d.ts +1 -9
- package/build/scroll-view-commands.js +13 -74
- package/build/state/flat-list.d.ts +2 -2
- package/build/state/flat-list.js +10 -2
- package/build/state/pressable.d.ts +6 -1
- package/build/state/pressable.js +63 -28
- package/build/state/section-list.d.ts +2 -0
- package/build/state/section-list.js +14 -7
- package/build/state/text-input.d.ts +7 -40
- package/build/state/text-input.js +17 -186
- package/build/state/touchable.d.ts +1 -0
- package/build/state/touchable.js +11 -8
- package/build/state/virtualized-list-reducer.d.ts +2 -2
- package/build/state/virtualized-list.d.ts +6 -6
- package/build/state/virtualized-list.js +71 -37
- package/build/text-props.d.ts +0 -8
- package/build/text-props.js +14 -25
- package/build/view/render-button.d.ts +1 -29
- package/build/view/render-button.js +44 -81
- package/build/view/render-image/index.d.ts +14 -1
- package/build/view/render-image/index.js +22 -147
- package/build/view/render-input-accessory-view.d.ts +1 -5
- package/build/view/render-input-accessory-view.js +26 -48
- package/build/view/render-keyboard-avoiding-view.d.ts +7 -1
- package/build/view/render-keyboard-avoiding-view.js +40 -1
- package/build/view/render-modal.d.ts +1 -1
- package/build/view/render-modal.js +15 -5
- package/build/view/render-pressable/index.d.ts +1 -0
- package/build/view/render-pressable/index.js +4 -0
- package/build/view/render-scroll-view.d.ts +0 -4
- package/build/view/render-scroll-view.js +3 -49
- package/build/view/render-switch.d.ts +0 -14
- package/build/view/render-switch.js +4 -42
- package/build/view/render-touchable-highlight.d.ts +1 -0
- package/build/view/render-touchable-native-feedback.d.ts +0 -1
- package/build/view/render-touchable-native-feedback.js +15 -9
- package/host-primitives.cjs +49 -203
- package/host-primitives.d.cts +0 -1
- package/package.json +3 -7
- package/build/fold-host-bag.d.ts +0 -15
- package/build/fold-host-bag.js +0 -99
- package/build/view/render-text-input.d.ts +0 -11
- package/build/view/render-text-input.js +0 -39
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
//
|
|
4
4
|
// THE TWO-NODE SHAPE IS RN'S. `ImageBackground.js:74-103` opens a `<View>` carrying the app's
|
|
5
5
|
// `style`, puts an absolutely-filled `<Image>` inside it, and lays the app's `{children}` AFTER
|
|
6
|
-
// that image so they paint on top. The
|
|
7
|
-
//
|
|
6
|
+
// that image so they paint on top. The tag commits the same two nodes — `image-background` (an
|
|
7
|
+
// RCTView, the tag an app writes) with an RCTImageView built under it.
|
|
8
8
|
//
|
|
9
9
|
// WHY THE SLOT TAKES NO CHILDREN, which is the one thing this primitive needed that ScrollView,
|
|
10
10
|
// ActivityIndicator and Button did not. `childHost` answers two questions at once — which node an
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
// importantForAccessibility, ...props` and spreads `...props` onto the Image
|
|
19
19
|
// (`ImageBackground.js:62-81`), so the set that moves is OPEN — every event, every accessibility
|
|
20
20
|
// prop, `testID`, `id`, whatever an app writes next — and only a complement can express it.
|
|
21
|
-
// `IMAGE_BACKGROUND_HOST_PROPS` is
|
|
22
|
-
//
|
|
23
|
-
// the
|
|
24
|
-
//
|
|
21
|
+
// `IMAGE_BACKGROUND_HOST_PROPS` is that complement, and `importantForAccessibility` is IN it despite
|
|
22
|
+
// never being part of the spread: RN reapplies it explicitly to both nodes (`:76,82`), so it stays on
|
|
23
|
+
// the owner here too and the image gets its own copy derived from `ownerProps` (FIXED 2026-09-20 —
|
|
24
|
+
// it used to redirect to the image alone and never reach the owner at all, which the module's own
|
|
25
|
+
// comment rationalized as "the divergence from RN predates all of it").
|
|
25
26
|
//
|
|
26
27
|
// WHAT THE IMAGE'S FOLD OWES. Everything on the image arrives as a real prop write, so its payload
|
|
27
28
|
// is built by the shared `foldImagePayload` like any other `image`. Two things cannot arrive that
|
|
@@ -29,95 +30,84 @@
|
|
|
29
30
|
// wrapper's width/height onto the image so it fills the box rather than collapsing to the source's
|
|
30
31
|
// intrinsic size), and `id`, whose rename to `nativeID` is applied per adapter on the tag THEY
|
|
31
32
|
// create and so never reaches a node a behavior built.
|
|
32
|
-
import { appendChild, createElement,
|
|
33
|
+
import { appendChild, createElement, registerHostBehavior, } from '@symbiote-native/engine';
|
|
33
34
|
import { descriptorFor } from '../component-names';
|
|
34
|
-
import {
|
|
35
|
+
import { IMAGE_TAG, registerImageBehavior } from './image.js';
|
|
35
36
|
export const IMAGE_BACKGROUND_TAG = 'image-background';
|
|
36
|
-
// The inner
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
left: 0,
|
|
40
|
-
right: 0,
|
|
41
|
-
top: 0,
|
|
42
|
-
bottom: 0,
|
|
43
|
-
};
|
|
37
|
+
// The inner image's own tag. Distinct from `image` because the absolute fill must NOT reach a bare
|
|
38
|
+
// `<image>`, and a tag is the only thing a per-node rule can branch on.
|
|
39
|
+
export const IMAGE_BACKGROUND_IMAGE_TAG = 'image-background-image';
|
|
44
40
|
// The props RN keeps on the wrapper View (`ImageBackground.js:74-78`), plus the two spellings of a
|
|
45
41
|
// class name — `routeProp`'s slot redirect runs above its own class branch, so an unlisted `class`
|
|
46
|
-
// would style the image instead of the box.
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
// would style the image instead of the box. `importantForAccessibility` stays too: RN destructures
|
|
43
|
+
// it out of `...props` and reapplies it explicitly to BOTH the wrapper (:76) and the image (:82), so
|
|
44
|
+
// it is never part of the spread — the engine derives the image's own copy from this node
|
|
45
|
+
// (`foldImageBackgroundImageProps` in `SymbioteFabricProps.cpp`), the same seam the box proxy uses.
|
|
46
|
+
const IMAGE_BACKGROUND_HOST_PROPS = [
|
|
47
|
+
'style',
|
|
48
|
+
'class',
|
|
49
|
+
'className',
|
|
50
|
+
'importantForAccessibility',
|
|
51
|
+
];
|
|
52
|
+
// The owner props the image's payload is derived from. `importantForAccessibility` joins `style`
|
|
53
|
+
// for the reason above: a late write to either must re-fold the image, not just the box.
|
|
54
|
+
const IMAGE_BACKGROUND_SLOT_DERIVED = ['style', 'importantForAccessibility'];
|
|
52
55
|
// `imageStyle` is the wrapper's own name for the image's `style`. A bare class NAME is a legal
|
|
53
56
|
// value for it — every adapter's wrapper resolved one — and `routeProp` routes a string landing on
|
|
54
57
|
// `style` as `class` instead, so the registry resolves it on the image with nothing needed here.
|
|
55
58
|
const IMAGE_BACKGROUND_SLOT_PROPS = { imageStyle: 'style' };
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return value;
|
|
78
|
-
return undefined;
|
|
79
|
-
}
|
|
80
|
-
function imageFold(owner) {
|
|
81
|
-
return props => {
|
|
82
|
-
// RN's own workaround, and its comment is worth reading before "simplifying" this
|
|
83
|
-
// (`ImageBackground.js:86-96`): an RN Image overwrites its own width/height from the source's
|
|
84
|
-
// intrinsic size, which fights the box's explicit dimensions, so they are proxied back on.
|
|
85
|
-
// Reads the OWNER's live style — a class name lands there too, published by `pushClassStyle`.
|
|
86
|
-
const box = flattenStyle(styleOf(owner.props.style));
|
|
87
|
-
const next = {
|
|
88
|
-
...props,
|
|
89
|
-
// `imageStyle` last, so a caller still wins over the fill and the proxy.
|
|
90
|
-
style: [
|
|
91
|
-
ABSOLUTE_FILL,
|
|
92
|
-
{
|
|
93
|
-
width: readDimension(box, 'width'),
|
|
94
|
-
height: readDimension(box, 'height'),
|
|
95
|
-
},
|
|
96
|
-
styleOf(props.style),
|
|
97
|
-
],
|
|
98
|
-
};
|
|
99
|
-
// Unconditional priority when both are set, matching RN (`View.js:77-79`) and `foldHostBag`.
|
|
100
|
-
// A raw `id` is a key no ViewConfig declares, so Fabric drops it and the nativeID is lost.
|
|
101
|
-
if (Object.hasOwn(next, 'id')) {
|
|
102
|
-
next.nativeID = next.id;
|
|
103
|
-
delete next.id;
|
|
104
|
-
}
|
|
105
|
-
return foldImagePayload(next);
|
|
106
|
-
};
|
|
107
|
-
}
|
|
59
|
+
// The owner's fold is GONE (2026-09-18), not moved into this file under another name: its whole
|
|
60
|
+
// content was `accessibilityIgnoresInvertColors: true`, a function of the tag and nothing else, and
|
|
61
|
+
// it is `foldImageBackgroundProps` in `SymbioteFabricProps.cpp` now. The owner therefore pays no
|
|
62
|
+
// trip into JS at all, and a committed ImageBackground is down from two crossings to one — the
|
|
63
|
+
// remaining one is the inner image's, below, which derives its style from THIS node and so cannot be
|
|
64
|
+
// a per-node rule. Contract: `core/engine/cpp/tests/js/image-background-payload.itest.ts`.
|
|
65
|
+
// THE IMAGE'S FOLD IS GONE TOO (2026-09-18) — `foldImageBackgroundImageProps` in
|
|
66
|
+
// `SymbioteFabricProps.cpp`, and with it this primitive costs ZERO trips into JS on both nodes.
|
|
67
|
+
//
|
|
68
|
+
// It outlived every other fold in this file because both of its inputs live on the node ABOVE: the
|
|
69
|
+
// app writes `style` on the `<image-background>` and `IMAGE_BACKGROUND_HOST_PROPS` keeps it there.
|
|
70
|
+
// "A per-node rule cannot reach another node" is what this file used to say, and it was a fact about
|
|
71
|
+
// the JS FOLD rather than the engine — the tree is in C++, so `fabricProps` takes `ownerProps` from
|
|
72
|
+
// `node.parent` and the proxy reads it there.
|
|
73
|
+
//
|
|
74
|
+
// The `id -> nativeID` half went earlier still and was DEAD before this port: `foldIdAlias` applies
|
|
75
|
+
// to every tagged node and runs ahead of any fold, so by the time this ran the key was already
|
|
76
|
+
// renamed. Worth naming, because a fold that still spells a rule someone else now applies reads as
|
|
77
|
+
// load-bearing and is not.
|
|
78
|
+
//
|
|
79
|
+
// Contract: `core/engine/cpp/tests/js/image-background-image-payload.itest.ts`.
|
|
108
80
|
// Returns the image, so `slotProps` / `slotPropsExcept` / `slotDerived` all point at it — and the
|
|
109
81
|
// app's children stay on the owner because of `slotTakesNoChildren`, not because of what this
|
|
110
82
|
// returns. The image is appended FIRST and nothing else is ever placed in front of it, which is
|
|
111
83
|
// what makes the children paint over it.
|
|
112
84
|
//
|
|
113
|
-
// Built
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
85
|
+
// Built WITH `IMAGE_TAG` since 2026-09-18, which is the reverse of what it used to do and for a
|
|
86
|
+
// reason that reversed with it. It used to withhold the tag so the node would not get Image's
|
|
87
|
+
// `payloadFold` — a single slot this primitive needed for its own derived style — and call the
|
|
88
|
+
// shared mapping by hand at the end. The mapping is the ENGINE's now, reached off the tag, so the
|
|
89
|
+
// tag is how this node gets the platform half at all; the JS slot is free for the composition.
|
|
90
|
+
//
|
|
91
|
+
// ONE ORDERING DIFFERENCE FALLS OUT, and it is deliberate rather than overlooked. The image rule
|
|
92
|
+
// folds the image's own `width`/`height` PROPS under its style, and the background rule then layers
|
|
93
|
+
// the box's dimensions over that — where RN nests it the other way (`ImageBackground.js:83-98` puts
|
|
94
|
+
// the props under the proxied box size). So when an app sets BOTH a `width` prop on the
|
|
95
|
+
// ImageBackground and a conflicting width in its `style`, RN gives the style's and we give the
|
|
96
|
+
// prop's.
|
|
97
|
+
//
|
|
98
|
+
// It is left this way rather than reproduced: RN's own comment calls that nesting a "Temporary
|
|
99
|
+
// Workaround" for an Image that overwrites its own dimensions, and an explicit prop winning over an
|
|
100
|
+
// inherited box is the less surprising of the two. Pinned in
|
|
101
|
+
// `core/components/src/behaviors/image-background.test.ts` so it stays a decision.
|
|
102
|
+
//
|
|
103
|
+
// THE TAG IS ITS OWN, and that is what the C++ port needed. The node used to carry plain `image`,
|
|
104
|
+
// which is right for everything the ordinary image rule does and wrong for the fill — a bare
|
|
105
|
+
// `<image>` must NOT be absolutely positioned. `image-background-image` is served by BOTH rules, the
|
|
106
|
+
// ordinary one first, the same shape `usesPressableRule` gives `button` and the touchables. The
|
|
107
|
+
// COMPONENT still resolves through `IMAGE_TAG`, because the native view is an ordinary RCTImageView.
|
|
117
108
|
function buildBackgroundImage(node) {
|
|
118
109
|
const descriptor = descriptorFor(IMAGE_TAG);
|
|
119
|
-
const image = createElement(descriptor.component, descriptor.isText);
|
|
120
|
-
image.payloadFold = imageFold(node);
|
|
110
|
+
const image = createElement(descriptor.component, descriptor.isText, IMAGE_BACKGROUND_IMAGE_TAG);
|
|
121
111
|
appendChild(node, image);
|
|
122
112
|
return image;
|
|
123
113
|
}
|
|
@@ -127,13 +117,39 @@ const imageBackgroundBehavior = {
|
|
|
127
117
|
slotDerived: IMAGE_BACKGROUND_SLOT_DERIVED,
|
|
128
118
|
slotTakesNoChildren: true,
|
|
129
119
|
buildStructure: buildBackgroundImage,
|
|
130
|
-
foldPayload: hostFold,
|
|
131
120
|
// Required by the interface and deliberately empty: this primitive owns no timer, no listener
|
|
132
121
|
// and no native handshake. Written out rather than shared with a `noop` so the emptiness reads
|
|
133
122
|
// as a decision.
|
|
134
123
|
attach() { },
|
|
135
124
|
detach() { },
|
|
136
125
|
};
|
|
126
|
+
// The inner image's registration, and it is NOT a formality twice over.
|
|
127
|
+
//
|
|
128
|
+
// A tag reaches C++ only through `recordSetTag`, which `attachHostBehavior` emits — so a tag nobody
|
|
129
|
+
// registered carries an EMPTY `tagName` in the host and no rule fires for it, however the rule is
|
|
130
|
+
// written. Same reason the ActivityIndicator spinner and the scroll content nodes carry one.
|
|
131
|
+
//
|
|
132
|
+
// And `resolvesImageSources` has to be repeated here rather than inherited from `image`: the lookup
|
|
133
|
+
// is by TAG, this node's tag is its own, and the flag is what makes `routeProp` run
|
|
134
|
+
// `require('./photo.png')` through Metro's asset registry on the way in. Without it an
|
|
135
|
+
// ImageBackground would commit the raw asset NUMBER and paint nothing — headless included, since
|
|
136
|
+
// the resolution is JS-side by necessity.
|
|
137
|
+
const backgroundImageBehavior = {
|
|
138
|
+
resolvesImageSources: true,
|
|
139
|
+
attach() { },
|
|
140
|
+
detach() { },
|
|
141
|
+
};
|
|
137
142
|
export function registerImageBackgroundBehavior() {
|
|
143
|
+
// A REAL DEPENDENCY, declared rather than assumed. The inner node is a TAG now, so it gets its
|
|
144
|
+
// platform half — the engine's two rules, and the write-time source resolution — only through
|
|
145
|
+
// these registrations. It used to need nothing, because the fold was a function this file called
|
|
146
|
+
// directly.
|
|
147
|
+
//
|
|
148
|
+
// `register.ts` registers everything anyway, so nothing in an app depended on this; what depended
|
|
149
|
+
// on it was every test that registers one behavior and not the whole set, and a silent inner
|
|
150
|
+
// image with no rule is precisely the failure that would reach a device before it reached a
|
|
151
|
+
// suite. Idempotent, like every `register*` here.
|
|
152
|
+
registerImageBehavior();
|
|
153
|
+
registerHostBehavior(IMAGE_BACKGROUND_IMAGE_TAG, backgroundImageBehavior);
|
|
138
154
|
registerHostBehavior(IMAGE_BACKGROUND_TAG, imageBackgroundBehavior);
|
|
139
155
|
}
|
package/build/behaviors/image.js
CHANGED
|
@@ -1,111 +1,30 @@
|
|
|
1
|
-
// Image's host behavior, and it carries
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Image's host behavior, and it carries no runtime at all — no listeners, no timers, no commit
|
|
2
|
+
// hook, and since 2026-09-18 no fold either. What is left is ONE declaration: that this tag's three
|
|
3
|
+
// source props are resolved on the way in.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
5
|
+
// WHERE THE FOLD WENT: `foldImageProps` in `SymbioteFabricProps.cpp`, with
|
|
6
|
+
// `core/engine/cpp/tests/js/image-payload.itest.ts` as its contract. The `srcSet`/`src`/`source`
|
|
7
|
+
// precedence, the W3C header decoration, the `width`/`height` fold into style, `alt` becoming
|
|
8
|
+
// `accessibilityLabel` + `accessible`, `resizeMode`/`tintColor` falling back to style keys, and
|
|
9
|
+
// `loadingIndicatorSource` being plucked down to a bare uri — all of it is a function of the tag,
|
|
10
|
+
// which is what makes it the platform's.
|
|
11
11
|
//
|
|
12
|
-
// WHY THIS
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
12
|
+
// WHY THIS ONE DID NOT MOVE WHOLE, and it is the first that did not. `resolveAssetSource` turns the
|
|
13
|
+
// number `require('./logo.png')` returns into a `{uri, width, height, scale}` by asking METRO'S
|
|
14
|
+
// ASSET REGISTRY — a JS module populated at bundle time. There is no such table in C++ and there
|
|
15
|
+
// should not be: it belongs to the bundler, not to the platform.
|
|
16
|
+
//
|
|
17
|
+
// So the lookup moved EARLIER rather than across. `resolvesImageSources` tells `routeProp` to run
|
|
18
|
+
// the three source props through the resolver on the way IN, exactly as it already resolves
|
|
19
|
+
// `boxShadow`/`filter`/`transform` (`structured-style.ts`), and for the identical reason: a value
|
|
20
|
+
// resolved at payload-build time is resolved HEADLESS ONLY, because the C++ builder has no JS to
|
|
21
|
+
// call, and the device then commits the raw input for Fabric to drop in silence.
|
|
22
|
+
//
|
|
23
|
+
// The idempotence this file used to pin is GONE as a question rather than as a property. It
|
|
24
|
+
// mattered because `renderImage` folded too and a re-render could hand the fold its own output;
|
|
25
|
+
// there is one implementation now, at one point in the commit, so a bag cannot be folded twice.
|
|
21
26
|
import { registerHostBehavior, } from '@symbiote-native/engine';
|
|
22
|
-
import { IMAGE_VIEW_PROP_NAMES, mapImageProps, } from '../view/render-image/index.js';
|
|
23
27
|
export const IMAGE_TAG = 'image';
|
|
24
|
-
const RESIZE_MODES = new Set([
|
|
25
|
-
'cover',
|
|
26
|
-
'contain',
|
|
27
|
-
'stretch',
|
|
28
|
-
'repeat',
|
|
29
|
-
'center',
|
|
30
|
-
]);
|
|
31
|
-
const CROSS_ORIGINS = new Set([
|
|
32
|
-
'anonymous',
|
|
33
|
-
'use-credentials',
|
|
34
|
-
]);
|
|
35
|
-
const CONSUMED = new Set(IMAGE_VIEW_PROP_NAMES);
|
|
36
|
-
function stringOf(value) {
|
|
37
|
-
return typeof value === 'string' ? value : undefined;
|
|
38
|
-
}
|
|
39
|
-
function numberOf(value) {
|
|
40
|
-
return typeof value === 'number' ? value : undefined;
|
|
41
|
-
}
|
|
42
|
-
function resizeModeOf(value) {
|
|
43
|
-
if (typeof value !== 'string' || !RESIZE_MODES.has(value))
|
|
44
|
-
return undefined;
|
|
45
|
-
// Narrowed by the set membership above, then re-stated as the union through a switch rather than
|
|
46
|
-
// a cast: the set and the type are two declarations of one list, and only this makes them agree.
|
|
47
|
-
switch (value) {
|
|
48
|
-
case 'cover':
|
|
49
|
-
case 'contain':
|
|
50
|
-
case 'stretch':
|
|
51
|
-
case 'repeat':
|
|
52
|
-
case 'center':
|
|
53
|
-
return value;
|
|
54
|
-
default:
|
|
55
|
-
return undefined;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function crossOriginOf(value) {
|
|
59
|
-
if (typeof value !== 'string' || !CROSS_ORIGINS.has(value))
|
|
60
|
-
return undefined;
|
|
61
|
-
return value === 'anonymous' ? 'anonymous' : 'use-credentials';
|
|
62
|
-
}
|
|
63
|
-
// A source is an asset id (number), a `{uri}` object, or an array of those. Anything else cannot be
|
|
64
|
-
// resolved and is dropped rather than forwarded — a malformed source reaching Fabric paints nothing
|
|
65
|
-
// and reports nothing, which is worse than an image that is simply absent.
|
|
66
|
-
function sourceOf(value) {
|
|
67
|
-
if (typeof value === 'number')
|
|
68
|
-
return value;
|
|
69
|
-
if (typeof value !== 'object' || value === null)
|
|
70
|
-
return undefined;
|
|
71
|
-
if (Array.isArray(value))
|
|
72
|
-
return value;
|
|
73
|
-
if (typeof Reflect.get(value, 'uri') === 'string')
|
|
74
|
-
return { ...value };
|
|
75
|
-
return undefined;
|
|
76
|
-
}
|
|
77
|
-
// A StyleProp is an object, an array of them, or a registered class array — all of which
|
|
78
|
-
// `flattenStyle` already handles. The only thing to exclude is a scalar.
|
|
79
|
-
function styleOf(value) {
|
|
80
|
-
if (typeof value !== 'object' || value === null)
|
|
81
|
-
return undefined;
|
|
82
|
-
if (Array.isArray(value))
|
|
83
|
-
return value;
|
|
84
|
-
return { ...value };
|
|
85
|
-
}
|
|
86
|
-
export function foldImagePayload(props) {
|
|
87
|
-
const passthrough = {};
|
|
88
|
-
for (const key of Object.keys(props)) {
|
|
89
|
-
if (!CONSUMED.has(key))
|
|
90
|
-
passthrough[key] = props[key];
|
|
91
|
-
}
|
|
92
|
-
return mapImageProps({
|
|
93
|
-
source: sourceOf(props.source),
|
|
94
|
-
defaultSource: sourceOf(props.defaultSource),
|
|
95
|
-
loadingIndicatorSource: sourceOf(props.loadingIndicatorSource),
|
|
96
|
-
style: styleOf(props.style),
|
|
97
|
-
resizeMode: resizeModeOf(props.resizeMode),
|
|
98
|
-
tintColor: stringOf(props.tintColor),
|
|
99
|
-
src: stringOf(props.src),
|
|
100
|
-
srcSet: stringOf(props.srcSet),
|
|
101
|
-
alt: stringOf(props.alt),
|
|
102
|
-
width: numberOf(props.width),
|
|
103
|
-
height: numberOf(props.height),
|
|
104
|
-
crossOrigin: crossOriginOf(props.crossOrigin),
|
|
105
|
-
referrerPolicy: stringOf(props.referrerPolicy),
|
|
106
|
-
passthrough,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
28
|
// Required by IHostBehavior and deliberately empty: Image owns no per-node runtime. Written out
|
|
110
29
|
// rather than shared with a `noop` helper so the emptiness reads as a decision.
|
|
111
30
|
function attach(_node) {
|
|
@@ -118,6 +37,6 @@ export function registerImageBehavior() {
|
|
|
118
37
|
registerHostBehavior(IMAGE_TAG, {
|
|
119
38
|
attach,
|
|
120
39
|
detach,
|
|
121
|
-
|
|
40
|
+
resolvesImageSources: true,
|
|
122
41
|
});
|
|
123
42
|
}
|
|
@@ -1,58 +1,56 @@
|
|
|
1
|
-
// InputAccessoryView's host behavior
|
|
2
|
-
//
|
|
1
|
+
// InputAccessoryView's host behavior. This file itself still owns no per-node runtime — the
|
|
2
|
+
// primitive's real rule lives in `foldInputAccessoryViewProps` (`SymbioteFabricProps.cpp`) now.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// `
|
|
10
|
-
// one implementation to extract, not three to collapse.
|
|
4
|
+
// THE OLD FOLD WAS GONE, not moved (2026-09-18): read end to end it split the bag into
|
|
5
|
+
// consumed/passthrough and reassembled it — there was no aliasing at all, every consumed name left
|
|
6
|
+
// under the same name, and the one thing it actually changed was dropping a `nativeID` or
|
|
7
|
+
// `backgroundColor` that was not a `string`. That is defensive narrowing of a typed view object,
|
|
8
|
+
// not a rule native cares about — and it was WRONG on `backgroundColor`, whose validAttributes
|
|
9
|
+
// entry carries `processColor` and therefore accepts a number.
|
|
11
10
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
11
|
+
// A REAL RULE WAS FOUND WHILE READING VENDOR FOR THE PORT (2026-09-20):
|
|
12
|
+
// `InputAccessoryView.js`'s `styles.container = {position: 'absolute'}`, composed AFTER the app's
|
|
13
|
+
// own style (`[props.style, styles.container]`) so it wins over any `position` an app authors.
|
|
14
|
+
// Every InputAccessoryView is positioned absolutely; nothing about it is per-instance, so it is
|
|
15
|
+
// `foldInputAccessoryViewProps` now rather than anything here. Contract:
|
|
16
|
+
// `core/engine/cpp/tests/js/touchable-payload.itest.ts`.
|
|
17
|
+
//
|
|
18
|
+
// NOT YET PORTED, and recorded honestly rather than silently skipped: vendor also wraps the app's
|
|
19
|
+
// children in an inner `SafeAreaView` sized to `useWindowDimensions().width` (`InputAccessoryView.js`
|
|
20
|
+
// render body) and returns nothing at all when it has zero children. Both are real structural gaps
|
|
21
|
+
// — a composed child node plus a live window-dimension binding — genuinely larger than a tag-only
|
|
22
|
+
// rule, and are NOT covered by this pass.
|
|
23
|
+
//
|
|
24
|
+
// TODO(rn-parity): port both. Needs (a) a `buildStructure` composing an inner wrapper node styled
|
|
25
|
+
// `{flex: 1, width}` around the app's children, with `width` read from a live window-dimensions
|
|
26
|
+
// subscription (no `SafeAreaView` primitive exists in this codebase yet — it would need building),
|
|
27
|
+
// and (b) suppressing the whole node (both platforms, not just Android's void case already fixed
|
|
28
|
+
// here) when it has zero children, matching `React.Children.count(props.children) === 0`.
|
|
29
|
+
//
|
|
30
|
+
// The `id -> nativeID` alias — is `foldIdAlias` in `SymbioteFabricProps.cpp`, applied to every
|
|
31
|
+
// tagged node rather than per primitive.
|
|
16
32
|
//
|
|
17
33
|
// PLATFORM. This is the only primitive in its group that is not platform-invariant in what it
|
|
18
34
|
// COMMITS TO: `input-accessory-view` resolves to `RCTInputAccessoryView` on iOS and to a
|
|
19
|
-
// plain `RCTView` on Android. The fold itself is platform-invariant on purpose
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
35
|
+
// plain `RCTView` on Android. The fold itself is platform-invariant on purpose.
|
|
36
|
+
//
|
|
37
|
+
// FIXED (2026-09-20). `InputAccessoryView.js` on Android does `console.warn('<InputAccessoryView>
|
|
38
|
+
// is only supported on iOS.'); return null` — the WHOLE component, children included, renders
|
|
39
|
+
// NOTHING. We used to commit a real `RCTView` and its whole children subtree (the toolbar content
|
|
40
|
+
// an app wrapped in it) — an extra, laid-out, potentially visible view where a real device shows
|
|
41
|
+
// none. `backgroundColor` being a declared iOS prop vs an Android style key is the SAME already-
|
|
42
|
+
// narrow gap, unaffected by this.
|
|
43
|
+
//
|
|
44
|
+
// The fix needed a new engine primitive, because every existing "commits no node of its own"
|
|
45
|
+
// tier-2 primitive (`touchable-native-feedback`, `touchable-without-feedback`) is an ANCHOR — it
|
|
46
|
+
// hoists its single child up in its own place, which is the opposite of what this tag needs: its
|
|
47
|
+
// whole subtree must vanish. `VOID_COMPONENT` (`core/engine/src/node.ts`, `OP_CREATE_VOID` in
|
|
48
|
+
// `mutation-buffer.ts`) is that primitive — a node the commit walk stops at, recursively,
|
|
49
|
+
// contributing neither itself nor its children. Wired purely through
|
|
50
|
+
// `core/components/src/component-names/index.android.ts`'s per-platform table, the same seam
|
|
51
|
+
// `ANCHOR_COMPONENT` already used — this behavior file needed no change at all.
|
|
27
52
|
import { registerHostBehavior, } from '@symbiote-native/engine';
|
|
28
|
-
import { INPUT_ACCESSORY_VIEW_PROP_NAMES, mapInputAccessoryViewProps, } from '../view/render-input-accessory-view.js';
|
|
29
53
|
export const INPUT_ACCESSORY_VIEW_TAG = 'input-accessory-view';
|
|
30
|
-
const CONSUMED = new Set(INPUT_ACCESSORY_VIEW_PROP_NAMES);
|
|
31
|
-
function stringOf(value) {
|
|
32
|
-
return typeof value === 'string' ? value : undefined;
|
|
33
|
-
}
|
|
34
|
-
// A StyleProp is an object, an array of them, or a registered class array — all shapes
|
|
35
|
-
// `flattenStyle` already handles. Only a scalar has to be excluded.
|
|
36
|
-
function styleOf(value) {
|
|
37
|
-
if (typeof value !== 'object' || value === null)
|
|
38
|
-
return undefined;
|
|
39
|
-
if (Array.isArray(value))
|
|
40
|
-
return value;
|
|
41
|
-
return { ...value };
|
|
42
|
-
}
|
|
43
|
-
export function foldInputAccessoryViewPayload(props) {
|
|
44
|
-
const passthrough = {};
|
|
45
|
-
for (const key of Object.keys(props)) {
|
|
46
|
-
if (!CONSUMED.has(key))
|
|
47
|
-
passthrough[key] = props[key];
|
|
48
|
-
}
|
|
49
|
-
return mapInputAccessoryViewProps({
|
|
50
|
-
nativeID: stringOf(props.nativeID),
|
|
51
|
-
backgroundColor: stringOf(props.backgroundColor),
|
|
52
|
-
style: styleOf(props.style),
|
|
53
|
-
passthrough,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
54
|
// Required by IHostBehavior and deliberately empty: this primitive owns no per-node runtime.
|
|
57
55
|
// Written out rather than shared with a `noop` so the emptiness reads as a decision.
|
|
58
56
|
function attach(_node) {
|
|
@@ -62,9 +60,5 @@ function detach(_node) {
|
|
|
62
60
|
// nothing to release
|
|
63
61
|
}
|
|
64
62
|
export function registerInputAccessoryViewBehavior() {
|
|
65
|
-
registerHostBehavior(INPUT_ACCESSORY_VIEW_TAG, {
|
|
66
|
-
attach,
|
|
67
|
-
detach,
|
|
68
|
-
foldPayload: foldInputAccessoryViewPayload,
|
|
69
|
-
});
|
|
63
|
+
registerHostBehavior(INPUT_ACCESSORY_VIEW_TAG, { attach, detach });
|
|
70
64
|
}
|
|
@@ -56,5 +56,5 @@ export declare function detachPressMachine(node: ISymbioteNode): void;
|
|
|
56
56
|
* WeakMap holding the machine's own state is keyed by node, so one node may hold exactly one of
|
|
57
57
|
* these; a tag composing it therefore must not also register the plain `pressable` behavior.
|
|
58
58
|
*/
|
|
59
|
-
export declare function createPressBehavior(refine?: IPressConfigRefinement, disabledOf?: IDisabledResolver): Pick<IHostBehavior, 'attach' | 'detach' | '
|
|
59
|
+
export declare function createPressBehavior(refine?: IPressConfigRefinement, disabledOf?: IDisabledResolver): Pick<IHostBehavior, 'attach' | 'detach' | 'ownedListeners'>;
|
|
60
60
|
export declare function registerPressableBehavior(): void;
|