@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
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// WHY THE DIVERGENCE CHECK IS DEFERRED A MICROTASK, NOT RUN SYNCHRONOUSLY INSIDE `onChange`. The
|
|
12
12
|
// obvious place to compare "what native just reported" against "what the app currently authors" is
|
|
13
13
|
// right where the report arrives. It is wrong: an ACCEPTED toggle updates the app's own state, and
|
|
14
|
-
// that state reaches
|
|
14
|
+
// that state reaches the node only once the app's OWN reconciliation runs — which, for
|
|
15
15
|
// every adapter here, happens strictly after `onChange` returns, never during it. Checking
|
|
16
16
|
// synchronously would read the STALE pre-accept value and send a spurious snap-back on every
|
|
17
17
|
// accepted toggle. The wrapper avoids this by running its own check from an effect that fires AFTER
|
|
@@ -36,97 +36,40 @@
|
|
|
36
36
|
// unresolved. `afterCommit` costs nothing extra (it fires only on a commit that already changed
|
|
37
37
|
// something) and closes that one case; the microtask path is what the no-op-handler case actually
|
|
38
38
|
// needs.
|
|
39
|
-
import { appListenerFor, dispatchViewCommand, dlog, Platform, registerHostBehavior, setBehaviorListener, } from '@symbiote-native/engine';
|
|
39
|
+
import { appListenerFor, dispatchViewCommand, dlog, Platform, propOf, registerHostBehavior, setBehaviorListener, } from '@symbiote-native/engine';
|
|
40
40
|
import { createInitialSwitchState, shouldSnapBack, switchReducer, valueFromChange, } from '../state/switch.js';
|
|
41
|
-
// The LOWERED tag — NOT the wrapper's `switch-managed` (`render-switch.ts`). One owner
|
|
42
|
-
// per node: the wrapper already runs this same machine in its own lifecycle, so registering here
|
|
43
|
-
// under the tag it emits would attach a second, redundant copy.
|
|
44
41
|
export const SWITCH_TAG = 'switch';
|
|
45
42
|
const states = new WeakMap();
|
|
46
43
|
function stateOf(node) {
|
|
47
44
|
return states.get(node);
|
|
48
45
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
// `text-input.ts`'s `selectionOf` uses for `{ start, end? }`.
|
|
58
|
-
function trackColorOf(value) {
|
|
59
|
-
if (typeof value !== 'object' || value === null)
|
|
60
|
-
return undefined;
|
|
61
|
-
const bag = { ...value };
|
|
62
|
-
const falseColor = stringOf(bag.false);
|
|
63
|
-
const trueColor = stringOf(bag.true);
|
|
64
|
-
if (falseColor === undefined && trueColor === undefined)
|
|
65
|
-
return undefined;
|
|
66
|
-
return { false: falseColor, true: trueColor };
|
|
67
|
-
}
|
|
68
|
-
// The wrapper-body prop fold this primitive owes its lowered form: `trackColor` / `thumbColor` /
|
|
69
|
-
// `ios_backgroundColor` are AUTHORED names, none of them a real Fabric prop — RN's Switch view
|
|
46
|
+
// The three narrowing helpers that stood here — `stringOf`, `booleanOf` and `trackColorOf` — went
|
|
47
|
+
// with the fold. They existed to read an untyped bag safely, and the bag is read on the other side
|
|
48
|
+
// of the wire now, where a `folly::dynamic` is checked the same way and with no allocation.
|
|
49
|
+
// THE PROP FOLD MOVED TO THE ENGINE — `foldSwitchProps` in `SymbioteFabricProps.cpp`, with
|
|
50
|
+
// `core/engine/cpp/tests/js/switch-payload.itest.ts` as its contract.
|
|
51
|
+
//
|
|
52
|
+
// It is the clearest case of the three ports so far: `trackColor` / `thumbColor` /
|
|
53
|
+
// `ios_backgroundColor` are AUTHORED names and none of them is a real Fabric prop. RN's Switch view
|
|
70
54
|
// declares `onTintColor`/`tintColor` (iOS) or `trackColorFor*`/`trackTintColor` (Android), plus
|
|
71
|
-
// `thumbTintColor
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
trackColorForFalse: trackColor?.false,
|
|
79
|
-
trackColorForTrue: trackColor?.true,
|
|
80
|
-
trackTintColor: value ? trackColor?.true : trackColor?.false,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
onTintColor: trackColor?.true,
|
|
85
|
-
tintColor: trackColor?.false,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
// RN rounds the iOS background pill to this radius when `ios_backgroundColor` is set — the same
|
|
89
|
-
// constant `render-switch.ts` uses, kept independent rather than exported+imported for one
|
|
90
|
-
// primitive-local literal (see that file for the upstream fact it encodes).
|
|
91
|
-
const IOS_BACKGROUND_BORDER_RADIUS = 16;
|
|
55
|
+
// `thumbTintColor`, and `ios_backgroundColor` is a STYLE rather than a prop. The wrappers took those
|
|
56
|
+
// per-platform NAMES from an adapter-supplied table; a tag has no adapter to ask, so the branch
|
|
57
|
+
// belongs beside the tree — once, instead of the five copies it had.
|
|
58
|
+
//
|
|
59
|
+
// What is still here is the MACHINE: the snap-back handshake below, which reads app state a
|
|
60
|
+
// microtask after native reports a toggle and corrects a disagreement with an imperative command.
|
|
92
61
|
// The platform-specific imperative command RN's own Switch sends to correct a rejected toggle
|
|
93
|
-
// (Switch.js:221-225)
|
|
62
|
+
// (Switch.js:221-225). It reads `Platform.OS` because it is an IMPERATIVE call decided at gesture
|
|
63
|
+
// time, not a prop — the payload half of the same platform split went to the engine with the fold.
|
|
94
64
|
function snapBackCommand() {
|
|
95
65
|
return Platform.OS === 'android' ? 'setNativeValue' : 'setValue';
|
|
96
66
|
}
|
|
97
|
-
function foldPayload(props) {
|
|
98
|
-
const value = props.value === true;
|
|
99
|
-
const trackColor = trackColorOf(props.trackColor);
|
|
100
|
-
const iosBackground = stringOf(props.ios_backgroundColor);
|
|
101
|
-
const out = {
|
|
102
|
-
...props,
|
|
103
|
-
value,
|
|
104
|
-
disabled: booleanOf(props.disabled),
|
|
105
|
-
...trackColorPropsFor(value, trackColor),
|
|
106
|
-
thumbTintColor: stringOf(props.thumbColor),
|
|
107
|
-
style: iosBackground === undefined
|
|
108
|
-
? props.style
|
|
109
|
-
: [
|
|
110
|
-
props.style,
|
|
111
|
-
{
|
|
112
|
-
backgroundColor: iosBackground,
|
|
113
|
-
borderRadius: IOS_BACKGROUND_BORDER_RADIUS,
|
|
114
|
-
},
|
|
115
|
-
],
|
|
116
|
-
};
|
|
117
|
-
// The authored names themselves must NOT ride along: none is a Fabric prop, and leaving them in
|
|
118
|
-
// the payload is how a reader concludes the fold ran when it did not.
|
|
119
|
-
delete out.trackColor;
|
|
120
|
-
delete out.thumbColor;
|
|
121
|
-
delete out.ios_backgroundColor;
|
|
122
|
-
return out;
|
|
123
|
-
}
|
|
124
67
|
// Shared by both triggers — see the module header for why there are two.
|
|
125
68
|
function evaluateSnapBack(node) {
|
|
126
69
|
const state = stateOf(node);
|
|
127
70
|
if (state === undefined)
|
|
128
71
|
return; // detached before this ran
|
|
129
|
-
const fabricValue = node
|
|
72
|
+
const fabricValue = propOf(node, 'value') === true;
|
|
130
73
|
if (!shouldSnapBack(state, fabricValue)) {
|
|
131
74
|
dlog(`Switch behavior snap-back no-op reported=${String(state.lastNativeReport)} value=${fabricValue}`);
|
|
132
75
|
return;
|
|
@@ -143,30 +86,45 @@ function onChange(node, event) {
|
|
|
143
86
|
return;
|
|
144
87
|
dlog(`Switch behavior onChange value=${String(value)} eventCount=${String(event.nativeEvent.eventCount)}`);
|
|
145
88
|
states.set(node, switchReducer(state, { type: 'native-reported', value }));
|
|
89
|
+
// Switch.js:201-207's `handleChange` — `onChange` first, THEN `onValueChange`, always. An app
|
|
90
|
+
// with side effects observable across both (a shared counter, a log) sees that exact order on a
|
|
91
|
+
// real device.
|
|
92
|
+
//
|
|
93
|
+
// `onChange` is a raw `change` listener authored directly on the bare tag — not part of any
|
|
94
|
+
// adapter's public surface today, but `change` is the name this behavior's own dispatcher owns
|
|
95
|
+
// (`ownedListeners` below), so one is stashed rather than silently evicting the machine.
|
|
96
|
+
const rawListener = appListenerFor(node, 'change');
|
|
97
|
+
if (typeof rawListener === 'function')
|
|
98
|
+
rawListener(event);
|
|
146
99
|
// `onValueChange` is not a Fabric event — it is a fold the component wrapper does over the raw
|
|
147
100
|
// `change` payload (same class as TextInput's `onValueChange`, `text-input.ts`'s
|
|
148
|
-
// `callValueChange`), so it lands
|
|
101
|
+
// `callValueChange`), so it lands on the node as a plain prop key rather than through
|
|
149
102
|
// `ownedListeners`. ONE argument, `value` carried on the event (`ISwitchChangeEvent`) — same
|
|
150
103
|
// reason as TextInput: Svelte's compiler forces an individual `on*` attribute through a native
|
|
151
104
|
// listener wrapper that calls with exactly one argument, always a real object.
|
|
152
|
-
const listener = node
|
|
105
|
+
const listener = propOf(node, 'onValueChange');
|
|
153
106
|
if (typeof listener === 'function') {
|
|
154
107
|
const changeEvent = Object.assign(event, { value });
|
|
155
108
|
listener(changeEvent);
|
|
156
109
|
}
|
|
157
|
-
// A raw `change` listener authored directly on the bare tag — not part of any adapter's public
|
|
158
|
-
// surface today, but `change` is the name this behavior's own dispatcher owns
|
|
159
|
-
// (`ownedListeners` below), so one is stashed rather than silently evicting the machine.
|
|
160
|
-
const rawListener = appListenerFor(node, 'change');
|
|
161
|
-
if (typeof rawListener === 'function')
|
|
162
|
-
rawListener(event);
|
|
163
110
|
// See the module header: deferred so an ACCEPTING app's own state update has a turn of the
|
|
164
|
-
// microtask queue to
|
|
111
|
+
// microtask queue to write the node first.
|
|
165
112
|
queueMicrotask(() => evaluateSnapBack(node));
|
|
166
113
|
}
|
|
114
|
+
// Switch.js:238-239,288-289 — unconditional on both platforms, never a function of any prop: a
|
|
115
|
+
// switch always claims the responder and never yields it, so its own native drag-to-toggle cannot
|
|
116
|
+
// be stolen mid-gesture by a parent ScrollView's own responder negotiation.
|
|
117
|
+
function alwaysClaimsResponder() {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
function neverYieldsResponder() {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
167
123
|
function attach(node) {
|
|
168
124
|
states.set(node, createInitialSwitchState());
|
|
169
125
|
setBehaviorListener(node, 'change', event => onChange(node, event));
|
|
126
|
+
setBehaviorListener(node, 'startShouldSetResponder', alwaysClaimsResponder);
|
|
127
|
+
setBehaviorListener(node, 'responderTerminationRequest', neverYieldsResponder);
|
|
170
128
|
}
|
|
171
129
|
function detach(node) {
|
|
172
130
|
states.delete(node);
|
|
@@ -180,7 +138,6 @@ export function registerSwitchBehavior() {
|
|
|
180
138
|
// cannot — a prop change with no preceding native event.
|
|
181
139
|
afterCommit: evaluateSnapBack,
|
|
182
140
|
detach,
|
|
183
|
-
foldPayload,
|
|
184
141
|
ownedListeners: ['change'],
|
|
185
142
|
});
|
|
186
143
|
}
|