@symbiote-native/vue 0.3.7 → 0.4.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/build/components/flat-list/index.js +18 -34
- package/build/components/image-background.js +4 -14
- package/build/components/image.js +9 -20
- package/build/components/keyboard-avoiding-view.js +10 -19
- package/build/components/modal/index.d.ts +4 -4
- package/build/components/pressable.js +24 -31
- package/build/components/refresh-control.js +12 -18
- package/build/components/scroll-view/index.android.js +21 -33
- package/build/components/scroll-view/shared.js +76 -121
- package/build/components/scroll-view/sticky-header.js +104 -104
- package/build/components/section-list/index.js +12 -19
- package/build/components/switch/shared.js +17 -29
- package/build/components/text-input/index.js +29 -49
- package/build/components/touchable.js +30 -50
- package/build/components/virtualized-list/index.js +174 -273
- package/build/components/virtualized-section-list/index.js +19 -29
- package/build/components.js +1 -3
- package/build/host-instance/index.js +2 -4
- package/build/index.d.ts +2 -1
- package/build/index.js +11 -45
- package/build/modules/animated/create-animated-component.js +34 -97
- package/build/modules/animated/index.js +13 -17
- package/build/modules/app-registry/index.js +7 -11
- package/build/modules/status-bar.js +8 -15
- package/build/renderer/index.js +11 -6
- package/build/runtime-helpers/index.js +16 -26
- package/metro-css-parser.cjs +9 -14
- package/metro-vue-transformer.cjs +112 -135
- package/package.json +4 -4
|
@@ -1,48 +1,34 @@
|
|
|
1
|
-
// ScrollView, the Vue lifecycle half. The Fabric tree is nested:
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// imperative handle. This is the Vue twin of the React adapter's useRef + buildScrollViewHandle.
|
|
1
|
+
// ScrollView, the Vue lifecycle half. The Fabric tree is nested: a scroll view wraps a
|
|
2
|
+
// content view holding the children (RN's ScrollView.js shape). Platform-invariant math
|
|
3
|
+
// (decelerationRate, per-axis intrinsics/base style, content-size dedupe, imperative handle,
|
|
4
|
+
// aria/role fold) lives in @symbiote-native/components, shared with React; Vue supplies only
|
|
5
|
+
// the reactivity - shallowRef host node, setup-scope lastContentSize dedupe, expose() handle -
|
|
6
|
+
// the twin of React's useRef + buildScrollViewHandle.
|
|
8
7
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// forwarding a function prop would reach Fabric and crash Android's folly::dynamic). Scroll events
|
|
13
|
-
// (onScroll/onLayout/…) ARE ViewConfig events, so they forward raw and routeProp turns them into
|
|
14
|
-
// listeners.
|
|
8
|
+
// Attrs are untyped, so every field is narrowed with a runtime guard, never a cast. The legacy
|
|
9
|
+
// onContentSizeChange callback key MUST be consumed if present: it is NOT a ViewConfig event,
|
|
10
|
+
// and forwarding a function prop reaches Fabric and crashes Android's folly::dynamic.
|
|
15
11
|
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// onScroll composition (native attach vs Animated.event) all live here; the per-header component and
|
|
20
|
-
// the children wrap live in scroll-view-sticky-header.ts (the Vue twin of the React file).
|
|
12
|
+
// Sticky headers are real: the scroll AnimatedValue, headerLayoutYs cross-talk map, viewport-
|
|
13
|
+
// height capture, and onScroll composition (native attach vs Animated.event) live here; the
|
|
14
|
+
// per-header component and children wrap live in sticky-header.ts.
|
|
21
15
|
import { defineComponent, h, isVNode, markRaw, onBeforeUnmount, ref, shallowRef, watch, } from '@vue/runtime-core';
|
|
22
|
-
import { attachStickyScroll, buildScrollViewHandle, didContentSizeChange, forwardScrollEvent, readLayoutDimension, resolveAccessibilityProps, resolveDecelerationRate, selectScrollIntrinsics, } from '@symbiote-native/components';
|
|
16
|
+
import { attachStickyScroll, buildScrollViewHandle, didContentSizeChange, forwardScrollEvent, readLayoutDimension, resolveAccessibilityProps, resolveDecelerationRate, resolveScrollForwarding, selectScrollIntrinsics, } from '@symbiote-native/components';
|
|
23
17
|
import { AnimatedValue, dlog, event as animatedEvent, isClassNameValue, isNativeAnimatedAvailable, isSymbioteNode, resolveClassName, } from '@symbiote-native/engine';
|
|
24
18
|
import { wrapStickyHeaders } from './sticky-header.js';
|
|
25
19
|
import { normalizeVueAttrs } from '../../utils/normalize-attrs.js';
|
|
26
20
|
function isHandler(value) {
|
|
27
21
|
return typeof value === 'function';
|
|
28
22
|
}
|
|
29
|
-
// Objects and arrays are valid StyleProp<ViewStyle
|
|
30
|
-
// engine omits RN's RegisteredStyle brand, so no numeric form). Primitives/null degrade to
|
|
31
|
-
// undefined; the engine flattens whatever object/array reaches it.
|
|
23
|
+
// Objects and arrays are valid StyleProp<ViewStyle>; primitives/null degrade to undefined.
|
|
32
24
|
function isStyleProp(value) {
|
|
33
25
|
return typeof value === 'object' && value !== null;
|
|
34
26
|
}
|
|
35
|
-
// `class`
|
|
36
|
-
//
|
|
37
|
-
// the
|
|
38
|
-
//
|
|
39
|
-
// wrapper's layout style, BEFORE that later resolution ever runs, so a class-only layout prop
|
|
40
|
-
// (flex, height, gap, …) never reaches the wrapper and it collapses to nothing.
|
|
41
|
-
// isClassNameProp is @symbiote-native/engine's own isClassNameValue guard (shared, not redeclared —
|
|
42
|
-
// routeProp's centralized class+style merge needs the identical narrowing).
|
|
27
|
+
// `class` forwards raw via forwardAttrs, resolved later by the renderer's patchProp. But the
|
|
28
|
+
// Android RefreshControl wrap (index.android.ts) reads userStyle alone to splitLayoutProps()
|
|
29
|
+
// the outer wrapper BEFORE that later resolution runs, so a class-only layout prop (flex,
|
|
30
|
+
// height, gap, …) would otherwise never reach the wrapper.
|
|
43
31
|
const isClassNameProp = isClassNameValue;
|
|
44
|
-
// decelerationRate is resolved per-platform (resolveDecelerationRate), so it must be narrowed to
|
|
45
|
-
// its declared shape before that call, unlike the raw pass-through props.
|
|
46
32
|
function asDecelerationRate(value) {
|
|
47
33
|
if (typeof value === 'number')
|
|
48
34
|
return value;
|
|
@@ -50,21 +36,16 @@ function asDecelerationRate(value) {
|
|
|
50
36
|
return value;
|
|
51
37
|
return undefined;
|
|
52
38
|
}
|
|
53
|
-
// stickyHeaderIndices arrives untyped (attrs); narrow to a number[] before the wrap reads it.
|
|
54
39
|
function isNumberArray(value) {
|
|
55
40
|
return Array.isArray(value) && value.every(entry => typeof entry === 'number');
|
|
56
41
|
}
|
|
57
|
-
// A StickyHeaderComponent override is a Vue component: a function (functional) or an object
|
|
58
|
-
// (defineComponent). Narrowed before it is handed to wrapStickyHeaders (no cast).
|
|
59
42
|
function isComponent(value) {
|
|
60
43
|
return typeof value === 'function' || (typeof value === 'object' && value !== null);
|
|
61
44
|
}
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
// StickyHeaderComponent) are lifecycle-consumed by the children wrap and must NEVER reach Fabric.
|
|
67
|
-
// style / contentContainerStyle / horizontal / decelerationRate are recomposed.
|
|
45
|
+
// Prop/handler keys the lifecycle consumes itself; everything else forwards onto the scroll-view
|
|
46
|
+
// node. onContentSizeChange is consumed (synthesized from the content onLayout); refreshControl
|
|
47
|
+
// and the sticky-header props are consumed by the platform assemble / children wrap and must
|
|
48
|
+
// NEVER reach Fabric.
|
|
68
49
|
const HANDLED_ATTRS = [
|
|
69
50
|
'style',
|
|
70
51
|
'contentContainerStyle',
|
|
@@ -86,39 +67,29 @@ function forwardAttrs(attrs) {
|
|
|
86
67
|
}
|
|
87
68
|
export function createScrollView(platform) {
|
|
88
69
|
return defineComponent((_props, { slots, attrs: rawAttrs, expose, emit }) => {
|
|
89
|
-
// shallowRef, NOT ref:
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
// miss and every scrollTo/scrollToEnd/flashScrollIndicators silently no-op. This is the
|
|
93
|
-
// same rule as the Switch host node.
|
|
70
|
+
// shallowRef, NOT ref: a plain ref() would run the node through Vue's toReactive(),
|
|
71
|
+
// handing back a Proxy the engine's WeakMap mirror doesn't recognize, so
|
|
72
|
+
// scrollTo/scrollToEnd/flashScrollIndicators would silently no-op. Same rule as Switch.
|
|
94
73
|
const nodeRef = shallowRef(null);
|
|
95
74
|
const setNodeRef = (el) => {
|
|
96
75
|
nodeRef.value = isSymbioteNode(el) ? el : null;
|
|
97
76
|
};
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
// null and every command would no-op. expose() makes it the value a parent ref
|
|
101
|
-
// sees: the Vue twin of React's useImperativeHandle(forwardedRef, buildScrollViewHandle(…)).
|
|
77
|
+
// Lazy getter, not the node captured once: it is null until the element commits, so an
|
|
78
|
+
// eager capture would freeze null. Vue twin of useImperativeHandle(ref, buildScrollViewHandle).
|
|
102
79
|
expose(buildScrollViewHandle(() => nodeRef.value));
|
|
103
|
-
//
|
|
104
|
-
// contentSizeChange: RN fires the content onLayout on every layout pass; only real size
|
|
105
|
-
// changes emit to the user (didContentSizeChange).
|
|
80
|
+
// RN fires the content onLayout on every layout pass; only a real size change emits.
|
|
106
81
|
let lastContentSize = null;
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
// markRaw: it is an engine object, held by IDENTITY, never run through toReactive (the
|
|
111
|
-
// reactivity rule; a deep ref would hand back a Proxy the engine's mirror misses). Allocated
|
|
112
|
-
// unconditionally (like React's unconditional hook); unused when no sticky headers are flagged.
|
|
82
|
+
// Drives every sticky header's translateY (RN's _scrollAnimatedValue). markRaw: an engine
|
|
83
|
+
// object held by identity, never run through toReactive. Allocated unconditionally
|
|
84
|
+
// (unused when no sticky headers are flagged), like React's unconditional hook.
|
|
113
85
|
const scrollAnimatedValue = markRaw(new AnimatedValue(0));
|
|
114
|
-
// Inverted sticky headers stick to the BOTTOM, so they need the viewport height (RN
|
|
115
|
-
//
|
|
86
|
+
// Inverted sticky headers stick to the BOTTOM, so they need the viewport height (RN's
|
|
87
|
+
// _handleLayout). Fed back into the wrapped headers on the next render.
|
|
116
88
|
const viewportHeight = ref(undefined);
|
|
117
|
-
// Sticky-header cross-talk (RN ScrollView.js _headerLayoutYs
|
|
118
|
-
// map
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
// freshly-recorded y forward into the previous header's nextHeaderLayoutY (via nextStickyHeaderY).
|
|
89
|
+
// Sticky-header cross-talk (RN ScrollView.js _headerLayoutYs): a child-index -> measured-y
|
|
90
|
+
// map so each header learns where the NEXT sticky header starts (its push-off collision
|
|
91
|
+
// point). Mutated imperatively from each header's onLayout; the bump ref forces the
|
|
92
|
+
// re-render that feeds the freshly-recorded y into the previous header's nextStickyHeaderY.
|
|
122
93
|
const headerLayoutYs = new Map();
|
|
123
94
|
const bumpHeaderLayout = ref(0);
|
|
124
95
|
const onHeaderLayoutY = (index, y) => {
|
|
@@ -128,13 +99,11 @@ export function createScrollView(platform) {
|
|
|
128
99
|
dlog(`Vue ScrollView sticky-header layoutY index=${index} y=${y}`);
|
|
129
100
|
bumpHeaderLayout.value += 1;
|
|
130
101
|
};
|
|
131
|
-
// Native sticky-scroll attach (RN attachNativeEvent
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
// reads it
|
|
136
|
-
// each frame. flush:'post' so the engine has committed the node before attachStickyScroll reads
|
|
137
|
-
// its Fabric handle. Detached on unmount (and re-detached if the node identity changes).
|
|
102
|
+
// Native sticky-scroll attach (RN attachNativeEvent): when the native module is available,
|
|
103
|
+
// the scroll value is driven on the UI thread so interpolations ride scroll natively (no
|
|
104
|
+
// JS jitter). A plain non-reactive flag set in render; the post-commit watch reads it once
|
|
105
|
+
// the node commits. flush:'post' so the node has a Fabric handle before attachStickyScroll
|
|
106
|
+
// reads it. Detached on unmount and re-detached if the node identity changes.
|
|
138
107
|
let nativeStickyWanted = false;
|
|
139
108
|
let detachStickyScroll;
|
|
140
109
|
watch(() => nodeRef.value, node => {
|
|
@@ -153,24 +122,17 @@ export function createScrollView(platform) {
|
|
|
153
122
|
return () => {
|
|
154
123
|
// Read the bump so a recorded header y re-runs render and feeds nextStickyHeaderY forward.
|
|
155
124
|
void bumpHeaderLayout.value;
|
|
156
|
-
// Fold kebab template props (:content-container-style) to the
|
|
157
|
-
// Vue templates use kebab, but the prop surface (and HANDLED_ATTRS below) is camelCase.
|
|
125
|
+
// Fold kebab template props (:content-container-style) to the camelCase prop surface.
|
|
158
126
|
const attrs = normalizeVueAttrs(rawAttrs);
|
|
159
127
|
const isHorizontal = attrs.horizontal === true;
|
|
160
128
|
const userStyle = isStyleProp(attrs.style) ? attrs.style : undefined;
|
|
161
|
-
// resolveClassName(undefined) is a cheap {} no-op, so this is safe with no class prop too.
|
|
162
129
|
const classProp = isClassNameProp(attrs.class) ? attrs.class : undefined;
|
|
163
130
|
const layoutSplitStyle = [resolveClassName(classProp), userStyle];
|
|
164
|
-
// A class-name string resolves through the same style registry as `class`/`style` above;
|
|
165
|
-
// an object/array is already style-shaped and passes through as-is.
|
|
166
131
|
const contentContainerStyle = typeof attrs.contentContainerStyle === 'string'
|
|
167
132
|
? resolveClassName(attrs.contentContainerStyle)
|
|
168
133
|
: isStyleProp(attrs.contentContainerStyle)
|
|
169
134
|
? attrs.contentContainerStyle
|
|
170
135
|
: undefined;
|
|
171
|
-
// Sticky headers are a pure-JS layer; the native scroll view ignores
|
|
172
|
-
// stickyHeaderIndices, so we wrap the flagged children below and drive their translateY off
|
|
173
|
-
// the scroll offset. invertStickyHeaders narrows to the inverted (stick-to-bottom) branch.
|
|
174
136
|
const stickyHeaderIndices = isNumberArray(attrs.stickyHeaderIndices)
|
|
175
137
|
? attrs.stickyHeaderIndices
|
|
176
138
|
: undefined;
|
|
@@ -180,10 +142,8 @@ export function createScrollView(platform) {
|
|
|
180
142
|
? attrs.StickyHeaderComponent
|
|
181
143
|
: undefined;
|
|
182
144
|
const { scrollViewIntrinsic, contentIntrinsic, scrollViewBaseStyle, contentStyle } = selectScrollIntrinsics(isHorizontal, contentContainerStyle);
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
// only when defined (load-bearing on iOS's RCTScrollView axis, ignored by Android's
|
|
186
|
-
// dedicated manager); decelerationRate is resolved per-platform.
|
|
145
|
+
// RN defaults nested scrolling ON (ScrollView.js `nestedScrollEnabled ?? true`);
|
|
146
|
+
// horizontal forwards only when defined (load-bearing on iOS's RCTScrollView axis).
|
|
187
147
|
const outerProps = {
|
|
188
148
|
...resolveAccessibilityProps(forwardAttrs(attrs)),
|
|
189
149
|
};
|
|
@@ -194,33 +154,38 @@ export function createScrollView(platform) {
|
|
|
194
154
|
const decel = asDecelerationRate(attrs.decelerationRate);
|
|
195
155
|
if (decel !== undefined)
|
|
196
156
|
outerProps.decelerationRate = resolveDecelerationRate(decel);
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
157
|
+
// When sticky headers are active, the offset must reach the AnimatedValue (RN's
|
|
158
|
+
// _scrollAnimatedValueAttachment). forwardAttrs already put the user's onScroll/onLayout/
|
|
159
|
+
// scrollEventThrottle on outerProps; here we override per resolveScrollForwarding's
|
|
160
|
+
// decisions (which path, throttle default, inverted capture).
|
|
200
161
|
const nativeStickyAvailable = hasStickyHeaders && isNativeAnimatedAvailable();
|
|
201
162
|
nativeStickyWanted = nativeStickyAvailable;
|
|
163
|
+
const userThrottle = typeof attrs.scrollEventThrottle === 'number' ? attrs.scrollEventThrottle : undefined;
|
|
164
|
+
const forwarding = resolveScrollForwarding({
|
|
165
|
+
hasStickyHeaders,
|
|
166
|
+
nativeStickyAvailable,
|
|
167
|
+
invertStickyHeaders,
|
|
168
|
+
scrollEventThrottle: userThrottle,
|
|
169
|
+
maintainVisibleContentPosition: attrs.maintainVisibleContentPosition,
|
|
170
|
+
snapToAlignment: attrs.snapToAlignment,
|
|
171
|
+
});
|
|
202
172
|
if (hasStickyHeaders) {
|
|
203
173
|
const userOnScroll = isHandler(attrs.onScroll) ? attrs.onScroll : undefined;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
//
|
|
207
|
-
// post-commit watch above, so onScroll only forwards to the user: zero JS per frame. RN
|
|
208
|
-
// uses throttle 1 when sticky (ScrollView.js:1798); the native driver can afford it. The
|
|
209
|
-
// user onScroll is already on outerProps via forwardAttrs.
|
|
210
|
-
outerProps.scrollEventThrottle = userThrottle ?? 1;
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
// JS fallback (no native module): Animated.event drives the value each frame and forwards
|
|
214
|
-
// the user's handler as the listener passthrough. Correct, but lags a frame under fast
|
|
215
|
-
// scroll (the jitter), which the native path above removes on a real host.
|
|
174
|
+
if (forwarding.mode === 'sticky-js') {
|
|
175
|
+
// JS fallback (no native module): correct, but lags a frame under fast scroll, which
|
|
176
|
+
// the native path removes on a real host.
|
|
216
177
|
outerProps.onScroll = animatedEvent([{ nativeEvent: { contentOffset: { y: scrollAnimatedValue } } }], userOnScroll === undefined
|
|
217
178
|
? undefined
|
|
218
179
|
: { listener: (...args) => forwardScrollEvent(userOnScroll, args) });
|
|
219
|
-
outerProps.scrollEventThrottle = userThrottle ?? 16;
|
|
220
180
|
}
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
if (
|
|
181
|
+
// Native path: the value is driven on the UI thread by the post-commit watch above, so
|
|
182
|
+
// onScroll forwards untouched.
|
|
183
|
+
if (forwarding.scrollEventThrottle !== undefined) {
|
|
184
|
+
outerProps.scrollEventThrottle = forwarding.scrollEventThrottle;
|
|
185
|
+
}
|
|
186
|
+
// Capture the viewport height for inverted sticky headers (RN _handleLayout), then call
|
|
187
|
+
// the user's handler.
|
|
188
|
+
if (forwarding.capturesViewportHeight) {
|
|
224
189
|
const userOnLayout = isHandler(attrs.onLayout) ? attrs.onLayout : undefined;
|
|
225
190
|
outerProps.onLayout = (event) => {
|
|
226
191
|
const height = readLayoutDimension(event, 'height');
|
|
@@ -232,19 +197,15 @@ export function createScrollView(platform) {
|
|
|
232
197
|
}
|
|
233
198
|
}
|
|
234
199
|
dlog(`Vue ScrollView -> ${scrollViewIntrinsic} (horizontal=${isHorizontal} sticky=${hasStickyHeaders})`);
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
// false also preserves the cell views maintainVisibleContentPosition / snapToAlignment
|
|
239
|
-
// anchor against (RN preserveChildren). iOS never flattens; both are no-ops there.
|
|
200
|
+
// `collapsable: false` keeps the layout-only content view as a real native view: Android
|
|
201
|
+
// Fabric view-flattens it away otherwise, hoisting the cells as direct children of the
|
|
202
|
+
// scroll view (which hosts exactly one) - an addViewAt crash. iOS never flattens.
|
|
240
203
|
const contentProps = { style: contentStyle, collapsable: false };
|
|
241
|
-
if (
|
|
242
|
-
attrs.snapToAlignment !== undefined) {
|
|
204
|
+
if (forwarding.collapsableChildren) {
|
|
243
205
|
contentProps.collapsableChildren = false;
|
|
244
206
|
}
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
// the setup-scope lastContentSize).
|
|
207
|
+
// Synthesized from the content view's own onLayout (RN _handleContentOnLayout); emit only
|
|
208
|
+
// on a real size change.
|
|
248
209
|
contentProps.onLayout = (event) => {
|
|
249
210
|
const width = readLayoutDimension(event, 'width');
|
|
250
211
|
const height = readLayoutDimension(event, 'height');
|
|
@@ -256,23 +217,17 @@ export function createScrollView(platform) {
|
|
|
256
217
|
dlog(`Vue ScrollView contentSizeChange ${width}x${height}`);
|
|
257
218
|
emit('contentSizeChange', width, height);
|
|
258
219
|
};
|
|
259
|
-
// Sticky headers are a pure-JS layer (the native scroll view ignores stickyHeaderIndices);
|
|
260
|
-
// wrap the flagged children so they pin to the scroll offset. No-op when none are flagged.
|
|
261
220
|
const slotChildren = slots.default !== undefined ? slots.default() : [];
|
|
262
221
|
const contentChildren = hasStickyHeaders
|
|
263
222
|
? wrapStickyHeaders(slotChildren, stickyHeaderIndices, scrollAnimatedValue, invertStickyHeaders, viewportHeight.value, stickyHeaderComponent, headerLayoutYs, onHeaderLayoutY)
|
|
264
223
|
: slotChildren;
|
|
265
224
|
const content = h(contentIntrinsic, contentProps, contentChildren);
|
|
266
|
-
// Base style UNDER user style so an explicit user value (height, flexDirection) still wins
|
|
267
|
-
// the scroll node carries overflow:'scroll' (frame clipping) + the per-axis flexDirection.
|
|
225
|
+
// Base style UNDER user style so an explicit user value (height, flexDirection) still wins.
|
|
268
226
|
const scrollProps = {
|
|
269
227
|
...outerProps,
|
|
270
228
|
style: [scrollViewBaseStyle, userStyle],
|
|
271
229
|
ref: setNodeRef,
|
|
272
230
|
};
|
|
273
|
-
// refreshControl arrives as a Vue VNode the app passes (h(RefreshControl, …)); narrow it
|
|
274
|
-
// with isVNode (no cast). Stripped from forwardAttrs (it is in HANDLED_ATTRS) so it never
|
|
275
|
-
// reaches the host as a prop: it is lifecycle-consumed by the platform assemble.
|
|
276
231
|
const refreshControl = isVNode(attrs.refreshControl) ? attrs.refreshControl : undefined;
|
|
277
232
|
return platform.assemble({
|
|
278
233
|
scrollViewIntrinsic,
|