@symbiote-native/solid 1.0.0 → 2.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 +9 -10
- package/babel-preset.cjs +0 -5
- package/build/components/activity-indicator-props.d.ts +5 -0
- package/build/components/activity-indicator-props.js +1 -0
- package/build/components/button-props.d.ts +5 -0
- package/build/components/button-props.js +1 -0
- package/build/components/flat-list/index.jsx +3 -3
- package/build/components/{image-background.d.ts → image-background-props.d.ts} +2 -3
- package/build/components/image-background-props.js +1 -0
- package/build/components/image-props.d.ts +5 -0
- package/build/components/image-props.js +6 -0
- package/build/components/{input-accessory-view.d.ts → input-accessory-view-props.d.ts} +1 -2
- package/build/components/input-accessory-view-props.js +5 -0
- package/build/components/keyboard-avoiding-view.jsx +9 -4
- package/build/components/modal.jsx +6 -6
- package/build/components/{pressable.d.ts → pressable-props.d.ts} +8 -7
- package/build/components/pressable-props.js +13 -0
- package/build/components/{refresh-control.d.ts → refresh-control-props.d.ts} +3 -3
- package/build/components/refresh-control-props.js +7 -0
- package/build/components/safe-area-view-props.d.ts +10 -0
- package/build/components/safe-area-view-props.js +5 -0
- package/build/components/{scroll-view/shared.d.ts → scroll-view-props.d.ts} +7 -13
- package/build/components/scroll-view-props.js +29 -0
- package/build/components/switch-props.d.ts +6 -0
- package/build/components/switch-props.js +5 -0
- package/build/components/text-input-props.d.ts +8 -0
- package/build/components/text-input-props.js +1 -0
- package/build/components/{text.d.ts → text-props.d.ts} +3 -3
- package/build/components/text-props.js +11 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +8 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback-props.js +1 -0
- package/build/components/touchable-props.d.ts +18 -0
- package/build/components/touchable-props.js +1 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +6 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.js +1 -0
- package/build/components/{view.d.ts → view-props.d.ts} +2 -3
- package/build/components/view-props.js +11 -0
- package/build/components/virtualized-list/index.android.js +5 -4
- package/build/components/virtualized-list/index.ios.js +6 -6
- package/build/components/virtualized-list/shared.d.ts +1 -3
- package/build/components/virtualized-list/shared.jsx +87 -98
- package/build/components/virtualized-list/sticky-header.d.ts +6 -0
- package/build/components/{scroll-view → virtualized-list}/sticky-header.jsx +28 -36
- package/build/components.d.ts +20 -30
- package/build/components.js +6 -33
- package/build/create-portal/index.jsx +1 -1
- package/build/descriptor-to-solid.js +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.js +11 -3
- package/build/jsx-runtime.d.ts +39 -2
- package/build/modules/animated/index.d.ts +0 -4
- package/build/modules/animated/index.js +16 -22
- package/build/modules/image.d.ts +3 -0
- package/build/modules/image.js +12 -0
- package/build/register.d.ts +1 -1
- package/build/register.js +12 -28
- package/build/renderer.js +79 -16
- package/build/utils/stable-keys.d.ts +1 -1
- package/build/utils/stable-keys.js +6 -5
- package/metro-css-parser.cjs +2 -1
- package/package.json +5 -10
- package/babel-lower-host-primitives.cjs +0 -323
- package/build/components/activity-indicator/index.android.d.ts +0 -2
- package/build/components/activity-indicator/index.android.js +0 -11
- package/build/components/activity-indicator/index.d.ts +0 -1
- package/build/components/activity-indicator/index.ios.d.ts +0 -2
- package/build/components/activity-indicator/index.ios.js +0 -10
- package/build/components/activity-indicator/index.js +0 -4
- package/build/components/activity-indicator/shared.d.ts +0 -8
- package/build/components/activity-indicator/shared.js +0 -51
- package/build/components/button.d.ts +0 -7
- package/build/components/button.jsx +0 -45
- package/build/components/image-background.jsx +0 -74
- package/build/components/image.d.ts +0 -9
- package/build/components/image.js +0 -75
- package/build/components/input-accessory-view.jsx +0 -39
- package/build/components/pressable.jsx +0 -211
- package/build/components/refresh-control.jsx +0 -36
- package/build/components/safe-area-view.d.ts +0 -11
- package/build/components/safe-area-view.jsx +0 -24
- package/build/components/scroll-view/index.android.d.ts +0 -2
- package/build/components/scroll-view/index.android.js +0 -11
- package/build/components/scroll-view/index.d.ts +0 -2
- package/build/components/scroll-view/index.ios.d.ts +0 -2
- package/build/components/scroll-view/index.ios.js +0 -8
- package/build/components/scroll-view/index.js +0 -9
- package/build/components/scroll-view/shared.jsx +0 -334
- package/build/components/scroll-view/sticky-header.d.ts +0 -18
- package/build/components/switch/index.android.d.ts +0 -2
- package/build/components/switch/index.android.js +0 -12
- package/build/components/switch/index.d.ts +0 -1
- package/build/components/switch/index.ios.d.ts +0 -2
- package/build/components/switch/index.ios.js +0 -11
- package/build/components/switch/index.js +0 -4
- package/build/components/switch/shared.d.ts +0 -11
- package/build/components/switch/shared.js +0 -87
- package/build/components/text-input.d.ts +0 -10
- package/build/components/text-input.js +0 -290
- package/build/components/text.jsx +0 -27
- package/build/components/touchable/index.d.ts +0 -22
- package/build/components/touchable/index.jsx +0 -247
- package/build/components/touchable-native-feedback/index.d.ts +0 -16
- package/build/components/touchable-native-feedback/index.jsx +0 -68
- package/build/components/view.jsx +0 -49
- package/build/state-style.d.ts +0 -1
- package/build/state-style.js +0 -13
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// InputAccessoryView — the Solid lifecycle half (iOS). The host-node assembly (nativeID /
|
|
2
|
-
// backgroundColor / style / passthrough forwarding onto `symbiote-input-accessory-view`) lives
|
|
3
|
-
// framework-agnostic in @symbiote-native/components' renderInputAccessoryView and is shared
|
|
4
|
-
// verbatim with React, Vue and Svelte; Solid supplies only the reactivity and the children.
|
|
5
|
-
//
|
|
6
|
-
// REAL JSX, NOT descriptorToSolid — the same rule View and SafeAreaView follow. The render fn
|
|
7
|
-
// returns a host with ZERO structural children by contract; the toolbar content is a live Solid
|
|
8
|
-
// subtree only the reconciler can reduce, so the tag stays literal and the shared fn contributes
|
|
9
|
-
// its PROPS. The literal tag is safe because renderInputAccessoryView's `type` is a constant.
|
|
10
|
-
//
|
|
11
|
-
// NOTHING here destructures `props` at setup. Solid props are getters and a component body runs
|
|
12
|
-
// ONCE; splitProps keeps both halves reactive, and every read happens inside the bag accessor
|
|
13
|
-
// that `spread` re-runs as a render effect on the SAME host node.
|
|
14
|
-
import { splitProps } from 'solid-js';
|
|
15
|
-
import { renderInputAccessoryView, resolveAccessibilityProps, } from '@symbiote-native/components';
|
|
16
|
-
import { withStableKeys } from '../utils/stable-keys.js';
|
|
17
|
-
export function InputAccessoryView(props) {
|
|
18
|
-
const [local, rest] = splitProps(props, [
|
|
19
|
-
'children',
|
|
20
|
-
'nativeID',
|
|
21
|
-
'backgroundColor',
|
|
22
|
-
'style',
|
|
23
|
-
]);
|
|
24
|
-
// withStableKeys is load-bearing twice over: renderInputAccessoryView emits `nativeID` and
|
|
25
|
-
// `backgroundColor` CONDITIONALLY, and resolveAccessibilityProps has two branches with
|
|
26
|
-
// different key sets. Solid's `spread` has no removal pass, so a key that vanishes between
|
|
27
|
-
// runs would keep its last value on the native view forever (see utils/stable-keys.ts).
|
|
28
|
-
const bag = withStableKeys(() => renderInputAccessoryView({
|
|
29
|
-
nativeID: local.nativeID,
|
|
30
|
-
backgroundColor: local.backgroundColor,
|
|
31
|
-
style: local.style,
|
|
32
|
-
// This component owns its host element rather than rendering through a View, so folding
|
|
33
|
-
// aria/role into the canonical accessibility* props is its own job, like Image's.
|
|
34
|
-
passthrough: { ...resolveAccessibilityProps(rest) },
|
|
35
|
-
}).props);
|
|
36
|
-
return (<symbiote-input-accessory-view {...bag()}>
|
|
37
|
-
{local.children}
|
|
38
|
-
</symbiote-input-accessory-view>);
|
|
39
|
-
}
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
// Pressable — the Solid lifecycle half. The press lifecycle (the long-press timer, the
|
|
2
|
-
// unstable_pressDelay deferral, the pressRetentionOffset drift test, the suppression flags) lives
|
|
3
|
-
// in @symbiote-native/components/state as a pure machine over a runtime + host; the render
|
|
4
|
-
// decisions (which responder listeners the View carries, the disabled→accessibilityState fold, the
|
|
5
|
-
// ripple prop) in @symbiote-native/components/view. Both are shared verbatim with React, Vue and
|
|
6
|
-
// Svelte. Solid supplies only the reactivity: a signal for `pressed`, a setup-scope object for the
|
|
7
|
-
// press runtime, a plain variable for the responder node, and one memo that rebuilds the handler
|
|
8
|
-
// bag when the caller's config changes.
|
|
9
|
-
//
|
|
10
|
-
// THE RESPONDER EVENTS ARE NEVER NAMED AS EVENTS HERE. buildPressableListeners returns a flat bag
|
|
11
|
-
// (`onPress`, `onStartShouldSetResponder`, `onResponderMove`, …) which rides into View's prop bag
|
|
12
|
-
// and reaches the engine through routeProp, which decides prop-vs-event from the node's ViewConfig
|
|
13
|
-
// and attaches the responder protocol itself (symbiote-engine-core §2). An adapter-side `onX` check
|
|
14
|
-
// would both duplicate that and get the edge cases wrong.
|
|
15
|
-
//
|
|
16
|
-
// Pressable composes View rather than painting its own host tag, exactly as React's and Vue's do —
|
|
17
|
-
// so the aria/role fold, the class+style merge and the vanished-key widening (utils/stable-keys)
|
|
18
|
-
// all happen once, in View. That also means there is no renderPressable() Descriptor to bridge:
|
|
19
|
-
// `children` is a live Solid subtree, which is the case the render-fn boundary rules OUT of core
|
|
20
|
-
// (.claude/rules/component-render-fn-boundary.md).
|
|
21
|
-
//
|
|
22
|
-
// NOTHING here destructures `props`. Solid props are getters and a component body runs ONCE, so a
|
|
23
|
-
// destructure would freeze the Pressable at its mount-time config; every read below sits inside an
|
|
24
|
-
// accessor, a memo or an event handler.
|
|
25
|
-
import { createEffect, createMemo, onCleanup, createSignal, splitProps, untrack, } from 'solid-js';
|
|
26
|
-
import { buildPressableListeners, createPressHandlers, createPressRuntime, disposePressRuntime, noteHoverNoop, resolveDisabledAccessibilityState, rippleProps, DEFAULT_DELAY_LONG_PRESS_MS, DEFAULT_MIN_PRESS_DURATION_MS, } from '@symbiote-native/components';
|
|
27
|
-
import { dlog, } from '@symbiote-native/engine';
|
|
28
|
-
import { setHostPressed } from '../renderer.js';
|
|
29
|
-
import { View } from './view.jsx';
|
|
30
|
-
// Read by Pressable itself; everything else forwards onto View. The user's press callbacks MUST be
|
|
31
|
-
// split off — they are pure JS, and it is the machine's SYNTHESIZED handlers that go on the host.
|
|
32
|
-
const HANDLED_PROPS = [
|
|
33
|
-
'onPress',
|
|
34
|
-
'onPressIn',
|
|
35
|
-
'onPressOut',
|
|
36
|
-
'onPressMove',
|
|
37
|
-
'onLongPress',
|
|
38
|
-
'delayLongPress',
|
|
39
|
-
'disabled',
|
|
40
|
-
'cancelable',
|
|
41
|
-
'pressRetentionOffset',
|
|
42
|
-
'unstable_pressDelay',
|
|
43
|
-
'android_ripple',
|
|
44
|
-
'android_disableSound',
|
|
45
|
-
'onHoverIn',
|
|
46
|
-
'onHoverOut',
|
|
47
|
-
'delayHoverIn',
|
|
48
|
-
'delayHoverOut',
|
|
49
|
-
'accessibilityState',
|
|
50
|
-
'style',
|
|
51
|
-
'children',
|
|
52
|
-
];
|
|
53
|
-
// `hitSlop` is deliberately NOT in that list: the machine reads it for the retention test AND the
|
|
54
|
-
// host needs it to enlarge the touch target, so it forwards through `rest` untouched.
|
|
55
|
-
function PressableImpl(props, minPressDuration) {
|
|
56
|
-
const [local, rest] = splitProps(props, HANDLED_PROPS);
|
|
57
|
-
const [pressed, setPressed] = createSignal(false);
|
|
58
|
-
// The mutable press runtime (in-flight timers, suppression flags, measured region). A plain
|
|
59
|
-
// setup-scope object, never a signal: the machine mutates it on every event and nothing reads it
|
|
60
|
-
// reactively — same shape as Vue's setup-scope runtime and Svelte's plain `const`.
|
|
61
|
-
const runtime = createPressRuntime();
|
|
62
|
-
// The responder node, held by IDENTITY in a plain variable. A store or a deep proxy would become
|
|
63
|
-
// a different key than the one the engine's commit mirror holds, and measure() would silently
|
|
64
|
-
// no-op (symbiote-engine-core §3).
|
|
65
|
-
let responder = null;
|
|
66
|
-
const attachRef = (node) => {
|
|
67
|
-
responder = node;
|
|
68
|
-
};
|
|
69
|
-
// The lifecycle seam the machine fills: flip the reactive `pressed` cell, and expose the
|
|
70
|
-
// responder's raw frame-measure. Before the first commit the node has no Fabric tag, so measure()
|
|
71
|
-
// no-ops and the machine falls back to its radius test — the same degradation every adapter has,
|
|
72
|
-
// and not a whenCommitted case: the measure is triggered by a touch, which cannot arrive before
|
|
73
|
-
// the view exists.
|
|
74
|
-
const host = {
|
|
75
|
-
// Two sinks for one fact, and both are needed. The signal drives the FRAMEWORK-visible half —
|
|
76
|
-
// a functional `style`, a render-prop child — which is what forces this element to stay a
|
|
77
|
-
// component at all. `setHostPressed` drives the ENGINE-visible half, so `.btn:active` applies
|
|
78
|
-
// to a refused Pressable exactly as it would to a lowered `symbiote-pressable`. That is what
|
|
79
|
-
// makes a refusal cost the component instance and NOT the pressed styling
|
|
80
|
-
// (`.claude/rules/host-primitive-tier.md`).
|
|
81
|
-
//
|
|
82
|
-
// Before the first commit `responder` is null and the engine call is skipped: nothing is
|
|
83
|
-
// painted yet, and a press cannot arrive before the view exists.
|
|
84
|
-
setPressed: next => {
|
|
85
|
-
setPressed(next);
|
|
86
|
-
if (responder !== null)
|
|
87
|
-
setHostPressed(responder, next);
|
|
88
|
-
},
|
|
89
|
-
getMeasureFn: () => {
|
|
90
|
-
const node = responder;
|
|
91
|
-
if (node === null)
|
|
92
|
-
return undefined;
|
|
93
|
-
return callback => {
|
|
94
|
-
node.measure(callback);
|
|
95
|
-
};
|
|
96
|
-
},
|
|
97
|
-
schedule: (callback, ms) => {
|
|
98
|
-
const id = setTimeout(callback, ms);
|
|
99
|
-
return () => {
|
|
100
|
-
clearTimeout(id);
|
|
101
|
-
};
|
|
102
|
-
},
|
|
103
|
-
now: Date.now,
|
|
104
|
-
};
|
|
105
|
-
onCleanup(() => {
|
|
106
|
-
disposePressRuntime(runtime);
|
|
107
|
-
});
|
|
108
|
-
// Rebuilt when the caller's config changes (the closures capture live values); the runtime
|
|
109
|
-
// persists across rebuilds, so an in-flight timer or drift flag survives one. Deliberately does
|
|
110
|
-
// NOT read `pressed` — a press must not churn the handler identities the host node holds.
|
|
111
|
-
const handlers = createMemo(() => createPressHandlers({
|
|
112
|
-
onPress: local.onPress,
|
|
113
|
-
onPressIn: local.onPressIn,
|
|
114
|
-
onPressOut: local.onPressOut,
|
|
115
|
-
onPressMove: local.onPressMove,
|
|
116
|
-
onLongPress: local.onLongPress,
|
|
117
|
-
delayLongPress: local.delayLongPress ?? DEFAULT_DELAY_LONG_PRESS_MS,
|
|
118
|
-
unstable_pressDelay: local.unstable_pressDelay ?? 0,
|
|
119
|
-
minPressDuration,
|
|
120
|
-
hitSlop: rest.hitSlop,
|
|
121
|
-
pressRetentionOffset: local.pressRetentionOffset,
|
|
122
|
-
}, runtime, host));
|
|
123
|
-
createEffect(() => {
|
|
124
|
-
noteHoverNoop(local.onHoverIn, local.onHoverOut);
|
|
125
|
-
});
|
|
126
|
-
// Reading `pressed` sits INSIDE the function branch on purpose: a static style must not make the
|
|
127
|
-
// prop bag depend on the press signal, or every touch would re-diff and re-commit the whole bag
|
|
128
|
-
// for nothing. Unlike the children case below, reading it here is SAFE even in the function form
|
|
129
|
-
// — the value feeds viewProps, a prop bag, and a prop bag reaches the host through `spread`,
|
|
130
|
-
// which is a per-key diff on the SAME element. Nothing is rebuilt, so nothing can be rebuilt
|
|
131
|
-
// mid-gesture.
|
|
132
|
-
const pressStyle = () => {
|
|
133
|
-
const style = local.style;
|
|
134
|
-
if (typeof style !== 'function')
|
|
135
|
-
return style;
|
|
136
|
-
return style({ pressed: pressed() });
|
|
137
|
-
};
|
|
138
|
-
const viewProps = createMemo(() => {
|
|
139
|
-
const disabled = local.disabled;
|
|
140
|
-
const bag = {
|
|
141
|
-
...rest,
|
|
142
|
-
style: pressStyle(),
|
|
143
|
-
accessibilityState: resolveDisabledAccessibilityState(local.accessibilityState, disabled),
|
|
144
|
-
};
|
|
145
|
-
// Forwarded under RN's own key; inert on iOS.
|
|
146
|
-
if (local.android_disableSound !== undefined) {
|
|
147
|
-
bag.android_disableSound = local.android_disableSound;
|
|
148
|
-
}
|
|
149
|
-
// Last, so the synthesized responder listeners win over anything of the same name in `rest`.
|
|
150
|
-
// When disabled the bag is EMPTY — every listener key vanishes, and View's withStableKeys
|
|
151
|
-
// widens it back to `undefined`, which routeProp treats as a delete. Without that widening a
|
|
152
|
-
// Pressable that became disabled would keep its old listeners on the native view forever
|
|
153
|
-
// (.claude/rules/solid-descriptor-bridge.md §1).
|
|
154
|
-
Object.assign(bag, buildPressableListeners(handlers(), {
|
|
155
|
-
disabled,
|
|
156
|
-
cancelable: local.cancelable,
|
|
157
|
-
}));
|
|
158
|
-
return bag;
|
|
159
|
-
});
|
|
160
|
-
// A memo, not a bare `() => ({ pressed: pressed() })`: two leaves reading `state()` in the same
|
|
161
|
-
// press must see the same object, and an unchanged press must not hand out a fresh one.
|
|
162
|
-
const pressState = createMemo(() => ({ pressed: pressed() }));
|
|
163
|
-
// WHY AN ACCESSOR AND NOT `{ pressed: pressed() }`. Solid has no reconciler between what this
|
|
164
|
-
// file returns and the host nodes — `insert` REPLACES a subtree, it never diffs one (the same
|
|
165
|
-
// fact descriptor-to-solid.ts opens with). resolveChildren runs inside View's `insert` render
|
|
166
|
-
// effect, so reading `pressed()` there put the press signal in that effect's dependency set:
|
|
167
|
-
// every touch tore the child subtree down and built a fresh one. On device that rebuild landed
|
|
168
|
-
// between `pressIn` and the native responder grant, the grant was lost, the gesture died, and
|
|
169
|
-
// `pressed` stayed true — the "fires on every other tap, label stuck on its pressed text"
|
|
170
|
-
// report. Vue diffs the slot's vnodes, Svelte patches a Snippet in place, Angular updates a
|
|
171
|
-
// template context; all three reuse the nodes underneath. Solid has no such layer, so the
|
|
172
|
-
// reactivity must cross the boundary as an accessor and only the leaf that reads it re-runs.
|
|
173
|
-
//
|
|
174
|
-
// The call is UNTRACKED and happens once, which is exactly how Solid core treats its own render
|
|
175
|
-
// props: `<For>` runs the map fn under createRoot, and createRoot nulls the Listener. A signal
|
|
176
|
-
// read at the top level of the child function therefore does not re-run it — nest it in the JSX
|
|
177
|
-
// (or a <Show>) like anywhere else in Solid.
|
|
178
|
-
const resolveChildren = () => {
|
|
179
|
-
const children = local.children;
|
|
180
|
-
if (typeof children !== 'function')
|
|
181
|
-
return children;
|
|
182
|
-
// A zero-argument accessor is an ordinary Solid JSX child (JSX.Element covers `() => Element`),
|
|
183
|
-
// not a render prop, and `typeof` cannot tell the two apart — arity can. Handing it back
|
|
184
|
-
// unread lets `insert` wrap it in its own render effect, which keeps it reactive; calling it
|
|
185
|
-
// here, under untrack, would freeze it at its first value.
|
|
186
|
-
if (children.length === 0)
|
|
187
|
-
return () => children(pressState);
|
|
188
|
-
return untrack(() => children(pressState));
|
|
189
|
-
};
|
|
190
|
-
// android_ripple rides a dedicated inner View — Android's ReactViewManager reads the ripple
|
|
191
|
-
// background off a child, not off the responder itself. rippleProps() returns undefined off
|
|
192
|
-
// Android, so the child renders unwrapped, no extra node (RN Pressable.js: "inert on iOS").
|
|
193
|
-
const renderContent = () => {
|
|
194
|
-
const config = local.android_ripple;
|
|
195
|
-
const ripple = config !== undefined ? rippleProps(config) : undefined;
|
|
196
|
-
if (ripple === undefined)
|
|
197
|
-
return resolveChildren();
|
|
198
|
-
dlog('Pressable wrapping children in an android_ripple View');
|
|
199
|
-
return <symbiote-view {...ripple}>{resolveChildren()}</symbiote-view>;
|
|
200
|
-
};
|
|
201
|
-
return (<View ref={attachRef} {...viewProps()}>
|
|
202
|
-
{renderContent()}
|
|
203
|
-
</View>);
|
|
204
|
-
}
|
|
205
|
-
export function Pressable(props) {
|
|
206
|
-
return PressableImpl(props, DEFAULT_MIN_PRESS_DURATION_MS);
|
|
207
|
-
}
|
|
208
|
-
// Relative-only composition seam; the public component barrel exports Pressable, never this.
|
|
209
|
-
export function TouchablePressable(props) {
|
|
210
|
-
return PressableImpl(props, 0);
|
|
211
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// RefreshControl — the Solid lifecycle half, and there is barely any: the component owns one host
|
|
2
|
-
// tag and forwards its props onto it. On iOS `symbiote-refresh-control` resolves to Fabric's
|
|
3
|
-
// PullToRefreshView, which lives INSIDE a ScrollView as a childless sibling before the content
|
|
4
|
-
// container; on Android it is AndroidSwipeRefreshLayout and WRAPS the scroll view instead (an
|
|
5
|
-
// Android ScrollView hosts exactly one child). Which of the two shapes is built is ScrollView's
|
|
6
|
-
// job, not this file's — see scroll-view/shared.tsx's `refreshControlMode`.
|
|
7
|
-
//
|
|
8
|
-
// `refreshing` is a CONTROLLED prop: the parent owns it and pushes it down, and native reports the
|
|
9
|
-
// gesture back through the direct `topRefresh` event. `onRefresh` therefore rides in the forwarded
|
|
10
|
-
// bag untouched — routeProp asks the node's ViewConfig whether `onRefresh` is an event and attaches
|
|
11
|
-
// it as a listener, which is why nothing here inspects the `on` prefix itself
|
|
12
|
-
// (symbiote-engine-core §2).
|
|
13
|
-
//
|
|
14
|
-
// FLAT, not a folder: the file-layout rule gives a module its own folder only when it has real
|
|
15
|
-
// platform (`.ios`/`.android`) or `-shared` variants. There is no JS-side platform branch here —
|
|
16
|
-
// every prop forwards to the native node, which reads what it understands and ignores the rest, so
|
|
17
|
-
// the Android-only spinner props below are harmless on iOS and vice versa.
|
|
18
|
-
import { splitProps } from 'solid-js';
|
|
19
|
-
import { resolveAccessibilityProps, } from '@symbiote-native/components';
|
|
20
|
-
import { dlog } from '@symbiote-native/engine';
|
|
21
|
-
import { withStableKeys } from '../utils/stable-keys.js';
|
|
22
|
-
export function RefreshControl(props) {
|
|
23
|
-
// `children` is pulled out rather than left in the bag: spread skips it anyway, but leaving it in
|
|
24
|
-
// would make the bag accessor re-run whenever the child subtree changed. Same split View makes.
|
|
25
|
-
const [local, rest] = splitProps(props, ['children']);
|
|
26
|
-
// withStableKeys because resolveAccessibilityProps has two branches with DIFFERENT key sets — it
|
|
27
|
-
// returns its input untouched while no aria-* alias holds a value, and a fully-blanked bag once
|
|
28
|
-
// one does. Solid's `spread` walks only the current keys and has no removal pass, so a key that
|
|
29
|
-
// vanishes would keep its last value on the native view forever
|
|
30
|
-
// (.claude/rules/solid-descriptor-bridge.md §1).
|
|
31
|
-
const bag = withStableKeys(() => ({ ...resolveAccessibilityProps(rest) }));
|
|
32
|
-
dlog('RefreshControl -> PullToRefreshView');
|
|
33
|
-
return (<symbiote-refresh-control {...bag()}>
|
|
34
|
-
{local.children}
|
|
35
|
-
</symbiote-refresh-control>);
|
|
36
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { JSX } from '../jsx-runtime';
|
|
2
|
-
import { type IAccessibilityProps, type IAriaProps } from '@symbiote-native/components';
|
|
3
|
-
import { type IClassNameValue, type IStyleProp, type ISymbioteEvent, type IViewStyle } from '@symbiote-native/engine';
|
|
4
|
-
export interface ISafeAreaViewProps extends IAccessibilityProps, IAriaProps {
|
|
5
|
-
style?: IStyleProp<IViewStyle>;
|
|
6
|
-
id?: string;
|
|
7
|
-
class?: IClassNameValue;
|
|
8
|
-
onLayout?: (event: ISymbioteEvent) => void;
|
|
9
|
-
children?: JSX.Element;
|
|
10
|
-
}
|
|
11
|
-
export declare function SafeAreaView(props: ISafeAreaViewProps): JSX.Element;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// SafeAreaView — the native view that insets its children past the notch, the rounded corners and
|
|
2
|
-
// the system bars. There is no JS-side inset math anywhere in this project: the host does it, and
|
|
3
|
-
// every adapter's job is only to put style + children on the `symbiote-safe-area-view` intrinsic.
|
|
4
|
-
//
|
|
5
|
-
// So this is a View twin, and ./view.tsx carries the reasoning they share (why the file is real
|
|
6
|
-
// JSX, why nothing is destructured at setup, why children and ref are pulled out of the bag).
|
|
7
|
-
// The one thing worth repeating: there is no renderSafeAreaView() in @symbiote-native/components
|
|
8
|
-
// and there must not be (.claude/rules/component-render-fn-boundary.md) — `children` is a live
|
|
9
|
-
// Solid subtree that flows through the reconciler and is never reduced back to a Descriptor.
|
|
10
|
-
//
|
|
11
|
-
// The bag goes through withStableKeys because resolveAccessibilityProps has two branches with
|
|
12
|
-
// DIFFERENT key sets, and Solid's `spread` has no removal pass — see utils/stable-keys.ts.
|
|
13
|
-
import { splitProps } from 'solid-js';
|
|
14
|
-
import { resolveAccessibilityProps, } from '@symbiote-native/components';
|
|
15
|
-
import { dlog, } from '@symbiote-native/engine';
|
|
16
|
-
import { withStableKeys } from '../utils/stable-keys.js';
|
|
17
|
-
export function SafeAreaView(props) {
|
|
18
|
-
const [local, rest] = splitProps(props, ['children']);
|
|
19
|
-
dlog('SafeAreaView -> SafeAreaView');
|
|
20
|
-
const bag = withStableKeys(() => ({ ...resolveAccessibilityProps(rest) }));
|
|
21
|
-
return (<symbiote-safe-area-view {...bag()}>
|
|
22
|
-
{local.children}
|
|
23
|
-
</symbiote-safe-area-view>);
|
|
24
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export type { IScrollViewProps, IScrollViewHandle, IScrollViewHostPlatform, IStickyHeaderComponentType, IStickyHeaderComponentProps, } from './shared';
|
|
2
|
-
export declare const ScrollView: (props: import("./shared").IScrollViewProps) => import("@symbiote-native/solid/jsx-runtime").JSX.Element;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// ScrollView on Android. An Android ScrollView accepts only ONE child, so a RefreshControl cannot
|
|
2
|
-
// sit beside the content the way iOS allows ("addViewAt: failed to insert view … at index 1").
|
|
3
|
-
// Instead the RefreshControl (AndroidSwipeRefreshLayout) WRAPS the scroll view, which nests inside
|
|
4
|
-
// it with nestedScrollEnabled so the inner scroll takes the gesture before the refresh parent —
|
|
5
|
-
// RN's own ScrollView.js android branch, and the same fact adapters/react's, adapters/vue's and
|
|
6
|
-
// adapters/svelte's Android bindings encode. Metro picks this file on an Android host; no
|
|
7
|
-
// Platform.OS read.
|
|
8
|
-
import { createScrollView } from './shared.jsx';
|
|
9
|
-
export const ScrollView = createScrollView({
|
|
10
|
-
refreshControlMode: 'wrap',
|
|
11
|
-
});
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export type { IScrollViewProps, IScrollViewHandle, IScrollViewHostPlatform, IStickyHeaderComponentType, IStickyHeaderComponentProps, } from './shared';
|
|
2
|
-
export declare const ScrollView: (props: import("./shared").IScrollViewProps) => import("@symbiote-native/solid/jsx-runtime").JSX.Element;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// ScrollView on iOS. The RefreshControl (PullToRefreshView) is a CHILD of the scroll view, rendered
|
|
2
|
-
// as a childless SIBLING BEFORE the content container — RN ScrollView.js's
|
|
3
|
-
// `{refreshControl}{contentContainer}`, the same fact adapters/react's, adapters/vue's and
|
|
4
|
-
// adapters/svelte's iOS bindings encode. Metro picks this file on an iOS host; no Platform.OS read.
|
|
5
|
-
import { createScrollView } from './shared.jsx';
|
|
6
|
-
export const ScrollView = createScrollView({
|
|
7
|
-
refreshControlMode: 'sibling',
|
|
8
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
// Base / default ScrollView: re-exports the iOS build. Metro overrides this with index.ios.ts /
|
|
2
|
-
// index.android.ts on a real host; under vitest / tsc the resolution lands here. Filename is the
|
|
3
|
-
// selector, no Platform.OS read.
|
|
4
|
-
//
|
|
5
|
-
// ScrollViewStickyHeader is exported from here rather than from the platform files: it has no
|
|
6
|
-
// platform branch of its own, and an app may compose it directly around a section instead of going
|
|
7
|
-
// through `stickyHeaderIndices`.
|
|
8
|
-
export * from './index.ios.js';
|
|
9
|
-
export { ScrollViewStickyHeader } from './sticky-header.jsx';
|