@symbiote-native/components 1.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.
Files changed (110) hide show
  1. package/README.md +11 -13
  2. package/build/accessibility-props.d.ts +1 -1
  3. package/build/accessibility-props.js +2 -2
  4. package/build/behaviors/activity-indicator/index.android.d.ts +1 -0
  5. package/build/behaviors/activity-indicator/index.android.js +16 -0
  6. package/build/behaviors/activity-indicator/index.d.ts +3 -0
  7. package/build/behaviors/activity-indicator/index.ios.d.ts +1 -0
  8. package/build/behaviors/activity-indicator/index.ios.js +14 -0
  9. package/build/behaviors/activity-indicator/index.js +5 -0
  10. package/build/behaviors/activity-indicator/shared.d.ts +18 -0
  11. package/build/behaviors/activity-indicator/shared.js +120 -0
  12. package/build/behaviors/button.d.ts +13 -0
  13. package/build/behaviors/button.js +340 -0
  14. package/build/behaviors/image-background.d.ts +3 -0
  15. package/build/behaviors/image-background.js +155 -0
  16. package/build/behaviors/image.d.ts +1 -2
  17. package/build/behaviors/image.js +25 -106
  18. package/build/behaviors/input-accessory-view.d.ts +1 -2
  19. package/build/behaviors/input-accessory-view.js +49 -55
  20. package/build/behaviors/pressable.d.ts +59 -1
  21. package/build/behaviors/pressable.js +142 -96
  22. package/build/behaviors/refresh-control.d.ts +2 -0
  23. package/build/behaviors/refresh-control.js +96 -0
  24. package/build/behaviors/scroll-view/index.android.d.ts +1 -0
  25. package/build/behaviors/scroll-view/index.android.js +40 -0
  26. package/build/behaviors/scroll-view/index.d.ts +3 -0
  27. package/build/behaviors/scroll-view/index.ios.d.ts +1 -0
  28. package/build/behaviors/scroll-view/index.ios.js +10 -0
  29. package/build/behaviors/scroll-view/index.js +8 -0
  30. package/build/behaviors/scroll-view/responder.d.ts +4 -0
  31. package/build/behaviors/scroll-view/responder.js +202 -0
  32. package/build/behaviors/scroll-view/shared.d.ts +9 -0
  33. package/build/behaviors/scroll-view/shared.js +291 -0
  34. package/build/behaviors/scroll-view/sticky.d.ts +18 -0
  35. package/build/behaviors/scroll-view/sticky.js +581 -0
  36. package/build/behaviors/switch.d.ts +1 -1
  37. package/build/behaviors/switch.js +49 -88
  38. package/build/behaviors/text-input.d.ts +2 -2
  39. package/build/behaviors/text-input.js +244 -104
  40. package/build/behaviors/touchable-highlight.d.ts +9 -0
  41. package/build/behaviors/touchable-highlight.js +205 -0
  42. package/build/behaviors/touchable-native-feedback.d.ts +20 -0
  43. package/build/behaviors/touchable-native-feedback.js +254 -0
  44. package/build/behaviors/touchable-opacity.d.ts +12 -0
  45. package/build/behaviors/touchable-opacity.js +239 -0
  46. package/build/behaviors/touchable-without-feedback.d.ts +2 -0
  47. package/build/behaviors/touchable-without-feedback.js +231 -0
  48. package/build/component-names/index.android.js +31 -25
  49. package/build/component-names/index.ios.js +28 -23
  50. package/build/component-names/shared.d.ts +2 -1
  51. package/build/component-names/shared.js +16 -6
  52. package/build/descriptor.js +4 -4
  53. package/build/index.d.ts +26 -26
  54. package/build/index.js +56 -27
  55. package/build/register.d.ts +1 -0
  56. package/build/register.js +55 -0
  57. package/build/resolve-intrinsic.js +3 -9
  58. package/build/scroll-view-commands.d.ts +1 -5
  59. package/build/scroll-view-commands.js +23 -85
  60. package/build/state/flat-list.d.ts +2 -2
  61. package/build/state/flat-list.js +10 -2
  62. package/build/state/pressable.d.ts +6 -1
  63. package/build/state/pressable.js +63 -28
  64. package/build/state/section-list.d.ts +2 -0
  65. package/build/state/section-list.js +14 -7
  66. package/build/state/text-input.d.ts +10 -40
  67. package/build/state/text-input.js +17 -186
  68. package/build/state/touchable.d.ts +1 -0
  69. package/build/state/touchable.js +11 -8
  70. package/build/state/virtualized-list-reducer.d.ts +2 -2
  71. package/build/state/virtualized-list.d.ts +6 -6
  72. package/build/state/virtualized-list.js +71 -37
  73. package/build/text-props.d.ts +0 -8
  74. package/build/text-props.js +14 -25
  75. package/build/view/render-button.d.ts +11 -4
  76. package/build/view/render-button.js +74 -22
  77. package/build/view/render-image/index.d.ts +14 -1
  78. package/build/view/render-image/index.js +22 -147
  79. package/build/view/render-input-accessory-view.d.ts +1 -5
  80. package/build/view/render-input-accessory-view.js +26 -48
  81. package/build/view/render-keyboard-avoiding-view.d.ts +7 -1
  82. package/build/view/render-keyboard-avoiding-view.js +40 -1
  83. package/build/view/render-modal.d.ts +1 -1
  84. package/build/view/render-modal.js +18 -8
  85. package/build/view/render-pressable/index.d.ts +3 -0
  86. package/build/view/render-pressable/index.js +28 -0
  87. package/build/view/render-scroll-view.d.ts +1 -4
  88. package/build/view/render-scroll-view.js +17 -54
  89. package/build/view/render-switch.d.ts +4 -15
  90. package/build/view/render-switch.js +4 -42
  91. package/build/view/render-touchable-highlight.d.ts +1 -0
  92. package/build/view/render-touchable-native-feedback.d.ts +19 -1
  93. package/build/view/render-touchable-native-feedback.js +34 -9
  94. package/host-primitives.cjs +178 -280
  95. package/host-primitives.d.cts +0 -3
  96. package/package.json +8 -21
  97. package/build/fold-host-bag.d.ts +0 -15
  98. package/build/fold-host-bag.js +0 -99
  99. package/build/state-style.d.ts +0 -15
  100. package/build/state-style.js +0 -47
  101. package/build/view/render-activity-indicator.d.ts +0 -25
  102. package/build/view/render-activity-indicator.js +0 -88
  103. package/build/view/render-image-background.d.ts +0 -9
  104. package/build/view/render-image-background.js +0 -48
  105. package/build/view/render-text-input.d.ts +0 -11
  106. package/build/view/render-text-input.js +0 -39
  107. package/lowering-fixtures.cjs +0 -259
  108. package/lowering-fixtures.d.cts +0 -17
  109. package/specialize-state-style.cjs +0 -219
  110. package/specialize-state-style.d.cts +0 -15
