@symbiote-native/vue 0.5.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 +28 -13
- package/babel-jsx.cjs +52 -0
- 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.d.ts +3 -3
- package/build/components/flat-list/index.js +2 -2
- package/build/components/image-background-props.d.ts +7 -0
- package/build/components/image-background-props.js +1 -0
- package/build/components/image-props.d.ts +5 -0
- package/build/components/image-props.js +5 -0
- package/build/components/{input-accessory-view.d.ts → input-accessory-view-props.d.ts} +1 -3
- package/build/components/input-accessory-view-props.js +5 -0
- package/build/components/keyboard-avoiding-view.js +3 -3
- package/build/components/modal/index.js +1 -1
- package/build/components/pressable-props.d.ts +29 -0
- package/build/components/pressable-props.js +12 -0
- package/build/components/refresh-control-props.d.ts +16 -0
- package/build/components/refresh-control-props.js +9 -0
- package/build/components/safe-area-view-props.d.ts +6 -0
- package/build/components/safe-area-view-props.js +5 -0
- package/build/components/scroll-view/{shared.d.ts → scroll-view-props.d.ts} +4 -27
- package/build/components/scroll-view/scroll-view-props.js +1 -0
- package/build/components/section-list/index.d.ts +3 -3
- package/build/components/switch/switch-props.d.ts +6 -0
- package/build/components/switch/switch-props.js +1 -0
- package/build/components/text-input/text-input-props.d.ts +5 -0
- package/build/components/text-input/text-input-props.js +1 -0
- package/build/components/text-props.d.ts +24 -0
- package/build/components/text-props.js +6 -0
- package/build/components/touchable-native-feedback-props.d.ts +6 -0
- package/build/components/touchable-native-feedback-props.js +1 -0
- package/build/components/touchable-props.d.ts +15 -0
- package/build/components/touchable-props.js +1 -0
- package/build/components/touchable-without-feedback-props.d.ts +3 -0
- package/build/components/touchable-without-feedback-props.js +1 -0
- package/build/components/view-props.d.ts +29 -0
- package/build/components/view-props.js +5 -0
- package/build/components/virtualized-list/index.d.ts +3 -3
- package/build/components/virtualized-list/index.js +42 -37
- package/build/components/virtualized-section-list/index.d.ts +3 -3
- package/build/composables/use-raw-attrs.d.ts +1 -0
- package/build/composables/use-raw-attrs.js +26 -0
- package/build/descriptor-to-vue.js +2 -2
- package/build/host-instance/index.js +4 -5
- package/build/index.d.ts +25 -27
- package/build/index.js +35 -15
- package/build/intrinsic-elements.d.ts +19 -0
- package/build/intrinsic-elements.js +1 -0
- package/build/jsx-runtime.d.ts +67 -0
- package/build/jsx-runtime.js +1 -0
- package/build/modules/animated/create-animated-component.d.ts +1 -1
- package/build/modules/animated/create-animated-component.js +18 -4
- package/build/modules/animated/index.d.ts +3 -1
- package/build/modules/animated/index.js +20 -19
- package/build/modules/image/index.d.ts +3 -0
- package/build/modules/image/index.js +13 -0
- package/build/register.d.ts +1 -0
- package/build/register.js +15 -0
- package/build/renderer/index.js +112 -12
- package/build/runtime-helpers/index.d.ts +14 -1
- package/build/runtime-helpers/index.js +250 -9
- package/build/utils/normalize-attrs.d.ts +1 -0
- package/build/utils/normalize-attrs.js +21 -15
- package/intrinsic-tags.cjs +52 -0
- package/metro-css-parser.cjs +2 -1
- package/metro-vue-transformer.cjs +83 -13
- package/package.json +33 -8
- package/build/components/activity-indicator/index.android.d.ts +0 -4
- package/build/components/activity-indicator/index.android.js +0 -10
- package/build/components/activity-indicator/index.d.ts +0 -1
- package/build/components/activity-indicator/index.ios.d.ts +0 -4
- 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 -48
- package/build/components/button.d.ts +0 -14
- package/build/components/button.js +0 -59
- package/build/components/image-background.d.ts +0 -9
- package/build/components/image-background.js +0 -103
- package/build/components/image.d.ts +0 -9
- package/build/components/image.js +0 -90
- package/build/components/input-accessory-view.js +0 -41
- package/build/components/pressable.d.ts +0 -58
- package/build/components/pressable.js +0 -214
- package/build/components/refresh-control.d.ts +0 -20
- package/build/components/refresh-control.js +0 -47
- package/build/components/safe-area-view.d.ts +0 -8
- package/build/components/safe-area-view.js +0 -33
- package/build/components/scroll-view/index.android.d.ts +0 -4
- package/build/components/scroll-view/index.android.js +0 -64
- package/build/components/scroll-view/index.d.ts +0 -1
- package/build/components/scroll-view/index.ios.d.ts +0 -4
- package/build/components/scroll-view/index.ios.js +0 -17
- package/build/components/scroll-view/index.js +0 -4
- package/build/components/scroll-view/shared.js +0 -271
- package/build/components/scroll-view/sticky-header.d.ts +0 -7
- package/build/components/scroll-view/sticky-header.js +0 -208
- package/build/components/switch/index.android.d.ts +0 -12
- package/build/components/switch/index.android.js +0 -13
- package/build/components/switch/index.d.ts +0 -2
- package/build/components/switch/index.ios.d.ts +0 -12
- package/build/components/switch/index.ios.js +0 -11
- package/build/components/switch/index.js +0 -5
- package/build/components/switch/shared.d.ts +0 -23
- package/build/components/switch/shared.js +0 -126
- package/build/components/text-input/index.d.ts +0 -24
- package/build/components/text-input/index.js +0 -242
- package/build/components/touchable-native-feedback.d.ts +0 -24
- package/build/components/touchable-native-feedback.js +0 -77
- package/build/components/touchable.d.ts +0 -50
- package/build/components/touchable.js +0 -278
- package/build/components.d.ts +0 -51
- package/build/components.js +0 -29
|
@@ -1,271 +0,0 @@
|
|
|
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.
|
|
7
|
-
//
|
|
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.
|
|
11
|
-
//
|
|
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.
|
|
15
|
-
import { defineComponent, h, isVNode, markRaw, onBeforeUnmount, ref, shallowRef, watch, } from '@vue/runtime-core';
|
|
16
|
-
import { attachStickyScroll, buildScrollViewHandle, didContentSizeChange, forwardScrollEvent, readLayoutDimension, resolveAccessibilityProps, resolveDecelerationRate, resolveScrollForwarding, selectScrollIntrinsics, } from '@symbiote-native/components';
|
|
17
|
-
import { AnimatedValue, dlog, event as animatedEvent, isClassNameValue, isNativeAnimatedAvailable, isSymbioteNode, resolveClassName, } from '@symbiote-native/engine';
|
|
18
|
-
import { wrapStickyHeaders, } from './sticky-header.js';
|
|
19
|
-
import { normalizeVueAttrs } from '../../utils/normalize-attrs.js';
|
|
20
|
-
function isHandler(value) {
|
|
21
|
-
return typeof value === 'function';
|
|
22
|
-
}
|
|
23
|
-
// Objects and arrays are valid StyleProp<ViewStyle>; primitives/null degrade to undefined.
|
|
24
|
-
function isStyleProp(value) {
|
|
25
|
-
return typeof value === 'object' && value !== null;
|
|
26
|
-
}
|
|
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.
|
|
31
|
-
const isClassNameProp = isClassNameValue;
|
|
32
|
-
function asDecelerationRate(value) {
|
|
33
|
-
if (typeof value === 'number')
|
|
34
|
-
return value;
|
|
35
|
-
if (value === 'normal' || value === 'fast')
|
|
36
|
-
return value;
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
function isNumberArray(value) {
|
|
40
|
-
return (Array.isArray(value) && value.every(entry => typeof entry === 'number'));
|
|
41
|
-
}
|
|
42
|
-
function isComponent(value) {
|
|
43
|
-
return (typeof value === 'function' || (typeof value === 'object' && value !== null));
|
|
44
|
-
}
|
|
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.
|
|
49
|
-
const HANDLED_ATTRS = [
|
|
50
|
-
'style',
|
|
51
|
-
'contentContainerStyle',
|
|
52
|
-
'horizontal',
|
|
53
|
-
'decelerationRate',
|
|
54
|
-
'onContentSizeChange',
|
|
55
|
-
'refreshControl',
|
|
56
|
-
'stickyHeaderIndices',
|
|
57
|
-
'invertStickyHeaders',
|
|
58
|
-
'StickyHeaderComponent',
|
|
59
|
-
];
|
|
60
|
-
function forwardAttrs(attrs) {
|
|
61
|
-
const result = {};
|
|
62
|
-
for (const key of Object.keys(attrs)) {
|
|
63
|
-
if (!HANDLED_ATTRS.includes(key))
|
|
64
|
-
result[key] = attrs[key];
|
|
65
|
-
}
|
|
66
|
-
return result;
|
|
67
|
-
}
|
|
68
|
-
export function createScrollView(platform) {
|
|
69
|
-
return defineComponent((_props, { slots, attrs: rawAttrs, expose, emit }) => {
|
|
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.
|
|
73
|
-
const nodeRef = shallowRef(null);
|
|
74
|
-
const setNodeRef = (el) => {
|
|
75
|
-
nodeRef.value = isSymbioteNode(el) ? el : null;
|
|
76
|
-
};
|
|
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).
|
|
79
|
-
expose(buildScrollViewHandle(() => nodeRef.value));
|
|
80
|
-
// RN fires the content onLayout on every layout pass; only a real size change emits.
|
|
81
|
-
let lastContentSize = null;
|
|
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.
|
|
85
|
-
const scrollAnimatedValue = markRaw(new AnimatedValue(0));
|
|
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.
|
|
88
|
-
const viewportHeight = ref(undefined);
|
|
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.
|
|
93
|
-
const headerLayoutYs = new Map();
|
|
94
|
-
const bumpHeaderLayout = ref(0);
|
|
95
|
-
const onHeaderLayoutY = (index, y) => {
|
|
96
|
-
if (headerLayoutYs.get(index) === y)
|
|
97
|
-
return;
|
|
98
|
-
headerLayoutYs.set(index, y);
|
|
99
|
-
dlog(`Vue ScrollView sticky-header layoutY index=${index} y=${y}`);
|
|
100
|
-
bumpHeaderLayout.value += 1;
|
|
101
|
-
};
|
|
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.
|
|
107
|
-
let nativeStickyWanted = false;
|
|
108
|
-
let detachStickyScroll;
|
|
109
|
-
watch(() => nodeRef.value, node => {
|
|
110
|
-
if (detachStickyScroll !== undefined) {
|
|
111
|
-
detachStickyScroll();
|
|
112
|
-
detachStickyScroll = undefined;
|
|
113
|
-
}
|
|
114
|
-
if (!nativeStickyWanted || node === null)
|
|
115
|
-
return;
|
|
116
|
-
detachStickyScroll = attachStickyScroll(node, scrollAnimatedValue);
|
|
117
|
-
}, { flush: 'post' });
|
|
118
|
-
onBeforeUnmount(() => {
|
|
119
|
-
if (detachStickyScroll !== undefined)
|
|
120
|
-
detachStickyScroll();
|
|
121
|
-
});
|
|
122
|
-
return () => {
|
|
123
|
-
// Read the bump so a recorded header y re-runs render and feeds nextStickyHeaderY forward.
|
|
124
|
-
void bumpHeaderLayout.value;
|
|
125
|
-
// Fold kebab template props (:content-container-style) to the camelCase prop surface.
|
|
126
|
-
const attrs = normalizeVueAttrs(rawAttrs);
|
|
127
|
-
const isHorizontal = attrs.horizontal === true;
|
|
128
|
-
const userStyle = isStyleProp(attrs.style) ? attrs.style : undefined;
|
|
129
|
-
const classProp = isClassNameProp(attrs.class)
|
|
130
|
-
? attrs.class
|
|
131
|
-
: undefined;
|
|
132
|
-
const layoutSplitStyle = [
|
|
133
|
-
resolveClassName(classProp),
|
|
134
|
-
userStyle,
|
|
135
|
-
];
|
|
136
|
-
const contentContainerStyle = typeof attrs.contentContainerStyle === 'string'
|
|
137
|
-
? resolveClassName(attrs.contentContainerStyle)
|
|
138
|
-
: isStyleProp(attrs.contentContainerStyle)
|
|
139
|
-
? attrs.contentContainerStyle
|
|
140
|
-
: undefined;
|
|
141
|
-
const stickyHeaderIndices = isNumberArray(attrs.stickyHeaderIndices)
|
|
142
|
-
? attrs.stickyHeaderIndices
|
|
143
|
-
: undefined;
|
|
144
|
-
const hasStickyHeaders = stickyHeaderIndices !== undefined && stickyHeaderIndices.length > 0;
|
|
145
|
-
const invertStickyHeaders = attrs.invertStickyHeaders === true ? true : undefined;
|
|
146
|
-
const stickyHeaderComponent = isComponent(attrs.StickyHeaderComponent)
|
|
147
|
-
? attrs.StickyHeaderComponent
|
|
148
|
-
: undefined;
|
|
149
|
-
const { scrollViewIntrinsic, contentIntrinsic, scrollViewBaseStyle, contentStyle, } = selectScrollIntrinsics(isHorizontal, contentContainerStyle);
|
|
150
|
-
// RN defaults nested scrolling ON (ScrollView.js `nestedScrollEnabled ?? true`);
|
|
151
|
-
// horizontal forwards only when defined (load-bearing on iOS's RCTScrollView axis).
|
|
152
|
-
const outerProps = {
|
|
153
|
-
...resolveAccessibilityProps(forwardAttrs(attrs)),
|
|
154
|
-
};
|
|
155
|
-
outerProps.nestedScrollEnabled =
|
|
156
|
-
typeof attrs.nestedScrollEnabled === 'boolean'
|
|
157
|
-
? attrs.nestedScrollEnabled
|
|
158
|
-
: true;
|
|
159
|
-
if (attrs.horizontal !== undefined)
|
|
160
|
-
outerProps.horizontal = attrs.horizontal;
|
|
161
|
-
const decel = asDecelerationRate(attrs.decelerationRate);
|
|
162
|
-
if (decel !== undefined)
|
|
163
|
-
outerProps.decelerationRate = resolveDecelerationRate(decel);
|
|
164
|
-
// When sticky headers are active, the offset must reach the AnimatedValue (RN's
|
|
165
|
-
// _scrollAnimatedValueAttachment). forwardAttrs already put the user's onScroll/onLayout/
|
|
166
|
-
// scrollEventThrottle on outerProps; here we override per resolveScrollForwarding's
|
|
167
|
-
// decisions (which path, throttle default, inverted capture).
|
|
168
|
-
const nativeStickyAvailable = hasStickyHeaders && isNativeAnimatedAvailable();
|
|
169
|
-
nativeStickyWanted = nativeStickyAvailable;
|
|
170
|
-
const userThrottle = typeof attrs.scrollEventThrottle === 'number'
|
|
171
|
-
? attrs.scrollEventThrottle
|
|
172
|
-
: undefined;
|
|
173
|
-
const forwarding = resolveScrollForwarding({
|
|
174
|
-
hasStickyHeaders,
|
|
175
|
-
nativeStickyAvailable,
|
|
176
|
-
invertStickyHeaders,
|
|
177
|
-
scrollEventThrottle: userThrottle,
|
|
178
|
-
maintainVisibleContentPosition: attrs.maintainVisibleContentPosition,
|
|
179
|
-
snapToAlignment: attrs.snapToAlignment,
|
|
180
|
-
});
|
|
181
|
-
if (hasStickyHeaders) {
|
|
182
|
-
const userOnScroll = isHandler(attrs.onScroll)
|
|
183
|
-
? attrs.onScroll
|
|
184
|
-
: undefined;
|
|
185
|
-
if (forwarding.mode === 'sticky-js') {
|
|
186
|
-
// JS fallback (no native module): correct, but lags a frame under fast scroll, which
|
|
187
|
-
// the native path removes on a real host.
|
|
188
|
-
outerProps.onScroll = animatedEvent([{ nativeEvent: { contentOffset: { y: scrollAnimatedValue } } }], userOnScroll === undefined
|
|
189
|
-
? undefined
|
|
190
|
-
: {
|
|
191
|
-
listener: (...args) => forwardScrollEvent(userOnScroll, args),
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
// Native path: the value is driven on the UI thread by the post-commit watch above, so
|
|
195
|
-
// onScroll forwards untouched.
|
|
196
|
-
if (forwarding.scrollEventThrottle !== undefined) {
|
|
197
|
-
outerProps.scrollEventThrottle = forwarding.scrollEventThrottle;
|
|
198
|
-
}
|
|
199
|
-
// Capture the viewport height for inverted sticky headers (RN _handleLayout), then call
|
|
200
|
-
// the user's handler.
|
|
201
|
-
if (forwarding.capturesViewportHeight) {
|
|
202
|
-
const userOnLayout = isHandler(attrs.onLayout)
|
|
203
|
-
? attrs.onLayout
|
|
204
|
-
: undefined;
|
|
205
|
-
outerProps.onLayout = (event) => {
|
|
206
|
-
const height = readLayoutDimension(event, 'height');
|
|
207
|
-
if (height !== undefined)
|
|
208
|
-
viewportHeight.value = height;
|
|
209
|
-
if (userOnLayout !== undefined)
|
|
210
|
-
userOnLayout(event);
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
dlog(`Vue ScrollView -> ${scrollViewIntrinsic} (horizontal=${isHorizontal} sticky=${hasStickyHeaders})`);
|
|
215
|
-
// `collapsable: false` keeps the layout-only content view as a real native view: Android
|
|
216
|
-
// Fabric view-flattens it away otherwise, hoisting the cells as direct children of the
|
|
217
|
-
// scroll view (which hosts exactly one) - an addViewAt crash. iOS never flattens.
|
|
218
|
-
const contentProps = {
|
|
219
|
-
style: contentStyle,
|
|
220
|
-
collapsable: false,
|
|
221
|
-
};
|
|
222
|
-
if (forwarding.collapsableChildren) {
|
|
223
|
-
contentProps.collapsableChildren = false;
|
|
224
|
-
}
|
|
225
|
-
// Synthesized from the content view's own onLayout (RN _handleContentOnLayout); emit only
|
|
226
|
-
// on a real size change.
|
|
227
|
-
contentProps.onLayout = (event) => {
|
|
228
|
-
const width = readLayoutDimension(event, 'width');
|
|
229
|
-
const height = readLayoutDimension(event, 'height');
|
|
230
|
-
if (width === undefined || height === undefined)
|
|
231
|
-
return;
|
|
232
|
-
if (!didContentSizeChange(lastContentSize, { width, height }))
|
|
233
|
-
return;
|
|
234
|
-
lastContentSize = { width, height };
|
|
235
|
-
dlog(`Vue ScrollView contentSizeChange ${width}x${height}`);
|
|
236
|
-
emit('contentSizeChange', width, height);
|
|
237
|
-
};
|
|
238
|
-
const slotChildren = slots.default !== undefined ? slots.default() : [];
|
|
239
|
-
const contentChildren = hasStickyHeaders
|
|
240
|
-
? wrapStickyHeaders(slotChildren, stickyHeaderIndices, scrollAnimatedValue, invertStickyHeaders, viewportHeight.value, stickyHeaderComponent, headerLayoutYs, onHeaderLayoutY)
|
|
241
|
-
: slotChildren;
|
|
242
|
-
const content = h(contentIntrinsic, contentProps, contentChildren);
|
|
243
|
-
// Base style UNDER user style so an explicit user value (height, flexDirection) still wins.
|
|
244
|
-
const scrollProps = {
|
|
245
|
-
...outerProps,
|
|
246
|
-
style: [scrollViewBaseStyle, userStyle],
|
|
247
|
-
ref: setNodeRef,
|
|
248
|
-
};
|
|
249
|
-
const refreshControl = isVNode(attrs.refreshControl)
|
|
250
|
-
? attrs.refreshControl
|
|
251
|
-
: undefined;
|
|
252
|
-
return platform.assemble({
|
|
253
|
-
scrollViewIntrinsic,
|
|
254
|
-
scrollProps,
|
|
255
|
-
content,
|
|
256
|
-
refreshControl,
|
|
257
|
-
scrollViewBaseStyle,
|
|
258
|
-
userStyle,
|
|
259
|
-
layoutSplitStyle,
|
|
260
|
-
scrollOuterProps: outerProps,
|
|
261
|
-
setNodeRef,
|
|
262
|
-
});
|
|
263
|
-
};
|
|
264
|
-
}, {
|
|
265
|
-
name: 'ScrollView',
|
|
266
|
-
inheritAttrs: false,
|
|
267
|
-
emits: {
|
|
268
|
-
contentSizeChange: (_width, _height) => true,
|
|
269
|
-
},
|
|
270
|
-
});
|
|
271
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type Component, type VNode } from '@vue/runtime-core';
|
|
2
|
-
import { AnimatedValue } from '@symbiote-native/engine';
|
|
3
|
-
export type IStickyHeaderComponentType = Component;
|
|
4
|
-
export declare const ScrollViewStickyHeader: import("vue").DefineComponent<{}, () => VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
5
|
-
[key: string]: any;
|
|
6
|
-
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
7
|
-
export declare function wrapStickyHeaders(children: VNode[], stickyHeaderIndices: number[] | undefined, scrollAnimatedValue: AnimatedValue, invertStickyHeaders: boolean | undefined, scrollViewHeight: number | undefined, StickyHeaderComponent: IStickyHeaderComponentType | undefined, headerLayoutYs: ReadonlyMap<number, number>, onHeaderLayoutY: (index: number, y: number) => void): VNode[];
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
// Sticky headers: the Vue twin of adapters/react/src/scroll-view-sticky-header.tsx, the JS layer
|
|
2
|
-
// RN implements in ScrollView.js / ScrollViewStickyHeader.js.
|
|
3
|
-
//
|
|
4
|
-
// RN does stickiness PURELY IN JS: ScrollView.js wraps each flagged child in a
|
|
5
|
-
// ScrollViewStickyHeader fed by a single _scrollAnimatedValue that Animated.event drives from
|
|
6
|
-
// onScroll. The native scroll view does NOT honor the index array itself, so forwarding
|
|
7
|
-
// stickyHeaderIndices to native is a silent no-op - we replicate the JS layer instead. The
|
|
8
|
-
// interpolation math (non-inverted + inverted) lives framework-agnostic in
|
|
9
|
-
// @symbiote-native/components (computeStickyInterpolation); this file holds the Vue component
|
|
10
|
-
// shell, layout state, and child-wrapping. Vue supplies only the reactive lifecycle
|
|
11
|
-
// (refs/watch instead of useState/useEffect).
|
|
12
|
-
import { defineComponent, h, isVNode, onBeforeUnmount, ref, shallowRef, watchEffect, markRaw, } from '@vue/runtime-core';
|
|
13
|
-
import { AnimatedValue, AnimatedInterpolation, Platform, dlog, } from '@symbiote-native/engine';
|
|
14
|
-
import { createInitialStickyState, nextStickyHeaderY, readLayoutNumber, reduceSticky, STICKY_HEADER_Z_INDEX, } from '@symbiote-native/components';
|
|
15
|
-
import { Animated } from '../../modules/animated/index.js';
|
|
16
|
-
function isHandler(value) {
|
|
17
|
-
return typeof value === 'function';
|
|
18
|
-
}
|
|
19
|
-
function isRecord(value) {
|
|
20
|
-
return typeof value === 'object' && value !== null;
|
|
21
|
-
}
|
|
22
|
-
function isAnimatedValue(value) {
|
|
23
|
-
return value instanceof AnimatedValue;
|
|
24
|
-
}
|
|
25
|
-
// So the sticky wrapper can forward layout to the child (RN calls the child's onLayout after its own).
|
|
26
|
-
function readChildOnLayout(child) {
|
|
27
|
-
if (!isRecord(child.props))
|
|
28
|
-
return undefined;
|
|
29
|
-
const handler = child.props.onLayout;
|
|
30
|
-
return isHandler(handler) ? handler : undefined;
|
|
31
|
-
}
|
|
32
|
-
// One sticky header. Measures its own y/height via onLayout, interpolates the shared scroll
|
|
33
|
-
// offset into a translateY pinning it to the top (or bottom, inverted) until the next header
|
|
34
|
-
// collides with it, driven through the native driver when available (no JS jitter). Ported from
|
|
35
|
-
// ScrollViewStickyHeader.js, including the Fabric ShadowTree debounce path. inheritAttrs:false
|
|
36
|
-
// so the IStickyHeaderProps inputs never fall through onto Animated.View and reach Fabric as
|
|
37
|
-
// props (scrollAnimatedValue on a host node would crash Android's folly::dynamic).
|
|
38
|
-
export const ScrollViewStickyHeader = defineComponent({
|
|
39
|
-
name: 'ScrollViewStickyHeader',
|
|
40
|
-
inheritAttrs: false,
|
|
41
|
-
setup(_props, { attrs, slots }) {
|
|
42
|
-
// Read once - stable across renders (the same markRaw'd AnimatedValue). Held by IDENTITY, never
|
|
43
|
-
// run through toReactive. A fresh fallback keeps working if wrapStickyHeaders ever fails to supply it.
|
|
44
|
-
const scrollAnimatedValue = isAnimatedValue(attrs.scrollAnimatedValue)
|
|
45
|
-
? attrs.scrollAnimatedValue
|
|
46
|
-
: markRaw(new AnimatedValue(0));
|
|
47
|
-
// Mutated in place by reduceSticky. A plain object, not a ref: render reads it gated by the
|
|
48
|
-
// reactive `version` bump + `animatedTranslateY` below. DECISIONS (zero-swallow gate,
|
|
49
|
-
// debounce delay, rebuild ranges) all live in reduceSticky.
|
|
50
|
-
const state = createInitialStickyState();
|
|
51
|
-
// Bumped when the reducer commits a new translateY, forcing render to re-read state.translateY.
|
|
52
|
-
const version = ref(0);
|
|
53
|
-
// Engine node -> shallowRef (identity rule); the un-measured identity stub until the
|
|
54
|
-
// rebuild-interpolation effect below replaces it.
|
|
55
|
-
const animatedTranslateY = shallowRef(scrollAnimatedValue.interpolate({
|
|
56
|
-
inputRange: [-1, 0],
|
|
57
|
-
outputRange: [0, 0],
|
|
58
|
-
}));
|
|
59
|
-
let debounceTimer;
|
|
60
|
-
// Held so the next rebuild detaches the old listener and onBeforeUnmount cleans up.
|
|
61
|
-
let interpolation;
|
|
62
|
-
let listenerId;
|
|
63
|
-
const inputs = () => ({
|
|
64
|
-
os: Platform.OS,
|
|
65
|
-
inverted: typeof attrs.inverted === 'boolean' ? attrs.inverted : undefined,
|
|
66
|
-
scrollViewHeight: typeof attrs.scrollViewHeight === 'number'
|
|
67
|
-
? attrs.scrollViewHeight
|
|
68
|
-
: undefined,
|
|
69
|
-
nextHeaderLayoutY: typeof attrs.nextHeaderLayoutY === 'number'
|
|
70
|
-
? attrs.nextHeaderLayoutY
|
|
71
|
-
: undefined,
|
|
72
|
-
});
|
|
73
|
-
const runEffects = (effects) => {
|
|
74
|
-
for (const effect of effects) {
|
|
75
|
-
switch (effect.kind) {
|
|
76
|
-
case 'rebuild-interpolation': {
|
|
77
|
-
// Detach the old listener, build a fresh interpolation, and wire the settled-value listener.
|
|
78
|
-
if (interpolation !== undefined && listenerId !== undefined) {
|
|
79
|
-
interpolation.removeListener(listenerId);
|
|
80
|
-
listenerId = undefined;
|
|
81
|
-
}
|
|
82
|
-
const next = scrollAnimatedValue.interpolate({
|
|
83
|
-
inputRange: effect.inputRange,
|
|
84
|
-
outputRange: effect.outputRange,
|
|
85
|
-
});
|
|
86
|
-
listenerId = next.addListener(({ value }) => {
|
|
87
|
-
if (typeof value === 'number')
|
|
88
|
-
dispatch({ kind: 'animated-tick', value });
|
|
89
|
-
});
|
|
90
|
-
interpolation = next;
|
|
91
|
-
animatedTranslateY.value = next;
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
case 'schedule-debounce':
|
|
95
|
-
// The animated value updates several times per frame; debounce the settled value so
|
|
96
|
-
// hit detection stays current (a Fabric issue, worse on Android).
|
|
97
|
-
if (debounceTimer !== undefined)
|
|
98
|
-
clearTimeout(debounceTimer);
|
|
99
|
-
debounceTimer = setTimeout(() => {
|
|
100
|
-
debounceTimer = undefined;
|
|
101
|
-
dispatch({ kind: 'debounce-fired', value: effect.value });
|
|
102
|
-
}, effect.delay);
|
|
103
|
-
break;
|
|
104
|
-
case 'apply-passthrough':
|
|
105
|
-
version.value += 1;
|
|
106
|
-
break;
|
|
107
|
-
case 'record-header-y':
|
|
108
|
-
// Vue records through attrs.onLayout (the wrapper closure); the reducer emits no index for it.
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
const dispatch = (action) => {
|
|
114
|
-
runEffects(reduceSticky(state, action, inputs()).effects);
|
|
115
|
-
};
|
|
116
|
-
// Rebuild whenever the collision/viewport inputs change ([inverted, scrollViewHeight,
|
|
117
|
-
// nextHeaderLayoutY]); also runs once on mount.
|
|
118
|
-
watchEffect(() => {
|
|
119
|
-
dispatch({ kind: 'inputs-changed' });
|
|
120
|
-
});
|
|
121
|
-
onBeforeUnmount(() => {
|
|
122
|
-
if (interpolation !== undefined && listenerId !== undefined) {
|
|
123
|
-
interpolation.removeListener(listenerId);
|
|
124
|
-
}
|
|
125
|
-
if (debounceTimer !== undefined)
|
|
126
|
-
clearTimeout(debounceTimer);
|
|
127
|
-
});
|
|
128
|
-
// Dispatch the layout, fire the wrapper's recorder (onHeaderLayoutY via attrs.onLayout), then
|
|
129
|
-
// the child's own onLayout. Matches RN ScrollViewStickyHeader.js._onLayout.
|
|
130
|
-
const onLayout = (event) => {
|
|
131
|
-
const y = readLayoutNumber(event, 'y');
|
|
132
|
-
const height = readLayoutNumber(event, 'height');
|
|
133
|
-
// Keep the previous value when a field is absent (RN sets state only on a defined read).
|
|
134
|
-
dispatch({
|
|
135
|
-
kind: 'layout',
|
|
136
|
-
y: y ?? state.layoutY,
|
|
137
|
-
height: height ?? state.layoutHeight,
|
|
138
|
-
});
|
|
139
|
-
const recorder = attrs.onLayout;
|
|
140
|
-
if (isHandler(recorder))
|
|
141
|
-
recorder(event);
|
|
142
|
-
const children = slots.default !== undefined ? slots.default() : [];
|
|
143
|
-
const child = children[0];
|
|
144
|
-
const childOnLayout = isVNode(child)
|
|
145
|
-
? readChildOnLayout(child)
|
|
146
|
-
: undefined;
|
|
147
|
-
if (childOnLayout !== undefined)
|
|
148
|
-
childOnLayout(event);
|
|
149
|
-
};
|
|
150
|
-
return () => {
|
|
151
|
-
// Read the version bump so a committed translateY re-runs render.
|
|
152
|
-
void version.value;
|
|
153
|
-
// The EXPLICIT debounced translateY overrides the committed transform for hit-testing, while
|
|
154
|
-
// animatedTranslateY does the smooth (native-driven) pin.
|
|
155
|
-
const passthroughAnimatedPropExplicitValues = state.translateY !== null
|
|
156
|
-
? { style: { transform: [{ translateY: state.translateY }] } }
|
|
157
|
-
: null;
|
|
158
|
-
// collapsable:false keeps the wrapper a real Yoga node; zIndex makes the pinned header paint
|
|
159
|
-
// OVER the rows scrolling under it.
|
|
160
|
-
return h(Animated.View, {
|
|
161
|
-
style: {
|
|
162
|
-
transform: [{ translateY: animatedTranslateY.value }],
|
|
163
|
-
zIndex: STICKY_HEADER_Z_INDEX,
|
|
164
|
-
},
|
|
165
|
-
onLayout,
|
|
166
|
-
collapsable: false,
|
|
167
|
-
passthroughAnimatedPropExplicitValues,
|
|
168
|
-
}, { default: () => (slots.default !== undefined ? slots.default() : []) });
|
|
169
|
-
};
|
|
170
|
-
},
|
|
171
|
-
});
|
|
172
|
-
// Wrap each child flagged by `stickyHeaderIndices` in the sticky header component, fed by the
|
|
173
|
-
// shared scroll AnimatedValue. Returns the children unchanged when no indices are flagged.
|
|
174
|
-
//
|
|
175
|
-
// Cross-talk plumbing (RN's _headerLayoutYs + _onStickyHeaderLayout): headerLayoutYs is a
|
|
176
|
-
// child-index -> measured-y map; each header reports its own y through onHeaderLayoutY, and we
|
|
177
|
-
// feed every header the y of the NEXT flagged header (the collision point past which it scrolls
|
|
178
|
-
// off). The LAST flagged header has no successor, so its nextHeaderLayoutY stays undefined and
|
|
179
|
-
// it sticks indefinitely (correct).
|
|
180
|
-
export function wrapStickyHeaders(children, stickyHeaderIndices, scrollAnimatedValue, invertStickyHeaders, scrollViewHeight, StickyHeaderComponent, headerLayoutYs, onHeaderLayoutY) {
|
|
181
|
-
if (stickyHeaderIndices === undefined || stickyHeaderIndices.length === 0)
|
|
182
|
-
return children;
|
|
183
|
-
const Wrapper = StickyHeaderComponent ?? ScrollViewStickyHeader;
|
|
184
|
-
return children.map((child, index) => {
|
|
185
|
-
const indexOfIndex = stickyHeaderIndices.indexOf(index);
|
|
186
|
-
if (indexOfIndex === -1 || !isVNode(child))
|
|
187
|
-
return child;
|
|
188
|
-
// The next flagged header's measured y. undefined until that header has measured (or for the last).
|
|
189
|
-
const nextIndex = stickyHeaderIndices[indexOfIndex + 1];
|
|
190
|
-
const nextHeaderLayoutY = nextStickyHeaderY(stickyHeaderIndices, indexOfIndex, headerLayoutYs);
|
|
191
|
-
dlog(`Vue ScrollView sticky-header wrap index=${index} next=${nextIndex} nextY=${nextHeaderLayoutY}`);
|
|
192
|
-
const props = {
|
|
193
|
-
key: child.key ?? `sticky-${index}`,
|
|
194
|
-
nextHeaderLayoutY,
|
|
195
|
-
// Record this header's own y into the parent map; the lookup above feeds it forward to the
|
|
196
|
-
// previous header on the resulting re-render (the headerLayoutYs bump in scroll-view-shared).
|
|
197
|
-
onLayout: (event) => {
|
|
198
|
-
const y = readLayoutNumber(event, 'y');
|
|
199
|
-
if (y !== undefined)
|
|
200
|
-
onHeaderLayoutY(index, y);
|
|
201
|
-
},
|
|
202
|
-
scrollAnimatedValue,
|
|
203
|
-
inverted: invertStickyHeaders,
|
|
204
|
-
scrollViewHeight,
|
|
205
|
-
};
|
|
206
|
-
return h(Wrapper, props, { default: () => [child] });
|
|
207
|
-
});
|
|
208
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export declare const Switch: import("vue").DefineSetupFnComponent<import("./shared").ISwitchProps, {
|
|
2
|
-
valueChange: (value: boolean, event: import("@symbiote-native/engine").ISymbioteEvent) => boolean;
|
|
3
|
-
'update:modelValue': (value: boolean) => boolean;
|
|
4
|
-
'update:value': (value: boolean) => boolean;
|
|
5
|
-
}, {}, Omit<import("@symbiote-native/components").ISwitchProps, "onValueChange"> & {
|
|
6
|
-
modelValue?: boolean;
|
|
7
|
-
class?: import("@symbiote-native/engine").IClassNameValue;
|
|
8
|
-
} & {
|
|
9
|
-
"onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
|
|
10
|
-
"onUpdate:value"?: ((value: boolean) => any) | undefined;
|
|
11
|
-
onValueChange?: ((value: boolean, event: import("@symbiote-native/engine").ISymbioteEvent) => any) | undefined;
|
|
12
|
-
}, import("vue").PublicProps>;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// Switch on Android: AndroidSwitch takes trackColorForTrue / trackColorForFalse plus
|
|
2
|
-
// trackTintColor (the color for the CURRENT value), and snaps native back via the
|
|
3
|
-
// `setNativeValue` command. Metro picks this on an Android host; no Platform.OS read.
|
|
4
|
-
// Mirrors the React adapter's Android binding.
|
|
5
|
-
import { createSwitch } from './shared.js';
|
|
6
|
-
export const Switch = createSwitch({
|
|
7
|
-
snapBackCommand: 'setNativeValue',
|
|
8
|
-
trackColorProps: (value, trackColor) => ({
|
|
9
|
-
trackColorForFalse: trackColor?.false,
|
|
10
|
-
trackColorForTrue: trackColor?.true,
|
|
11
|
-
trackTintColor: value ? trackColor?.true : trackColor?.false,
|
|
12
|
-
}),
|
|
13
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export declare const Switch: import("vue").DefineSetupFnComponent<import("./shared").ISwitchProps, {
|
|
2
|
-
valueChange: (value: boolean, event: import("@symbiote-native/engine").ISymbioteEvent) => boolean;
|
|
3
|
-
'update:modelValue': (value: boolean) => boolean;
|
|
4
|
-
'update:value': (value: boolean) => boolean;
|
|
5
|
-
}, {}, Omit<import("@symbiote-native/components").ISwitchProps, "onValueChange"> & {
|
|
6
|
-
modelValue?: boolean;
|
|
7
|
-
class?: import("@symbiote-native/engine").IClassNameValue;
|
|
8
|
-
} & {
|
|
9
|
-
"onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
|
|
10
|
-
"onUpdate:value"?: ((value: boolean) => any) | undefined;
|
|
11
|
-
onValueChange?: ((value: boolean, event: import("@symbiote-native/engine").ISymbioteEvent) => any) | undefined;
|
|
12
|
-
}, import("vue").PublicProps>;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// Switch on iOS: the native Switch takes onTintColor (ON-track) / tintColor (OFF-track) and
|
|
2
|
-
// snaps native back via the `setValue` command. Also the base (switch.ts re-exports it) for
|
|
3
|
-
// headless / web. Mirrors the React adapter's iOS binding.
|
|
4
|
-
import { createSwitch } from './shared.js';
|
|
5
|
-
export const Switch = createSwitch({
|
|
6
|
-
snapBackCommand: 'setValue',
|
|
7
|
-
trackColorProps: (_value, trackColor) => ({
|
|
8
|
-
onTintColor: trackColor?.true,
|
|
9
|
-
tintColor: trackColor?.false,
|
|
10
|
-
}),
|
|
11
|
-
});
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// Base / default Switch. Re-exports the iOS build. Metro overrides this with switch.ios.ts /
|
|
2
|
-
// switch.android.ts on a real host; under tsx / tsc / web the host resolves here. Filename is
|
|
3
|
-
// the selector, no Platform.OS read. Switch is the first component to bring the
|
|
4
|
-
// state half (the reducer) into the Vue adapter: render-and-state, not render-only.
|
|
5
|
-
export * from './index.ios.js';
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { type ISwitchPlatform, type ISwitchProps as ISwitchBaseProps, type ISwitchTrackColor } from '@symbiote-native/components';
|
|
2
|
-
import { type IClassNameValue, type ISymbioteEvent } from '@symbiote-native/engine';
|
|
3
|
-
type ISwitchHostPlatform = ISwitchPlatform & {
|
|
4
|
-
snapBackCommand: string;
|
|
5
|
-
};
|
|
6
|
-
export type ISwitchProps = Omit<ISwitchBaseProps, 'onValueChange'> & {
|
|
7
|
-
modelValue?: boolean;
|
|
8
|
-
class?: IClassNameValue;
|
|
9
|
-
};
|
|
10
|
-
export type { ISwitchTrackColor };
|
|
11
|
-
type ISwitchEmits = {
|
|
12
|
-
valueChange: (value: boolean, event: ISymbioteEvent) => boolean;
|
|
13
|
-
'update:modelValue': (value: boolean) => boolean;
|
|
14
|
-
'update:value': (value: boolean) => boolean;
|
|
15
|
-
};
|
|
16
|
-
export declare function createSwitch(platform: ISwitchHostPlatform): import("vue").DefineSetupFnComponent<ISwitchProps, ISwitchEmits, {}, Omit<ISwitchBaseProps, "onValueChange"> & {
|
|
17
|
-
modelValue?: boolean;
|
|
18
|
-
class?: IClassNameValue;
|
|
19
|
-
} & {
|
|
20
|
-
"onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
|
|
21
|
-
"onUpdate:value"?: ((value: boolean) => any) | undefined;
|
|
22
|
-
onValueChange?: ((value: boolean, event: ISymbioteEvent) => any) | undefined;
|
|
23
|
-
}, import("vue").PublicProps>;
|