@symbiote-native/solid 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.
- package/README.md +159 -86
- 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 +4 -4
- 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 +15 -6
- package/build/components/modal.jsx +6 -6
- package/build/components/{pressable.d.ts → pressable-props.d.ts} +10 -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 +9 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback-props.js +1 -0
- package/build/components/touchable-props.d.ts +20 -0
- package/build/components/touchable-props.js +1 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +7 -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 +95 -102
- 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 +3 -3
- package/build/descriptor-to-solid.js +1 -1
- package/build/host-instance.js +2 -2
- 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 +26 -23
- 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 +74 -61
- 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 +6 -11
- 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,14 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { JSX } from '
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
1
|
+
import type { Ref } from 'solid-js';
|
|
2
|
+
import type { JSX } from '../jsx-runtime';
|
|
3
|
+
import type { ISymbioteEvent } from '@symbiote-native/engine';
|
|
4
|
+
import type { IAccessibilityProps, IAriaProps } from '@symbiote-native/components';
|
|
5
|
+
import type { IClassNameValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
6
|
+
import type { IHostInstance } from '../host-instance';
|
|
6
7
|
export type { IScrollViewHandle } from '@symbiote-native/components';
|
|
7
|
-
export type { IStickyHeaderComponentType, IStickyHeaderComponentProps, } from './sticky-header';
|
|
8
8
|
type IScrollHandler = (event: ISymbioteEvent) => void;
|
|
9
|
-
export type IScrollViewHostPlatform = {
|
|
10
|
-
refreshControlMode: 'sibling' | 'wrap';
|
|
11
|
-
};
|
|
12
9
|
export interface IScrollViewProps extends IAccessibilityProps, IAriaProps {
|
|
13
10
|
style?: IStyleProp<IViewStyle>;
|
|
14
11
|
class?: IClassNameValue;
|
|
@@ -31,7 +28,6 @@ export interface IScrollViewProps extends IAccessibilityProps, IAriaProps {
|
|
|
31
28
|
x: number;
|
|
32
29
|
y: number;
|
|
33
30
|
};
|
|
34
|
-
refreshControl?: JSX.Element;
|
|
35
31
|
removeClippedSubviews?: boolean;
|
|
36
32
|
onContentSizeChange?: (width: number, height: number) => void;
|
|
37
33
|
snapToInterval?: number;
|
|
@@ -42,7 +38,6 @@ export interface IScrollViewProps extends IAccessibilityProps, IAriaProps {
|
|
|
42
38
|
disableIntervalMomentum?: boolean;
|
|
43
39
|
stickyHeaderIndices?: number[];
|
|
44
40
|
invertStickyHeaders?: boolean;
|
|
45
|
-
StickyHeaderComponent?: IStickyHeaderComponentType;
|
|
46
41
|
keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
|
|
47
42
|
keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
|
|
48
43
|
maintainVisibleContentPosition?: {
|
|
@@ -79,7 +74,6 @@ export interface IScrollViewProps extends IAccessibilityProps, IAriaProps {
|
|
|
79
74
|
onMomentumScrollBegin?: IScrollHandler;
|
|
80
75
|
onMomentumScrollEnd?: IScrollHandler;
|
|
81
76
|
onScrollToTop?: IScrollHandler;
|
|
82
|
-
ref?: Ref<
|
|
77
|
+
ref?: Ref<IHostInstance>;
|
|
83
78
|
children?: JSX.Element;
|
|
84
79
|
}
|
|
85
|
-
export declare function createScrollView(platform: IScrollViewHostPlatform): (props: IScrollViewProps) => JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// `<scroll-view>` / `<horizontal-scroll-view>`'s prop surface, for Solid. No component left to
|
|
2
|
+
// type — the tag IS the element, and the engine owns everything the wrapper used to assemble:
|
|
3
|
+
// `buildStructure` builds the content node, `slotProps` carries `contentContainerStyle` onto it,
|
|
4
|
+
// `claimedChildren` places a `<refresh-control>` child (a sibling on iOS, an inverting wrap on
|
|
5
|
+
// Android), and the sticky seam lives in `core/components/src/behaviors/scroll-view/`.
|
|
6
|
+
// `../register` names it.
|
|
7
|
+
//
|
|
8
|
+
// `stickyHeaderIndices` works on a bare tag — the behavior walks the COMMITTED children rather
|
|
9
|
+
// than pulling "child at index N" out of a render function, so it needs no wrapper at all. The
|
|
10
|
+
// `sticky-header` TAG is the other spelling, better for markup you control directly since it pins
|
|
11
|
+
// by document order and no index has to stay in step with the children.
|
|
12
|
+
//
|
|
13
|
+
// TWO PROPS ARE NOT WHAT THEY LOOK LIKE:
|
|
14
|
+
//
|
|
15
|
+
// horizontal DELETED from the payload and rewritten from the TAG (`ownerFold`,
|
|
16
|
+
// behaviors/scroll-view/shared.ts). Horizontal scroll is a separate native
|
|
17
|
+
// ViewManager on Android, so the axis is the tag; a prop contradicting it
|
|
18
|
+
// is dropped with a dlog. Typed here for parity and for a ported app.
|
|
19
|
+
// nestedScrollEnabled defaults to `true` in the behavior, on both platforms, which every
|
|
20
|
+
// wrapper used to write by hand.
|
|
21
|
+
//
|
|
22
|
+
// NO `refreshControl` PROP, deliberately — write a `<refresh-control>` CHILD instead; the
|
|
23
|
+
// behavior claims it and places it per platform. Nothing reads a prop by that name, so typing one
|
|
24
|
+
// would compile and paint nothing; leaving it out fails loudly at `tsc` with this comment beside
|
|
25
|
+
// it, same as React's and Vue's barrels.
|
|
26
|
+
//
|
|
27
|
+
// `children` is a Solid `JSX.Element`, so this type is per-adapter by construction
|
|
28
|
+
// (<prop_types_split_agnostic_vs_per_adapter>).
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ISwitchProps as ISwitchBaseProps } from '@symbiote-native/components';
|
|
2
|
+
import type { IClassNameValue } from '@symbiote-native/engine';
|
|
3
|
+
export type { ISwitchTrackColor, ISwitchChangeEvent, } from '@symbiote-native/components';
|
|
4
|
+
export type ISwitchProps = ISwitchBaseProps & {
|
|
5
|
+
class?: IClassNameValue;
|
|
6
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// The `<switch>` tag's prop surface. The controlled handshake the wrapper used to run — the
|
|
2
|
+
// lastNativeReport mirror, the snap-back command when the parent rejects a toggle, the platform
|
|
3
|
+
// track-colour mapping — is the tag's own behavior now (`registerSwitchBehavior`, wired by
|
|
4
|
+
// `../register`), which is also where the iOS/Android command-name split lives.
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Ref } from 'solid-js';
|
|
2
|
+
import type { ITextInputProps as ITextInputBaseProps } from '@symbiote-native/components';
|
|
3
|
+
import type { IClassNameValue } from '@symbiote-native/engine';
|
|
4
|
+
import type { IHostInstance } from '../host-instance';
|
|
5
|
+
export type ITextInputProps = ITextInputBaseProps & {
|
|
6
|
+
class?: IClassNameValue;
|
|
7
|
+
ref?: Ref<IHostInstance>;
|
|
8
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Ref } from 'solid-js';
|
|
2
2
|
import type { JSX } from '../jsx-runtime';
|
|
3
|
-
import {
|
|
3
|
+
import type { IAccessibilityProps, IAriaProps } from '@symbiote-native/components';
|
|
4
4
|
import type { IClassNameValue, IStyleProp, ISymbioteEvent, ITextStyle } from '@symbiote-native/engine';
|
|
5
5
|
import type { IHostInstance } from '../host-instance';
|
|
6
6
|
export interface ITextProps extends IAccessibilityProps, IAriaProps {
|
|
@@ -15,6 +15,7 @@ export interface ITextProps extends IAccessibilityProps, IAriaProps {
|
|
|
15
15
|
numberOfLines?: number;
|
|
16
16
|
ellipsizeMode?: 'head' | 'middle' | 'tail' | 'clip';
|
|
17
17
|
selectable?: boolean;
|
|
18
|
+
disabled?: boolean;
|
|
18
19
|
adjustsFontSizeToFit?: boolean;
|
|
19
20
|
minimumFontScale?: number;
|
|
20
21
|
allowFontScaling?: boolean;
|
|
@@ -23,4 +24,3 @@ export interface ITextProps extends IAccessibilityProps, IAriaProps {
|
|
|
23
24
|
ref?: Ref<IHostInstance>;
|
|
24
25
|
children?: JSX.Element;
|
|
25
26
|
}
|
|
26
|
-
export declare function Text(props: ITextProps): JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// The `<text>` tag's prop surface. Same story as `./view-props.ts` — the wrapper forwarded a bag
|
|
2
|
+
// and its two folds live below the adapter now, with one addition: RN's `Text` DEFAULTS
|
|
3
|
+
// (`ellipsizeMode`, `allowFontScaling`) are seeded by the renderer at `createElement` and re-seeded
|
|
4
|
+
// on a patch that clears them, so a bare tag carries them exactly as the component did.
|
|
5
|
+
//
|
|
6
|
+
// THE NESTING (`RCTText` vs `RCTVirtualText`) was never this file's job and still is not. React
|
|
7
|
+
// Native tracks a TextAncestor context so a Text inside another Text renders as a virtual span;
|
|
8
|
+
// here the engine's commit walk carries `hasTextAncestor` down and picks the view name itself
|
|
9
|
+
// (`viewNameFor`, core/engine/src/commit.ts), re-creating the node when the kind flips. Every
|
|
10
|
+
// adapter emits the same flat `text` and the engine resolves it for all of them.
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { INativeFeedbackBackground } from '@symbiote-native/components';
|
|
2
|
+
import type { JSX } from '../../jsx-runtime';
|
|
3
|
+
import type { IPressableProps } from '../pressable-props';
|
|
4
|
+
export interface ITouchableNativeFeedbackProps extends Omit<IPressableProps, 'style' | 'children'> {
|
|
5
|
+
background?: INativeFeedbackBackground;
|
|
6
|
+
useForeground?: boolean;
|
|
7
|
+
children?: JSX.Element;
|
|
8
|
+
touchSoundDisabled?: boolean;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { JSX } from '../jsx-runtime';
|
|
2
|
+
import type { IPressTimingProps } from '@symbiote-native/components';
|
|
3
|
+
import type { IClassNameValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
4
|
+
import type { IPressableProps } from './pressable-props';
|
|
5
|
+
export type ITouchableBaseProps = Omit<IPressableProps, 'style' | 'class' | 'children'> & IPressTimingProps & {
|
|
6
|
+
style?: IStyleProp<IViewStyle>;
|
|
7
|
+
class?: IClassNameValue;
|
|
8
|
+
children?: JSX.Element;
|
|
9
|
+
};
|
|
10
|
+
export interface ITouchableOpacityProps extends ITouchableBaseProps {
|
|
11
|
+
activeOpacity?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ITouchableHighlightProps extends ITouchableBaseProps {
|
|
14
|
+
activeOpacity?: number;
|
|
15
|
+
underlayColor?: string;
|
|
16
|
+
touchSoundDisabled?: boolean;
|
|
17
|
+
testOnly_pressed?: boolean;
|
|
18
|
+
onShowUnderlay?: () => void;
|
|
19
|
+
onHideUnderlay?: () => void;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IPressTimingProps } from '@symbiote-native/components';
|
|
2
|
+
import type { JSX } from '../../jsx-runtime';
|
|
3
|
+
import type { IPressableProps } from '../pressable-props';
|
|
4
|
+
export interface ITouchableWithoutFeedbackProps extends Omit<IPressableProps, 'style' | 'children'>, IPressTimingProps {
|
|
5
|
+
children?: JSX.Element;
|
|
6
|
+
touchSoundDisabled?: boolean;
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Ref } from 'solid-js';
|
|
2
2
|
import type { JSX } from '../jsx-runtime';
|
|
3
|
-
import {
|
|
3
|
+
import type { IAccessibilityProps, IAriaProps, IResponderProps } from '@symbiote-native/components';
|
|
4
4
|
import type { IClassNameValue, IStyleProp, ISymbioteEvent, IViewStyle } from '@symbiote-native/engine';
|
|
5
5
|
import type { IHostInstance } from '../host-instance';
|
|
6
6
|
export interface IViewProps extends IAccessibilityProps, IAriaProps, IResponderProps {
|
|
@@ -29,4 +29,3 @@ export interface IViewProps extends IAccessibilityProps, IAriaProps, IResponderP
|
|
|
29
29
|
ref?: Ref<IHostInstance>;
|
|
30
30
|
children?: JSX.Element;
|
|
31
31
|
}
|
|
32
|
-
export declare function View(props: IViewProps): JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// The `<view>` tag's prop surface. The wrapper is gone: it forwarded a bag onto one host element
|
|
2
|
+
// and did two folds, both of which now run below the adapter — `id -> nativeID` in the renderer's
|
|
3
|
+
// `foldAliasKey` (which covers `createElement` and every later prop write, not just the wrapper's
|
|
4
|
+
// one path) and the aria/role fold in the engine's `fabricProps`.
|
|
5
|
+
//
|
|
6
|
+
// ONE THING THE WRAPPER DID IS NOT INHERITED, and a consumer that builds a whole bag has to do it
|
|
7
|
+
// itself: `withStableKeys`. Solid's `spread` walks only the CURRENT key set and has no removal
|
|
8
|
+
// pass, so a key that stops being emitted keeps its last value on the native view forever
|
|
9
|
+
// (`.claude/rules/solid-descriptor-bridge.md` §1). A bag whose keys are all statically present —
|
|
10
|
+
// an ordinary attribute list, a `splitProps` rest proxy — is unaffected.
|
|
11
|
+
export {};
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
// cannot sit beside the content the way iOS allows ("addViewAt: failed to insert view … at index 1").
|
|
3
3
|
// Instead the RefreshControl (AndroidSwipeRefreshLayout) WRAPS the scroll host, which nests inside it
|
|
4
4
|
// so the inner scroll takes the gesture before the refresh parent — RN's own ScrollView.js android
|
|
5
|
-
// branch
|
|
5
|
+
// branch, decided by the `<scroll-view>` tag's own engine behavior registered for this platform
|
|
6
|
+
// (`core/components/src/behaviors/scroll-view/index.android.ts`). Metro picks this file on an
|
|
7
|
+
// Android host; no Platform.OS read. iOS and Android share one `createVirtualizedList` now — nothing
|
|
8
|
+
// here differs per platform any more except which file Metro resolves.
|
|
6
9
|
import { createVirtualizedList } from './shared.jsx';
|
|
7
|
-
export const VirtualizedList = createVirtualizedList(
|
|
8
|
-
refreshControlMode: 'wrap',
|
|
9
|
-
});
|
|
10
|
+
export const VirtualizedList = createVirtualizedList();
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// VirtualizedList on iOS. The RefreshControl (PullToRefreshView) is a CHILD of the scroll host,
|
|
2
2
|
// rendered as a childless SIBLING BEFORE the content container — RN ScrollView.js's
|
|
3
|
-
// `{refreshControl}{contentContainer}`,
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// `{refreshControl}{contentContainer}`, decided by the `<scroll-view>` tag's own engine behavior
|
|
4
|
+
// registered for this platform (`core/components/src/behaviors/scroll-view/index.ios.ts`), the same
|
|
5
|
+
// one every other adapter's iOS binding registers. Metro picks this file on an iOS host; no
|
|
6
|
+
// Platform.OS read. iOS and Android share one `createVirtualizedList` now — nothing here differs
|
|
7
|
+
// per platform any more except which file Metro resolves.
|
|
6
8
|
import { createVirtualizedList } from './shared.jsx';
|
|
7
|
-
export const VirtualizedList = createVirtualizedList(
|
|
8
|
-
refreshControlMode: 'sibling',
|
|
9
|
-
});
|
|
9
|
+
export const VirtualizedList = createVirtualizedList();
|
|
@@ -2,8 +2,6 @@ import { type Accessor, type Ref } from 'solid-js';
|
|
|
2
2
|
import type { JSX } from '../../jsx-runtime';
|
|
3
3
|
import { type IAccessibilityProps, type IAriaProps, type ICellLayout, type IViewabilityConfig, type IViewabilityConfigCallbackPair, type IViewableItemsChangedInfo, type ISeparatorProps, type ISeparators, type IViewToken, type IVirtualizedListHandle } from '@symbiote-native/components';
|
|
4
4
|
import { type IClassNameValue, type IStyleProp, type ISymbioteEvent, type IViewStyle } from '@symbiote-native/engine';
|
|
5
|
-
import type { IScrollViewHostPlatform } from '../scroll-view/shared';
|
|
6
|
-
export type { IScrollViewHostPlatform };
|
|
7
5
|
export type { ICellLayout, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, IVirtualizedListHandle, };
|
|
8
6
|
export interface IVirtualizedListCellInfo<ItemT> {
|
|
9
7
|
item: ItemT;
|
|
@@ -72,4 +70,4 @@ export interface IVirtualizedListProps<ItemT> extends IAccessibilityProps, IAria
|
|
|
72
70
|
}) => void;
|
|
73
71
|
}
|
|
74
72
|
export type IVirtualizedListComponent = <ItemT>(props: IVirtualizedListProps<ItemT>) => JSX.Element;
|
|
75
|
-
export declare function createVirtualizedList(
|
|
73
|
+
export declare function createVirtualizedList(): IVirtualizedListComponent;
|
|
@@ -1,25 +1,43 @@
|
|
|
1
|
-
// VirtualizedList — real windowing over
|
|
2
|
-
// offset falls inside the visible window (plus a leading/trailing
|
|
3
|
-
// everything above and below collapses into spacers whose sizes sum
|
|
4
|
-
// scroll thumb and total content size stay right without mounting
|
|
1
|
+
// VirtualizedList — real windowing over the `<scroll-view>` tag's own engine-owned scroll host.
|
|
2
|
+
// Only the cells whose computed offset falls inside the visible window (plus a leading/trailing
|
|
3
|
+
// buffer) exist as native views; everything above and below collapses into spacers whose sizes sum
|
|
4
|
+
// to the off-screen extent, so the scroll thumb and total content size stay right without mounting
|
|
5
|
+
// all N rows.
|
|
5
6
|
//
|
|
6
7
|
// The orchestration — window recompute, edge-reached, viewability, batch fill, MVCP, the imperative
|
|
7
8
|
// scrolls — is the framework-agnostic `reduceList` state machine in @symbiote-native/components,
|
|
8
9
|
// shared verbatim with React, Vue, Svelte and Angular. So is every geometry leaf (`buildListPlan`,
|
|
9
10
|
// `computeWindow`, `resolveItemKey`, …) and every scroll-host helper ScrollView also uses
|
|
10
|
-
// (`selectScrollIntrinsics`, `resolveScrollForwarding`, `
|
|
11
|
+
// (`selectScrollIntrinsics`, `resolveScrollForwarding`, `buildScrollViewHandle`,
|
|
11
12
|
// `attachStickyScroll`, `forwardScrollEvent`). Solid supplies ONLY its lifecycle: it turns native
|
|
12
13
|
// events into ACTIONS, holds ONE plain state cell, runs the returned EFFECTS with Solid primitives,
|
|
13
14
|
// and assembles the host elements. Lists have no Descriptor render fn — the cell content is the
|
|
14
15
|
// user's own subtree (`symbiote-add-component` §0, category 2).
|
|
15
16
|
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
17
|
+
// SINCE 2026-09-11 THIS FILE NO LONGER BUILDS THE CONTENT NODE ITSELF. `registerScrollViewBehavior()`
|
|
18
|
+
// makes the tag's own `buildStructure` do that (the same content node ScrollView's bare tag gets
|
|
19
|
+
// anywhere else), and this file reads it back off `scroll.childHost` rather than constructing a
|
|
20
|
+
// second one — building one here too would nest a content view inside the behavior's own
|
|
21
|
+
// (`core/components/src/behaviors/scroll-view/shared.ts`'s header names this precondition). The
|
|
22
|
+
// RefreshControl placement (a sibling on iOS, an inverting wrap on Android) moved the same way: it is
|
|
23
|
+
// an ordinary child now, and the behavior's `claimedChildren` decides where it lands.
|
|
24
|
+
//
|
|
25
|
+
// WHY THIS FILE STILL HAND-AUTHORS THE SCROLL TAG VIA `createElement` INSTEAD OF PLAIN JSX
|
|
26
|
+
// (`<scroll-view>{listBody}</scroll-view>`), unlike a component with no dynamic children. Solid's
|
|
27
|
+
// own `insert()` reconciles a parent's children by walking that SAME node's `getFirstChild`/
|
|
28
|
+
// `getNextSibling` — which read `node.children` directly and know nothing about `childHost`
|
|
29
|
+
// redirection. Calling `insert` on the SCROLL node itself would have it diff against the scroll
|
|
30
|
+
// node's own (single) child rather than the content node's list items. So this file keeps an
|
|
31
|
+
// explicit reference to the content node and calls `insert(content, listBody)` on THAT, exactly the
|
|
32
|
+
// node Solid's own diffing needs to see.
|
|
33
|
+
//
|
|
34
|
+
// WHY THE SCROLL CELLS ARE WALKED FOR STICKY HEADERS RATHER THAN LEFT TO THE ENGINE'S GENERIC
|
|
35
|
+
// `stickyHeaderIndices` WALK OF COMMITTED CHILDREN (what a bare `<scroll-view>` gets for free
|
|
36
|
+
// elsewhere): the flagged CELL here needs wrapping ONCE, inside the keyed <For> row that owns it, or
|
|
37
|
+
// every scroll event would tear the wrapper down and rebuild it (losing its measured layout each
|
|
38
|
+
// time) the moment a naive re-render tried to resolve which child is sticky. Same conclusion, same
|
|
39
|
+
// reason, as adapters/svelte's index.svelte. This is independent of the content-node question above
|
|
40
|
+
// — it is about the LIST's own cells, not about how the scroll host itself is built.
|
|
23
41
|
//
|
|
24
42
|
// THREE SOLID FACTS DRIVE THE REST, and none of them is cosmetic (.claude/rules/
|
|
25
43
|
// solid-descriptor-bridge.md):
|
|
@@ -35,18 +53,12 @@
|
|
|
35
53
|
// 3. The scroll AXIS picks a different host TAG and Solid cannot swap a tag under a live node — so
|
|
36
54
|
// the flip is an explicit rebuild boundary (trap 5), with the build untracked so every other prop
|
|
37
55
|
// re-props the SAME nodes through `spread`.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// before the content on iOS, WRAPPING the whole scroll host on Android (an Android ScrollView takes
|
|
41
|
-
// exactly one child). That is `IScrollViewHostPlatform.refreshControlMode`, the same fact this
|
|
42
|
-
// adapter's ScrollView encodes, so the two platform barrels feed it in.
|
|
43
|
-
import { For, Show, createEffect, createMemo, createRenderEffect, createSignal, on, onCleanup, splitProps, untrack, } from 'solid-js';
|
|
44
|
-
import { DEFAULT_END_REACHED_THRESHOLD, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_START_REACHED_THRESHOLD, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_WINDOW_SIZE, EMPTY_OFFSET, FIRST_INDEX, NO_INDEX, INVERTED_X_STYLE, INVERTED_Y_STYLE, buildListPlan, buildScrollViewHandle, buildViewabilityPairs, nextStickyHeaderY, isSeparatorGapInRange, listEffectSignature, attachStickyScroll, forwardScrollEvent, resolveAccessibilityProps, resolveScrollForwarding, splitLayoutProps, selectScrollIntrinsics, createInitialListState, readLayoutLength, readLayoutOffset, readLayoutNumber, readScrollOffset, reduceList, resolveItemKey, } from '@symbiote-native/components';
|
|
56
|
+
import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, splitProps, untrack, } from 'solid-js';
|
|
57
|
+
import { DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_WINDOW_SIZE, EMPTY_OFFSET, FIRST_INDEX, NO_INDEX, INVERTED_X_STYLE, INVERTED_Y_STYLE, buildListPlan, buildScrollViewHandle, buildViewabilityPairs, nextStickyHeaderY, isSeparatorGapInRange, listEffectSignature, attachStickyScroll, forwardScrollEvent, resolveAccessibilityProps, resolveScrollForwarding, selectScrollIntrinsics, createInitialListState, readLayoutLength, readLayoutOffset, readLayoutNumber, readScrollOffset, reduceList, resolveItemKey, } from '@symbiote-native/components';
|
|
45
58
|
import { AnimatedValue, dlog, event as animatedEvent, getNativeTag, isNativeAnimatedAvailable, isSymbioteNode, whenCommitted, resolveClassName, } from '@symbiote-native/engine';
|
|
46
|
-
import { createElement, insert, insertNode,
|
|
59
|
+
import { createElement, insert, insertNode, spread } from '../../renderer.js';
|
|
47
60
|
import { withStableKeys } from '../../utils/stable-keys.js';
|
|
48
|
-
import {
|
|
49
|
-
import { ScrollViewStickyHeader } from '../scroll-view/sticky-header.jsx';
|
|
61
|
+
import { ScrollViewStickyHeader } from './sticky-header.jsx';
|
|
50
62
|
// Consumed by the lifecycle itself; everything LEFT OVER is the accessibility surface, which rides
|
|
51
63
|
// down onto the scroll host exactly as React spreads `...accessibilityRest` onto its ScrollView.
|
|
52
64
|
const HANDLED_PROPS = [
|
|
@@ -97,7 +109,11 @@ const HANDLED_PROPS = [
|
|
|
97
109
|
// A ListHeaderComponent occupies child 0 of the content container, which shifts every data index
|
|
98
110
|
// native MVCP anchors against by one (RN VirtualizedList.js does the same bump).
|
|
99
111
|
const HEADER_CHILD_COUNT = 1;
|
|
100
|
-
|
|
112
|
+
// No platform parameter any more: the RefreshControl placement per platform (a sibling on iOS, an
|
|
113
|
+
// inverting wrap on Android) is entirely the tag's own engine behavior's concern now
|
|
114
|
+
// (`core/components/src/behaviors/scroll-view/index.{ios,android}.ts`) — this factory used to take
|
|
115
|
+
// an `IScrollViewHostPlatform` only to re-decide the same thing by hand.
|
|
116
|
+
export function createVirtualizedList() {
|
|
101
117
|
return function VirtualizedList(props) {
|
|
102
118
|
// The ONE folded state cell (RN's scattered refs collapsed into IListState). A plain object, never
|
|
103
119
|
// a signal: nothing reads it reactively.
|
|
@@ -174,8 +190,8 @@ export function createVirtualizedList(platform) {
|
|
|
174
190
|
maxToRenderPerBatch: props.maxToRenderPerBatch ?? DEFAULT_MAX_TO_RENDER_PER_BATCH,
|
|
175
191
|
updateCellsBatchingPeriod: props.updateCellsBatchingPeriod ??
|
|
176
192
|
DEFAULT_UPDATE_CELLS_BATCHING_PERIOD,
|
|
177
|
-
onEndReachedThreshold: props.onEndReachedThreshold
|
|
178
|
-
onStartReachedThreshold: props.onStartReachedThreshold
|
|
193
|
+
onEndReachedThreshold: props.onEndReachedThreshold,
|
|
194
|
+
onStartReachedThreshold: props.onStartReachedThreshold,
|
|
179
195
|
maintainVisibleContentPosition: props.maintainVisibleContentPosition,
|
|
180
196
|
initialScrollIndex: props.initialScrollIndex,
|
|
181
197
|
onEndReachedActive: props.onEndReached !== undefined,
|
|
@@ -226,8 +242,12 @@ export function createVirtualizedList(platform) {
|
|
|
226
242
|
// lastViewable is folded back only when the fire actually LANDS, so a debounce superseded
|
|
227
243
|
// mid-flight still diffs against the last committed set.
|
|
228
244
|
const fire = () => {
|
|
229
|
-
for (const pair of pairs)
|
|
230
|
-
pair.onViewableItemsChanged(
|
|
245
|
+
for (const pair of pairs) {
|
|
246
|
+
pair.onViewableItemsChanged({
|
|
247
|
+
...info,
|
|
248
|
+
viewabilityConfig: pair.viewabilityConfig,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
231
251
|
dispatch({ kind: 'viewable-fired', map });
|
|
232
252
|
};
|
|
233
253
|
if (viewableTimer !== null) {
|
|
@@ -542,13 +562,13 @@ export function createVirtualizedList(platform) {
|
|
|
542
562
|
// moves. Measured at exactly 17px on device 2026-08-19 (a 1px divider under a 16px container
|
|
543
563
|
// gap); see .claude/rules/list-geometry-feedback-loop.md.
|
|
544
564
|
const separator = (<Show when={hasSeparatorAfter(index())}>
|
|
545
|
-
<
|
|
565
|
+
<view>{separatorElement(index())}</view>
|
|
546
566
|
</Show>);
|
|
547
567
|
if (!sticky) {
|
|
548
|
-
return (<
|
|
568
|
+
return (<view onLayout={makeCellMeasure(index)} style={invertedStyle()}>
|
|
549
569
|
{content}
|
|
550
570
|
{separator}
|
|
551
|
-
</
|
|
571
|
+
</view>);
|
|
552
572
|
}
|
|
553
573
|
// The sticky wrapper measures the cell itself, so it REPLACES the plain measuring view rather
|
|
554
574
|
// than nesting inside it. Every reactive input is written as a CALL, which makes the compiler
|
|
@@ -575,7 +595,7 @@ export function createVirtualizedList(platform) {
|
|
|
575
595
|
cell,
|
|
576
596
|
// The gap between the force-mounted sticky cell and the window's first cell.
|
|
577
597
|
<Show when={forcedStickyIndex() === index() && gapExtent() > EMPTY_OFFSET}>
|
|
578
|
-
<
|
|
598
|
+
<view style={spacerStyle(gapExtent())}/>
|
|
579
599
|
</Show>,
|
|
580
600
|
];
|
|
581
601
|
}
|
|
@@ -588,33 +608,30 @@ export function createVirtualizedList(platform) {
|
|
|
588
608
|
return typeof style === 'string' ? resolveClassName(style) : style;
|
|
589
609
|
};
|
|
590
610
|
const intrinsics = createMemo(() => selectScrollIntrinsics(isHorizontal(), contentContainerStyleInput()));
|
|
591
|
-
|
|
592
|
-
//
|
|
593
|
-
//
|
|
594
|
-
//
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
611
|
+
// The composed content style RN's ScrollView carries: the app's contentContainerStyle plus the
|
|
612
|
+
// one thing only THIS list knows — the total content extent along the scroll axis, which pins
|
|
613
|
+
// the content to its measured width so a horizontal row has something to scroll (RN's own
|
|
614
|
+
// ScrollView never needs this; it never virtualizes). The behavior's own `contentFold` appends
|
|
615
|
+
// the row-direction style on top of whatever this resolves to (`core/components/src/behaviors/
|
|
616
|
+
// scroll-view/shared.ts`), same composition `intrinsics().contentStyle` used to build by hand.
|
|
617
|
+
const contentContainerStyleForContent = () => isHorizontal()
|
|
618
|
+
? [contentContainerStyleInput(), { width: metrics().total }]
|
|
619
|
+
: contentContainerStyleInput();
|
|
600
620
|
// withStableKeys because several keys below are conditional and Solid's `spread` walks only the
|
|
601
621
|
// CURRENT key set with no removal pass — a key that vanished would keep its last value on the
|
|
602
622
|
// native view forever (.claude/rules/solid-descriptor-bridge.md §1).
|
|
623
|
+
//
|
|
624
|
+
// No `wrapsRefreshControl`/`splitStyles`/base-style composition here any more: the tag's own
|
|
625
|
+
// engine behavior owns the content node, the base-style-under-the-app's-style composition, the
|
|
626
|
+
// Android-wrap style split (`splitScrollViewStyle`, read off exactly the `style` written below),
|
|
627
|
+
// `nestedScrollEnabled`'s default and the `horizontal` axis fold — this file only supplies what
|
|
628
|
+
// the behavior cannot know (the windowing metrics).
|
|
603
629
|
const outerBag = withStableKeys(() => {
|
|
604
630
|
const bag = {
|
|
605
631
|
...resolveAccessibilityProps(accessibilityRest),
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
? [intrinsics().scrollViewBaseStyle, splitStyles().inner]
|
|
610
|
-
: [intrinsics().scrollViewBaseStyle, props.style, invertedStyle()],
|
|
611
|
-
// Load-bearing on iOS (it flips RCTScrollView's own axis); ignored by Android's dedicated
|
|
612
|
-
// horizontal manager.
|
|
613
|
-
horizontal: isHorizontal(),
|
|
614
|
-
// RN's ScrollView default (`nestedScrollEnabled ?? true`): Android needs it for a list nested
|
|
615
|
-
// inside another scrollable to scroll at all, and the Android RefreshControl wrap needs the
|
|
616
|
-
// inner scroll to take the gesture before the refresh parent.
|
|
617
|
-
nestedScrollEnabled: true,
|
|
632
|
+
style: [props.style, invertedStyle()],
|
|
633
|
+
class: props.class,
|
|
634
|
+
contentContainerStyle: contentContainerStyleForContent(),
|
|
618
635
|
onLayout: onViewportLayout,
|
|
619
636
|
onScroll: scrollHandler(),
|
|
620
637
|
};
|
|
@@ -622,10 +639,6 @@ export function createVirtualizedList(platform) {
|
|
|
622
639
|
// JS-fallback), without which a header rebuilt off too-sparse scroll events pins late.
|
|
623
640
|
if (forwarding().scrollEventThrottle !== undefined)
|
|
624
641
|
bag.scrollEventThrottle = forwarding().scrollEventThrottle;
|
|
625
|
-
// Stripped under the wrap: splitStyles already folded the resolved class into outer/inner, so
|
|
626
|
-
// forwarding it raw too would re-apply its LAYOUT half a second time.
|
|
627
|
-
if (!wrapsRefreshControl())
|
|
628
|
-
bag.class = props.class;
|
|
629
642
|
if (props.onScrollBeginDrag !== undefined)
|
|
630
643
|
bag.onScrollBeginDrag = props.onScrollBeginDrag;
|
|
631
644
|
if (props.onScrollEndDrag !== undefined)
|
|
@@ -654,24 +667,6 @@ export function createVirtualizedList(platform) {
|
|
|
654
667
|
}
|
|
655
668
|
return bag;
|
|
656
669
|
});
|
|
657
|
-
// `collapsable: false` keeps the content container a real Yoga node: Android Fabric would otherwise
|
|
658
|
-
// flatten it away and hoist the cells up as DIRECT children of the scroll view, which hosts exactly
|
|
659
|
-
// one. A horizontal list also pins the container to the full content WIDTH, or the row is stretched
|
|
660
|
-
// to the viewport and there is nothing to scroll.
|
|
661
|
-
const contentBag = withStableKeys(() => {
|
|
662
|
-
const bag = {
|
|
663
|
-
style: isHorizontal()
|
|
664
|
-
? [intrinsics().contentStyle, { width: metrics().total }]
|
|
665
|
-
: intrinsics().contentStyle,
|
|
666
|
-
collapsable: false,
|
|
667
|
-
};
|
|
668
|
-
// maintainVisibleContentPosition anchors against the metrics of MOUNTED cell views, and Android
|
|
669
|
-
// Fabric flattens layout-only cells away — so the native helper would have nothing to anchor to
|
|
670
|
-
// and the list jumps on prepend. RN keeps the cells real the same way. iOS never flattens.
|
|
671
|
-
if (props.maintainVisibleContentPosition !== undefined)
|
|
672
|
-
bag.collapsableChildren = false;
|
|
673
|
-
return bag;
|
|
674
|
-
});
|
|
675
670
|
// Every reactive read below sits inside a <Show>/<For> prop getter, so this accessor itself has no
|
|
676
671
|
// dependencies and the content view's `insert` effect runs exactly ONCE. Moving one of those
|
|
677
672
|
// conditions into a plain helper called from here would put it in that effect's dependency set and
|
|
@@ -679,23 +674,23 @@ export function createVirtualizedList(platform) {
|
|
|
679
674
|
function listBody() {
|
|
680
675
|
return [
|
|
681
676
|
<Show when={headerElement !== undefined}>
|
|
682
|
-
<
|
|
677
|
+
<view>{headerElement}</view>
|
|
683
678
|
</Show>,
|
|
684
679
|
<Show when={isEmpty()} fallback={<>
|
|
685
680
|
<Show when={leadingExtent() > EMPTY_OFFSET}>
|
|
686
|
-
<
|
|
681
|
+
<view style={spacerStyle(leadingExtent())}/>
|
|
687
682
|
</Show>
|
|
688
683
|
<For each={cellKeys()}>{buildRow}</For>
|
|
689
684
|
<Show when={trailingExtent() > EMPTY_OFFSET}>
|
|
690
|
-
<
|
|
685
|
+
<view style={spacerStyle(trailingExtent())}/>
|
|
691
686
|
</Show>
|
|
692
687
|
</>}>
|
|
693
688
|
<Show when={emptyElement !== undefined}>
|
|
694
|
-
<
|
|
689
|
+
<view>{emptyElement}</view>
|
|
695
690
|
</Show>
|
|
696
691
|
</Show>,
|
|
697
692
|
<Show when={footerElement !== undefined}>
|
|
698
|
-
<
|
|
693
|
+
<view>{footerElement}</view>
|
|
699
694
|
</Show>,
|
|
700
695
|
];
|
|
701
696
|
}
|
|
@@ -714,38 +709,36 @@ export function createVirtualizedList(platform) {
|
|
|
714
709
|
if (onRefresh === undefined)
|
|
715
710
|
return undefined;
|
|
716
711
|
dlog('VirtualizedList wiring RefreshControl (onRefresh provided)');
|
|
717
|
-
const element = (<
|
|
712
|
+
const element = (<refresh-control refreshing={props.refreshing ?? false} onRefresh={onRefresh} progressViewOffset={props.progressViewOffset}/>);
|
|
718
713
|
return isSymbioteNode(element) ? element : undefined;
|
|
719
714
|
}
|
|
720
|
-
// The
|
|
715
|
+
// The scroll host, wired to the ENGINE-OWNED content node rather than a hand-built one. Once
|
|
716
|
+
// `registerScrollViewBehavior()` is live, `createElement(scrollViewIntrinsic)` already ran the
|
|
717
|
+
// behavior's `buildStructure` and appended its own content child as `childHost` — building a
|
|
718
|
+
// second one here would nest `RCTScrollContentView > RCTScrollContentView`
|
|
719
|
+
// (`core/components/src/behaviors/scroll-view/shared.ts`'s own header names this precondition).
|
|
721
720
|
function buildTree() {
|
|
722
|
-
const { scrollViewIntrinsic
|
|
721
|
+
const { scrollViewIntrinsic } = intrinsics();
|
|
723
722
|
dlog(`VirtualizedList -> ${scrollViewIntrinsic}`);
|
|
724
|
-
const content = hostElement(contentIntrinsic);
|
|
725
|
-
spread(content, contentBag, true);
|
|
726
|
-
insert(content, listBody);
|
|
727
723
|
const scroll = hostElement(scrollViewIntrinsic);
|
|
728
724
|
hostNode = scroll;
|
|
729
725
|
spread(scroll, outerBag, true);
|
|
730
|
-
|
|
731
|
-
|
|
726
|
+
const content = scroll.childHost;
|
|
727
|
+
if (content === undefined)
|
|
728
|
+
throw new Error(`VirtualizedList: ${scrollViewIntrinsic} built no content node — is ` +
|
|
729
|
+
'registerScrollViewBehavior() wired?');
|
|
730
|
+
insert(content, listBody);
|
|
731
|
+
// An ordinary child on both platforms now, not a hand-branched sibling/wrap: the behavior's
|
|
732
|
+
// own `claimedChildren` places it (a sibling of the content view on iOS, an inverting wrap on
|
|
733
|
+
// Android) and puts it BEFORE the content view regardless of insertion order.
|
|
732
734
|
const refresh = buildRefreshControl();
|
|
733
|
-
if (refresh !== undefined
|
|
735
|
+
if (refresh !== undefined)
|
|
734
736
|
insertNode(scroll, refresh);
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
// by re-invoking the VNode's type; Solid has neither, because the element arrived already
|
|
741
|
-
// built — so the scroll host is nested into the existing node and the outer (layout) half of
|
|
742
|
-
// the style is written onto it. A render effect, not a one-shot set, so a later style change
|
|
743
|
-
// still moves.
|
|
744
|
-
createRenderEffect(() => {
|
|
745
|
-
setProp(refresh, 'style', splitStyles().outer);
|
|
746
|
-
});
|
|
747
|
-
insertNode(refresh, scroll);
|
|
748
|
-
return refresh;
|
|
737
|
+
// A wrap claim re-parents `scroll` under the RefreshControl and records it as `scroll.wrapper`
|
|
738
|
+
// (`core/engine/src/node.ts`'s `placedNode`) — the wrapper, not `scroll`, is what actually
|
|
739
|
+
// occupies this subtree's place once claimed, so returning `scroll` on Android would hand back
|
|
740
|
+
// a node that is no longer the tree's root.
|
|
741
|
+
return scroll.wrapper ?? scroll;
|
|
749
742
|
}
|
|
750
743
|
// Drive the sticky scroll value on the native UI thread (RN's attachNativeEvent) so the header
|
|
751
744
|
// interpolations ride scroll natively with no JS per frame. `tree()` is read for its dependency:
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { JSX } from '../../jsx-runtime';
|
|
2
|
+
import { type IStickyHeaderProps } from '@symbiote-native/components';
|
|
3
|
+
export type IStickyHeaderComponentProps = IStickyHeaderProps & {
|
|
4
|
+
children?: JSX.Element;
|
|
5
|
+
};
|
|
6
|
+
export declare function ScrollViewStickyHeader(props: IStickyHeaderComponentProps): JSX.Element;
|