@@ -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 `node.props.value` only once the app's OWN reconciliation runs — which, for
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 `symbiote-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
- export const SWITCH_TAG = 'symbiote-switch';
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
- function stringOf(value) {
50
- return typeof value === 'string' ? value : undefined;
51
- }
52
- function booleanOf(value) {
53
- return typeof value === 'boolean' ? value : undefined;
54
- }
55
- // `{ false?, true? }` narrowed at runtime — an authored object arriving through an untyped bag
56
- // cannot be trusted to the type system without reading it field by field, the same idiom
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`. `render-switch.ts` folds them for the wrapper path via an adapter-supplied
72
- // `ISwitchPlatform`; a lowered node has no adapter to supply one, so this reads `Platform.OS`
73
- // directly — the same fact every adapter's own index.ios.ts/index.android.ts already encodes as a
74
- // literal, just read once here instead of five times.
75
- function trackColorPropsFor(value, trackColor) {
76
- if (Platform.OS === 'android') {
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) — read off Platform.OS for the same reason `trackColorPropsFor` is.
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.props.value === true;
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,26 +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 }));
146
- // `onValueChange` is not a Fabric event — it is a fold the component wrapper does over the raw
147
- // `change` payload (same class as TextInput's `onValueChange`, `text-input.ts`'s
148
- // `callValueChange`), so it lands in `node.props` as a plain function key rather than through
149
- // `ownedListeners`.
150
- const listener = node.props.onValueChange;
151
- if (typeof listener === 'function')
152
- listener(value, event);
153
- // A raw `change` listener authored directly on the bare tag — not part of any adapter's public
154
- // surface today, but `change` is the name this behavior's own dispatcher owns
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
155
95
  // (`ownedListeners` below), so one is stashed rather than silently evicting the machine.
156
96
  const rawListener = appListenerFor(node, 'change');
157
97
  if (typeof rawListener === 'function')
158
98
  rawListener(event);
99
+ // `onValueChange` is not a Fabric event — it is a fold the component wrapper does over the raw
100
+ // `change` payload (same class as TextInput's `onValueChange`, `text-input.ts`'s
101
+ // `callValueChange`), so it lands on the node as a plain prop key rather than through
102
+ // `ownedListeners`. ONE argument, `value` carried on the event (`ISwitchChangeEvent`) — same
103
+ // reason as TextInput: Svelte's compiler forces an individual `on*` attribute through a native
104
+ // listener wrapper that calls with exactly one argument, always a real object.
105
+ const listener = propOf(node, 'onValueChange');
106
+ if (typeof listener === 'function') {
107
+ const changeEvent = Object.assign(event, { value });
108
+ listener(changeEvent);
109
+ }
159
110
  // See the module header: deferred so an ACCEPTING app's own state update has a turn of the
160
- // microtask queue to reach `node.props.value` first.
111
+ // microtask queue to write the node first.
161
112
  queueMicrotask(() => evaluateSnapBack(node));
162
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
+ }
163
123
  function attach(node) {
164
124
  states.set(node, createInitialSwitchState());
165
125
  setBehaviorListener(node, 'change', event => onChange(node, event));
126
+ setBehaviorListener(node, 'startShouldSetResponder', alwaysClaimsResponder);
127
+ setBehaviorListener(node, 'responderTerminationRequest', neverYieldsResponder);
166
128
  }
167
129
  function detach(node) {
168
130
  states.delete(node);
@@ -176,7 +138,6 @@ export function registerSwitchBehavior() {
176
138
  // cannot — a prop change with no preceding native event.
177
139
  afterCommit: evaluateSnapBack,
178
140
  detach,
179
- foldPayload,
180
141
  ownedListeners: ['change'],
181
142
  });
182
143
  }
@@ -1,7 +1,7 @@
1
1
  import { type ISymbioteNode } from '@symbiote-native/engine';
2
2
  import { type ITextInputHandle } from '../state/text-input';
3
- export declare const TEXT_INPUT_TAG = "symbiote-text-input";
4
- export declare const TEXT_INPUT_MULTILINE_TAG = "symbiote-text-input-multiline";
3
+ export declare const TEXT_INPUT_TAG = "text-input";
4
+ export declare const TEXT_INPUT_MULTILINE_TAG = "text-input-multiline";
5
5
  /**
6
6
  * The imperative API RN exposes on a TextInput ref, built over the engine node. Reached through
7
7
  * each adapter's own `host-instance` accessor — the capability, not a shape