@symbiote-native/solid 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -10
- package/babel-preset.cjs +0 -5
- package/build/components/activity-indicator-props.d.ts +5 -0
- package/build/components/activity-indicator-props.js +1 -0
- package/build/components/button-props.d.ts +5 -0
- package/build/components/button-props.js +1 -0
- package/build/components/flat-list/index.jsx +3 -3
- package/build/components/{image-background.d.ts → image-background-props.d.ts} +2 -3
- package/build/components/image-background-props.js +1 -0
- package/build/components/image-props.d.ts +5 -0
- package/build/components/image-props.js +6 -0
- package/build/components/{input-accessory-view.d.ts → input-accessory-view-props.d.ts} +1 -2
- package/build/components/input-accessory-view-props.js +5 -0
- package/build/components/keyboard-avoiding-view.jsx +9 -4
- package/build/components/modal.jsx +6 -6
- package/build/components/{pressable.d.ts → pressable-props.d.ts} +8 -7
- package/build/components/pressable-props.js +13 -0
- package/build/components/{refresh-control.d.ts → refresh-control-props.d.ts} +3 -3
- package/build/components/refresh-control-props.js +7 -0
- package/build/components/safe-area-view-props.d.ts +10 -0
- package/build/components/safe-area-view-props.js +5 -0
- package/build/components/{scroll-view/shared.d.ts → scroll-view-props.d.ts} +7 -13
- package/build/components/scroll-view-props.js +29 -0
- package/build/components/switch-props.d.ts +6 -0
- package/build/components/switch-props.js +5 -0
- package/build/components/text-input-props.d.ts +8 -0
- package/build/components/text-input-props.js +1 -0
- package/build/components/{text.d.ts → text-props.d.ts} +3 -3
- package/build/components/text-props.js +11 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +8 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback-props.js +1 -0
- package/build/components/touchable-props.d.ts +18 -0
- package/build/components/touchable-props.js +1 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +6 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.js +1 -0
- package/build/components/{view.d.ts → view-props.d.ts} +2 -3
- package/build/components/view-props.js +11 -0
- package/build/components/virtualized-list/index.android.js +5 -4
- package/build/components/virtualized-list/index.ios.js +6 -6
- package/build/components/virtualized-list/shared.d.ts +1 -3
- package/build/components/virtualized-list/shared.jsx +87 -98
- package/build/components/virtualized-list/sticky-header.d.ts +6 -0
- package/build/components/{scroll-view → virtualized-list}/sticky-header.jsx +28 -36
- package/build/components.d.ts +20 -30
- package/build/components.js +6 -33
- package/build/create-portal/index.jsx +1 -1
- package/build/descriptor-to-solid.js +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.js +11 -3
- package/build/jsx-runtime.d.ts +39 -2
- package/build/modules/animated/index.d.ts +0 -4
- package/build/modules/animated/index.js +16 -22
- package/build/modules/image.d.ts +3 -0
- package/build/modules/image.js +12 -0
- package/build/register.d.ts +1 -1
- package/build/register.js +12 -28
- package/build/renderer.js +79 -16
- package/build/utils/stable-keys.d.ts +1 -1
- package/build/utils/stable-keys.js +6 -5
- package/metro-css-parser.cjs +2 -1
- package/package.json +5 -10
- package/babel-lower-host-primitives.cjs +0 -323
- package/build/components/activity-indicator/index.android.d.ts +0 -2
- package/build/components/activity-indicator/index.android.js +0 -11
- package/build/components/activity-indicator/index.d.ts +0 -1
- package/build/components/activity-indicator/index.ios.d.ts +0 -2
- package/build/components/activity-indicator/index.ios.js +0 -10
- package/build/components/activity-indicator/index.js +0 -4
- package/build/components/activity-indicator/shared.d.ts +0 -8
- package/build/components/activity-indicator/shared.js +0 -51
- package/build/components/button.d.ts +0 -7
- package/build/components/button.jsx +0 -45
- package/build/components/image-background.jsx +0 -74
- package/build/components/image.d.ts +0 -9
- package/build/components/image.js +0 -75
- package/build/components/input-accessory-view.jsx +0 -39
- package/build/components/pressable.jsx +0 -211
- package/build/components/refresh-control.jsx +0 -36
- package/build/components/safe-area-view.d.ts +0 -11
- package/build/components/safe-area-view.jsx +0 -24
- package/build/components/scroll-view/index.android.d.ts +0 -2
- package/build/components/scroll-view/index.android.js +0 -11
- package/build/components/scroll-view/index.d.ts +0 -2
- package/build/components/scroll-view/index.ios.d.ts +0 -2
- package/build/components/scroll-view/index.ios.js +0 -8
- package/build/components/scroll-view/index.js +0 -9
- package/build/components/scroll-view/shared.jsx +0 -334
- package/build/components/scroll-view/sticky-header.d.ts +0 -18
- package/build/components/switch/index.android.d.ts +0 -2
- package/build/components/switch/index.android.js +0 -12
- package/build/components/switch/index.d.ts +0 -1
- package/build/components/switch/index.ios.d.ts +0 -2
- package/build/components/switch/index.ios.js +0 -11
- package/build/components/switch/index.js +0 -4
- package/build/components/switch/shared.d.ts +0 -11
- package/build/components/switch/shared.js +0 -87
- package/build/components/text-input.d.ts +0 -10
- package/build/components/text-input.js +0 -290
- package/build/components/text.jsx +0 -27
- package/build/components/touchable/index.d.ts +0 -22
- package/build/components/touchable/index.jsx +0 -247
- package/build/components/touchable-native-feedback/index.d.ts +0 -16
- package/build/components/touchable-native-feedback/index.jsx +0 -68
- package/build/components/view.jsx +0 -49
- package/build/state-style.d.ts +0 -1
- package/build/state-style.js +0 -13
|
@@ -1,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_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, 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.
|
|
@@ -542,13 +558,13 @@ export function createVirtualizedList(platform) {
|
|
|
542
558
|
// moves. Measured at exactly 17px on device 2026-08-19 (a 1px divider under a 16px container
|
|
543
559
|
// gap); see .claude/rules/list-geometry-feedback-loop.md.
|
|
544
560
|
const separator = (<Show when={hasSeparatorAfter(index())}>
|
|
545
|
-
<
|
|
561
|
+
<view>{separatorElement(index())}</view>
|
|
546
562
|
</Show>);
|
|
547
563
|
if (!sticky) {
|
|
548
|
-
return (<
|
|
564
|
+
return (<view onLayout={makeCellMeasure(index)} style={invertedStyle()}>
|
|
549
565
|
{content}
|
|
550
566
|
{separator}
|
|
551
|
-
</
|
|
567
|
+
</view>);
|
|
552
568
|
}
|
|
553
569
|
// The sticky wrapper measures the cell itself, so it REPLACES the plain measuring view rather
|
|
554
570
|
// than nesting inside it. Every reactive input is written as a CALL, which makes the compiler
|
|
@@ -575,7 +591,7 @@ export function createVirtualizedList(platform) {
|
|
|
575
591
|
cell,
|
|
576
592
|
// The gap between the force-mounted sticky cell and the window's first cell.
|
|
577
593
|
<Show when={forcedStickyIndex() === index() && gapExtent() > EMPTY_OFFSET}>
|
|
578
|
-
<
|
|
594
|
+
<view style={spacerStyle(gapExtent())}/>
|
|
579
595
|
</Show>,
|
|
580
596
|
];
|
|
581
597
|
}
|
|
@@ -588,33 +604,30 @@ export function createVirtualizedList(platform) {
|
|
|
588
604
|
return typeof style === 'string' ? resolveClassName(style) : style;
|
|
589
605
|
};
|
|
590
606
|
const intrinsics = createMemo(() => selectScrollIntrinsics(isHorizontal(), contentContainerStyleInput()));
|
|
591
|
-
|
|
592
|
-
//
|
|
593
|
-
//
|
|
594
|
-
//
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
607
|
+
// The composed content style RN's ScrollView carries: the app's contentContainerStyle plus the
|
|
608
|
+
// one thing only THIS list knows — the total content extent along the scroll axis, which pins
|
|
609
|
+
// the content to its measured width so a horizontal row has something to scroll (RN's own
|
|
610
|
+
// ScrollView never needs this; it never virtualizes). The behavior's own `contentFold` appends
|
|
611
|
+
// the row-direction style on top of whatever this resolves to (`core/components/src/behaviors/
|
|
612
|
+
// scroll-view/shared.ts`), same composition `intrinsics().contentStyle` used to build by hand.
|
|
613
|
+
const contentContainerStyleForContent = () => isHorizontal()
|
|
614
|
+
? [contentContainerStyleInput(), { width: metrics().total }]
|
|
615
|
+
: contentContainerStyleInput();
|
|
600
616
|
// withStableKeys because several keys below are conditional and Solid's `spread` walks only the
|
|
601
617
|
// CURRENT key set with no removal pass — a key that vanished would keep its last value on the
|
|
602
618
|
// native view forever (.claude/rules/solid-descriptor-bridge.md §1).
|
|
619
|
+
//
|
|
620
|
+
// No `wrapsRefreshControl`/`splitStyles`/base-style composition here any more: the tag's own
|
|
621
|
+
// engine behavior owns the content node, the base-style-under-the-app's-style composition, the
|
|
622
|
+
// Android-wrap style split (`splitScrollViewStyle`, read off exactly the `style` written below),
|
|
623
|
+
// `nestedScrollEnabled`'s default and the `horizontal` axis fold — this file only supplies what
|
|
624
|
+
// the behavior cannot know (the windowing metrics).
|
|
603
625
|
const outerBag = withStableKeys(() => {
|
|
604
626
|
const bag = {
|
|
605
627
|
...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,
|
|
628
|
+
style: [props.style, invertedStyle()],
|
|
629
|
+
class: props.class,
|
|
630
|
+
contentContainerStyle: contentContainerStyleForContent(),
|
|
618
631
|
onLayout: onViewportLayout,
|
|
619
632
|
onScroll: scrollHandler(),
|
|
620
633
|
};
|
|
@@ -622,10 +635,6 @@ export function createVirtualizedList(platform) {
|
|
|
622
635
|
// JS-fallback), without which a header rebuilt off too-sparse scroll events pins late.
|
|
623
636
|
if (forwarding().scrollEventThrottle !== undefined)
|
|
624
637
|
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
638
|
if (props.onScrollBeginDrag !== undefined)
|
|
630
639
|
bag.onScrollBeginDrag = props.onScrollBeginDrag;
|
|
631
640
|
if (props.onScrollEndDrag !== undefined)
|
|
@@ -654,24 +663,6 @@ export function createVirtualizedList(platform) {
|
|
|
654
663
|
}
|
|
655
664
|
return bag;
|
|
656
665
|
});
|
|
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
666
|
// Every reactive read below sits inside a <Show>/<For> prop getter, so this accessor itself has no
|
|
676
667
|
// dependencies and the content view's `insert` effect runs exactly ONCE. Moving one of those
|
|
677
668
|
// conditions into a plain helper called from here would put it in that effect's dependency set and
|
|
@@ -679,23 +670,23 @@ export function createVirtualizedList(platform) {
|
|
|
679
670
|
function listBody() {
|
|
680
671
|
return [
|
|
681
672
|
<Show when={headerElement !== undefined}>
|
|
682
|
-
<
|
|
673
|
+
<view>{headerElement}</view>
|
|
683
674
|
</Show>,
|
|
684
675
|
<Show when={isEmpty()} fallback={<>
|
|
685
676
|
<Show when={leadingExtent() > EMPTY_OFFSET}>
|
|
686
|
-
<
|
|
677
|
+
<view style={spacerStyle(leadingExtent())}/>
|
|
687
678
|
</Show>
|
|
688
679
|
<For each={cellKeys()}>{buildRow}</For>
|
|
689
680
|
<Show when={trailingExtent() > EMPTY_OFFSET}>
|
|
690
|
-
<
|
|
681
|
+
<view style={spacerStyle(trailingExtent())}/>
|
|
691
682
|
</Show>
|
|
692
683
|
</>}>
|
|
693
684
|
<Show when={emptyElement !== undefined}>
|
|
694
|
-
<
|
|
685
|
+
<view>{emptyElement}</view>
|
|
695
686
|
</Show>
|
|
696
687
|
</Show>,
|
|
697
688
|
<Show when={footerElement !== undefined}>
|
|
698
|
-
<
|
|
689
|
+
<view>{footerElement}</view>
|
|
699
690
|
</Show>,
|
|
700
691
|
];
|
|
701
692
|
}
|
|
@@ -714,38 +705,36 @@ export function createVirtualizedList(platform) {
|
|
|
714
705
|
if (onRefresh === undefined)
|
|
715
706
|
return undefined;
|
|
716
707
|
dlog('VirtualizedList wiring RefreshControl (onRefresh provided)');
|
|
717
|
-
const element = (<
|
|
708
|
+
const element = (<refresh-control refreshing={props.refreshing ?? false} onRefresh={onRefresh} progressViewOffset={props.progressViewOffset}/>);
|
|
718
709
|
return isSymbioteNode(element) ? element : undefined;
|
|
719
710
|
}
|
|
720
|
-
// The
|
|
711
|
+
// The scroll host, wired to the ENGINE-OWNED content node rather than a hand-built one. Once
|
|
712
|
+
// `registerScrollViewBehavior()` is live, `createElement(scrollViewIntrinsic)` already ran the
|
|
713
|
+
// behavior's `buildStructure` and appended its own content child as `childHost` — building a
|
|
714
|
+
// second one here would nest `RCTScrollContentView > RCTScrollContentView`
|
|
715
|
+
// (`core/components/src/behaviors/scroll-view/shared.ts`'s own header names this precondition).
|
|
721
716
|
function buildTree() {
|
|
722
|
-
const { scrollViewIntrinsic
|
|
717
|
+
const { scrollViewIntrinsic } = intrinsics();
|
|
723
718
|
dlog(`VirtualizedList -> ${scrollViewIntrinsic}`);
|
|
724
|
-
const content = hostElement(contentIntrinsic);
|
|
725
|
-
spread(content, contentBag, true);
|
|
726
|
-
insert(content, listBody);
|
|
727
719
|
const scroll = hostElement(scrollViewIntrinsic);
|
|
728
720
|
hostNode = scroll;
|
|
729
721
|
spread(scroll, outerBag, true);
|
|
730
|
-
|
|
731
|
-
|
|
722
|
+
const content = scroll.childHost;
|
|
723
|
+
if (content === undefined)
|
|
724
|
+
throw new Error(`VirtualizedList: ${scrollViewIntrinsic} built no content node — is ` +
|
|
725
|
+
'registerScrollViewBehavior() wired?');
|
|
726
|
+
insert(content, listBody);
|
|
727
|
+
// An ordinary child on both platforms now, not a hand-branched sibling/wrap: the behavior's
|
|
728
|
+
// own `claimedChildren` places it (a sibling of the content view on iOS, an inverting wrap on
|
|
729
|
+
// Android) and puts it BEFORE the content view regardless of insertion order.
|
|
732
730
|
const refresh = buildRefreshControl();
|
|
733
|
-
if (refresh !== undefined
|
|
731
|
+
if (refresh !== undefined)
|
|
734
732
|
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;
|
|
733
|
+
// A wrap claim re-parents `scroll` under the RefreshControl and records it as `scroll.wrapper`
|
|
734
|
+
// (`core/engine/src/node.ts`'s `placedNode`) — the wrapper, not `scroll`, is what actually
|
|
735
|
+
// occupies this subtree's place once claimed, so returning `scroll` on Android would hand back
|
|
736
|
+
// a node that is no longer the tree's root.
|
|
737
|
+
return scroll.wrapper ?? scroll;
|
|
749
738
|
}
|
|
750
739
|
// Drive the sticky scroll value on the native UI thread (RN's attachNativeEvent) so the header
|
|
751
740
|
// 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;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
// Sticky headers — the Solid lifecycle half of the JS layer RN implements in ScrollView.js /
|
|
2
2
|
// ScrollViewStickyHeader.js.
|
|
3
3
|
//
|
|
4
|
+
// PRIVATE TO VIRTUALIZED-LIST since 2026-09-11. It used to live under `../scroll-view` and back
|
|
5
|
+
// that component's `stickyHeaderIndices` too; the tag `<scroll-view>` now honors that prop through
|
|
6
|
+
// the engine's own `sticky-header` intrinsic (`core/components/src/behaviors/scroll-view/sticky.ts`)
|
|
7
|
+
// with no adapter code at all — see that primitive's props header. VirtualizedList still needs its
|
|
8
|
+
// OWN implementation because it hand-builds its scroll host (`shared.tsx`'s own header explains
|
|
9
|
+
// why) rather than emitting the tag, so it wraps a flagged CELL itself instead of leaning on the
|
|
10
|
+
// engine's per-child walk. `wrapStickyHeaders`/`IStickyWrapInputs` — the generic child-array wrapper
|
|
11
|
+
// ScrollView used and this file never called — were dropped with the move; `virtualized-list/
|
|
12
|
+
// shared.tsx` wraps the cell it already owns directly.
|
|
13
|
+
//
|
|
4
14
|
// RN does stickiness PURELY IN JS: ScrollView wraps each flagged child in a ScrollViewStickyHeader
|
|
5
15
|
// fed by ONE scroll AnimatedValue, and the native scroll view ignores `stickyHeaderIndices`
|
|
6
16
|
// entirely — forwarding that array to Fabric is a silent no-op, so we replicate the JS layer
|
|
@@ -21,8 +31,7 @@
|
|
|
21
31
|
// underneath; Solid does not (.claude/rules/solid-descriptor-bridge.md §4).
|
|
22
32
|
import { createEffect, createMemo, createSignal, onCleanup } from 'solid-js';
|
|
23
33
|
import { createInitialStickyState, readLayoutNumber, reduceSticky, STICKY_HEADER_Z_INDEX, } from '@symbiote-native/components';
|
|
24
|
-
import { AnimatedInterpolation, Platform, dlog,
|
|
25
|
-
import { Animated } from '../../modules/animated/index.js';
|
|
34
|
+
import { AnimatedInterpolation, Platform, dlog, } from '@symbiote-native/engine';
|
|
26
35
|
// One sticky header. Measures its own y/height through onLayout, interpolates the shared scroll
|
|
27
36
|
// offset into a translateY that pins it to the top (or the bottom, inverted) until the next header
|
|
28
37
|
// collides with it, and drives that translate through the native driver when available so the pin
|
|
@@ -137,46 +146,29 @@ export function ScrollViewStickyHeader(props) {
|
|
|
137
146
|
props.onLayout(event);
|
|
138
147
|
};
|
|
139
148
|
// The EXPLICIT debounced translateY overrides the committed transform for hit-testing, while
|
|
140
|
-
// animatedTranslateY does the smooth (native-driven) pin.
|
|
149
|
+
// animatedTranslateY does the smooth (native-driven) pin. It rides the style ARRAY now rather
|
|
150
|
+
// than `passthroughAnimatedPropExplicitValues`: that prop was read by `createAnimatedComponent`,
|
|
151
|
+
// and the host is a bare tag, where the engine ignores it
|
|
152
|
+
// (core/engine/src/animated/host-binding.ts calls it a wrapper-ism by name). The wrapper did
|
|
153
|
+
// exactly this merge — `[reduced.style, passthroughStyle]` — so a later array entry overriding
|
|
154
|
+
// the transform is the same semantics, one layer down, and the engine still binds the
|
|
155
|
+
// interpolation it finds in entry 0.
|
|
141
156
|
const passthrough = createMemo(() => {
|
|
142
157
|
// Read the bump so a committed translateY repaints.
|
|
143
158
|
version();
|
|
144
159
|
if (state.translateY === null)
|
|
145
|
-
return
|
|
146
|
-
return {
|
|
160
|
+
return undefined;
|
|
161
|
+
return { transform: [{ translateY: state.translateY }] };
|
|
147
162
|
});
|
|
148
163
|
// collapsable:false keeps the wrapper a real Yoga node; zIndex makes the pinned header paint OVER
|
|
149
164
|
// the rows scrolling up under it.
|
|
150
|
-
return (<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
165
|
+
return (<view style={[
|
|
166
|
+
{
|
|
167
|
+
transform: [{ translateY: animatedTranslateY() }],
|
|
168
|
+
zIndex: STICKY_HEADER_Z_INDEX,
|
|
169
|
+
},
|
|
170
|
+
passthrough(),
|
|
171
|
+
]} onLayout={onLayout} collapsable={false}>
|
|
154
172
|
{props.children}
|
|
155
|
-
</
|
|
156
|
-
}
|
|
157
|
-
// Wrap each child flagged by `stickyHeaderIndices` in the sticky header component, fed by the shared
|
|
158
|
-
// scroll AnimatedValue (RN ScrollView.js's render-time children.map). Unflagged children pass
|
|
159
|
-
// through untouched.
|
|
160
|
-
//
|
|
161
|
-
// Cross-talk (RN's _headerLayoutYs + _onStickyHeaderLayout): each header reports its own y through
|
|
162
|
-
// `onHeaderLayoutY`, and every header is fed the y of the NEXT flagged one — the point past which it
|
|
163
|
-
// gets pushed off. The LAST flagged header has no successor, so its nextHeaderLayoutY stays
|
|
164
|
-
// undefined and it sticks indefinitely, which is correct.
|
|
165
|
-
export function wrapStickyHeaders(children, inputs) {
|
|
166
|
-
const Wrapper = inputs.StickyHeaderComponent ?? ScrollViewStickyHeader;
|
|
167
|
-
return children.map((child, index) => {
|
|
168
|
-
const indexOfIndex = inputs.stickyHeaderIndices.indexOf(index);
|
|
169
|
-
// A non-node child (a bare string or number in a scroll view) has no frame to pin and cannot be
|
|
170
|
-
// wrapped meaningfully, so it passes through like an unflagged one.
|
|
171
|
-
if (indexOfIndex === -1 || !isSymbioteNode(child))
|
|
172
|
-
return child;
|
|
173
|
-
dlog(`Solid ScrollView sticky-header wrap index=${index}`);
|
|
174
|
-
return (<Wrapper nextHeaderLayoutY={inputs.readNextHeaderLayoutY(indexOfIndex)} onLayout={(event) => {
|
|
175
|
-
const y = readLayoutNumber(event, 'y');
|
|
176
|
-
if (y !== undefined)
|
|
177
|
-
inputs.onHeaderLayoutY(index, y);
|
|
178
|
-
}} scrollAnimatedValue={inputs.scrollAnimatedValue} inverted={inputs.readInverted()} scrollViewHeight={inputs.readScrollViewHeight()}>
|
|
179
|
-
{child}
|
|
180
|
-
</Wrapper>);
|
|
181
|
-
});
|
|
173
|
+
</view>);
|
|
182
174
|
}
|
package/build/components.d.ts
CHANGED
|
@@ -1,28 +1,20 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type {
|
|
3
|
-
export {
|
|
4
|
-
export type {
|
|
5
|
-
export {
|
|
6
|
-
export type {
|
|
7
|
-
export { Image } from './components/image';
|
|
8
|
-
export type { IImageProps } from './components/image';
|
|
9
|
-
export { Pressable } from './components/pressable';
|
|
10
|
-
export type { IPressableProps } from './components/pressable';
|
|
11
|
-
export { ActivityIndicator } from './components/activity-indicator';
|
|
12
|
-
export type { IActivityIndicatorProps } from './components/activity-indicator';
|
|
1
|
+
export type { IViewProps } from './components/view-props';
|
|
2
|
+
export type { ITextProps } from './components/text-props';
|
|
3
|
+
export type { ISafeAreaViewProps } from './components/safe-area-view-props';
|
|
4
|
+
export type { IImageProps } from './components/image-props';
|
|
5
|
+
export type { IPressableProps } from './components/pressable-props';
|
|
6
|
+
export type { IActivityIndicatorProps } from './components/activity-indicator-props';
|
|
13
7
|
export type { IImageStatics, IPressState, IPressableAndroidRippleConfig, } from '@symbiote-native/components';
|
|
14
|
-
export {
|
|
15
|
-
export type {
|
|
16
|
-
export {
|
|
17
|
-
export type { ITextInputProps, ITextInputHandle, ITextInputSelection, } from './components/text-input';
|
|
8
|
+
export type { ISwitchProps, ISwitchTrackColor, ISwitchChangeEvent, } from './components/switch-props';
|
|
9
|
+
export type { ITextInputProps } from './components/text-input-props';
|
|
10
|
+
export type { ITextInputHandle, ITextInputSelection, ITextInputChangeEvent, } from '@symbiote-native/components';
|
|
18
11
|
export { Modal } from './components/modal';
|
|
19
12
|
export type { IModalProps, IModalAnimationType, IModalPresentationStyle, IModalOrientation, IModalOrientationChangeEvent, } from './components/modal';
|
|
20
13
|
export { KeyboardAvoidingView } from './components/keyboard-avoiding-view';
|
|
21
14
|
export type { IKeyboardAvoidingViewProps, IKeyboardAvoidingBehavior, } from './components/keyboard-avoiding-view';
|
|
22
|
-
export {
|
|
23
|
-
export type {
|
|
24
|
-
export {
|
|
25
|
-
export type { IScrollViewProps, IScrollViewHandle, } from './components/scroll-view';
|
|
15
|
+
export type { IRefreshControlProps } from './components/refresh-control-props';
|
|
16
|
+
export type { IScrollViewProps } from './components/scroll-view-props';
|
|
17
|
+
export type { IScrollViewHandle } from '@symbiote-native/components';
|
|
26
18
|
export { VirtualizedList } from './components/virtualized-list';
|
|
27
19
|
export type { IVirtualizedListProps, IVirtualizedListHandle, IVirtualizedListCellInfo, IVirtualizedListRenderItem, ICellLayout, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, } from './components/virtualized-list';
|
|
28
20
|
export { FlatList } from './components/flat-list';
|
|
@@ -31,13 +23,11 @@ export { VirtualizedSectionList } from './components/virtualized-section-list';
|
|
|
31
23
|
export type { IVirtualizedSectionListProps, IVirtualizedSectionListHandle, ISectionHeaderInfo, ISectionCellInfo, ISection, } from './components/virtualized-section-list';
|
|
32
24
|
export { SectionList } from './components/section-list';
|
|
33
25
|
export type { ISectionListProps, ISectionListHandle, } from './components/section-list';
|
|
34
|
-
export {
|
|
35
|
-
export type {
|
|
36
|
-
export { TouchableNativeFeedback } from '
|
|
37
|
-
export type {
|
|
38
|
-
export {
|
|
39
|
-
export type { IButtonProps } from './components/button';
|
|
40
|
-
export {
|
|
41
|
-
export type {
|
|
42
|
-
export { InputAccessoryView } from './components/input-accessory-view';
|
|
43
|
-
export type { IInputAccessoryViewProps } from './components/input-accessory-view';
|
|
26
|
+
export type { ITouchableOpacityProps, ITouchableHighlightProps, } from './components/touchable-props';
|
|
27
|
+
export type { ITouchableWithoutFeedbackProps } from './components/touchable-without-feedback/touchable-without-feedback-props';
|
|
28
|
+
export { TouchableNativeFeedback } from '@symbiote-native/components';
|
|
29
|
+
export type { INativeFeedbackBackground, IThemeAttrBackground, IRippleBackground, } from '@symbiote-native/components';
|
|
30
|
+
export type { ITouchableNativeFeedbackProps } from './components/touchable-native-feedback/touchable-native-feedback-props';
|
|
31
|
+
export type { IButtonProps } from './components/button-props';
|
|
32
|
+
export type { IImageBackgroundProps } from './components/image-background-props';
|
|
33
|
+
export type { IInputAccessoryViewProps } from './components/input-accessory-view-props';
|
package/build/components.js
CHANGED
|
@@ -1,31 +1,12 @@
|
|
|
1
1
|
// The component re-export barrel, flat at the package root like every other adapter's. Grouping
|
|
2
2
|
// under components/ is internal; this file plus src/index.ts is the whole public surface.
|
|
3
|
-
export { View } from './components/view.jsx';
|
|
4
|
-
export { Text } from './components/text.jsx';
|
|
5
|
-
export { SafeAreaView } from './components/safe-area-view.jsx';
|
|
6
|
-
export { Image } from './components/image.js';
|
|
7
|
-
export { Pressable } from './components/pressable.jsx';
|
|
8
|
-
export { ActivityIndicator } from './components/activity-indicator';
|
|
9
|
-
export { Switch } from './components/switch';
|
|
10
|
-
export { TextInput } from './components/text-input.js';
|
|
11
3
|
export { Modal } from './components/modal.jsx';
|
|
12
4
|
export { KeyboardAvoidingView } from './components/keyboard-avoiding-view.jsx';
|
|
13
|
-
export { RefreshControl } from './components/refresh-control.jsx';
|
|
14
|
-
// ScrollViewStickyHeader is NOT exported, matching react-native itself: it lives at
|
|
15
|
-
// Libraries/Components/ScrollView/ScrollViewStickyHeader.js and is absent from RN's public
|
|
16
|
-
// index.js, so it is ScrollView's internal, not part of the public surface. React's and Vue's
|
|
17
|
-
// barrels agree. Svelte's and Angular's do export it, but as a WORKAROUND rather than an API
|
|
18
|
-
// decision — Svelte documents `stickyHeaderIndices` as a KNOWN GAP, so its apps have to compose the
|
|
19
|
-
// wrapper by hand. This adapter auto-wraps flagged children the way React and Vue do, so the escape
|
|
20
|
-
// hatch has nothing to escape. `IStickyHeaderComponentType` stays internal for the same reason even
|
|
21
|
-
// though `IScrollViewProps.StickyHeaderComponent` is typed by it — React has the identical shape.
|
|
22
|
-
export { ScrollView } from './components/scroll-view';
|
|
23
5
|
// The shared list detail types (ISeparators, IViewToken, IViewabilityConfig…) come through the
|
|
24
6
|
// component module rather than straight from '@symbiote-native/components', matching React's own
|
|
25
7
|
// virtualized-list barrel: a consumer typing a renderItem callback or a viewability config reaches
|
|
26
8
|
// for the same import as the component. `IVirtualizedListComponent` stays internal — it is the
|
|
27
|
-
// platform factory's return type, not API
|
|
28
|
-
// out of this barrel.
|
|
9
|
+
// platform factory's return type, not API.
|
|
29
10
|
export { VirtualizedList } from './components/virtualized-list';
|
|
30
11
|
// FlatList carries no platform split of its own — the iOS/Android divergence is
|
|
31
12
|
// VirtualizedList's refreshControlMode, inherited by importing that folder, so Metro picks the
|
|
@@ -39,16 +20,8 @@ export { VirtualizedSectionList } from './components/virtualized-section-list/in
|
|
|
39
20
|
// SectionList declares no input of its own — it is the preset over VirtualizedSectionList, the same
|
|
40
21
|
// relation React's 25-line section-list has to its 262-line virtualized twin.
|
|
41
22
|
export { SectionList } from './components/section-list/index.jsx';
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
export { TouchableNativeFeedback } from './components/touchable-native-feedback/index.jsx';
|
|
48
|
-
// IButtonProps is agnostic and lives in @symbiote-native/components; this re-exports it rather
|
|
49
|
-
// than redeclaring it, matching react/vue/svelte/angular (CLAUDE.md prop-type split).
|
|
50
|
-
export { Button } from './components/button.jsx';
|
|
51
|
-
// Both take `children`, a framework value, so their public prop types are declared per-adapter
|
|
52
|
-
// over the shared agnostic field base — never imported from another adapter.
|
|
53
|
-
export { ImageBackground } from './components/image-background.jsx';
|
|
54
|
-
export { InputAccessoryView } from './components/input-accessory-view.jsx';
|
|
23
|
+
// Android's native ripple/theme-attr feedback. `TouchableNativeFeedback` is now RN's STATIC
|
|
24
|
+
// NAMESPACE, not a component: the element is the tag `<touchable-native-feedback>`, which commits no
|
|
25
|
+
// node of its own and clones onto its single child. `.Ripple(…)` / `.SelectableBackground(…)` are
|
|
26
|
+
// unchanged, and are the SHARED functions every adapter already offered.
|
|
27
|
+
export { TouchableNativeFeedback } from '@symbiote-native/components';
|
|
@@ -45,7 +45,7 @@ import { insert, insertNode, removeNode } from '../renderer.js';
|
|
|
45
45
|
function assertPortalTarget(target) {
|
|
46
46
|
if (target instanceof SymbioteSurface || isSymbioteNode(target))
|
|
47
47
|
return target;
|
|
48
|
-
throw new Error('Portal `mount` must be an already-mounted host node (a ref off a rendered <
|
|
48
|
+
throw new Error('Portal `mount` must be an already-mounted host node (a ref off a rendered <view>) or a surface — got something else. Did you pass the signal instead of CALLING it (`mount={overlay()}`), read it before the target element existed (gate the Portal behind <Show when={overlay()}>), or pass a CSS-selector-style string?');
|
|
49
49
|
}
|
|
50
50
|
export function Portal(props) {
|
|
51
51
|
// One engine ANCHOR per Portal instance, used as a fragment host: it is a real retained node, so
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// So the tree is built ONCE and every node's props are wired through the renderer's own `spread`,
|
|
15
15
|
// which is a render effect that diffs prop-by-prop against the previous values and calls
|
|
16
16
|
// setProperty -> routeProp only for the ones that actually changed. Same mechanism, same node
|
|
17
|
-
// identity, as compiled `<
|
|
17
|
+
// identity, as compiled `<switch value={v()} />`.
|
|
18
18
|
//
|
|
19
19
|
// This relies on `render-*.ts` producing a SHAPE-STABLE Descriptor: same type, same child count,
|
|
20
20
|
// text where text was — only prop VALUES vary between calls. That is already the contract the
|
package/build/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { AppRegistry, setHostRegistrar } from './modules/app-registry';
|
|
|
10
10
|
export type { IComponentProvider, IWrapperComponentProvider, IAppParameters, IRunnable, IHostRegistrar, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from './modules/app-registry';
|
|
11
11
|
export { StatusBar } from './modules/status-bar';
|
|
12
12
|
export type { IStatusBarProps, IStatusBarStyle } from './modules/status-bar';
|
|
13
|
+
export { Image } from './modules/image';
|
|
13
14
|
export { Portal } from './create-portal';
|
|
14
15
|
export type { IPortalProps, IPortalTarget } from './create-portal';
|
|
15
16
|
export { createTunnel } from './create-tunnel';
|
package/build/index.js
CHANGED
|
@@ -36,11 +36,19 @@ export { descriptorToSolid } from './descriptor-to-solid.js';
|
|
|
36
36
|
// Safe in the barrel (unlike ./bootstrap, which imports react-native): the registry seam itself
|
|
37
37
|
// only reaches the engine and solid-js.
|
|
38
38
|
export { AppRegistry, setHostRegistrar } from './modules/app-registry/index.js';
|
|
39
|
-
// Animated: the
|
|
40
|
-
// graph.
|
|
41
|
-
//
|
|
39
|
+
// Animated: the three list/scroll components that are still components, over the engine's
|
|
40
|
+
// framework-agnostic value graph. There is no `Animated.View` / `Animated.Text` / `Animated.Image`
|
|
41
|
+
// — those primitives are tags, and `<view style={{ opacity: someAnimatedValue }}>` is the whole
|
|
42
|
+
// API (.claude/rules/animated-values-resolve-in-the-engine.md). Solid's JSX takes the dotted form
|
|
43
|
+
// of what remains directly — `<Animated.ScrollView/>` compiles to
|
|
44
|
+
// createComponent(Animated.ScrollView, …) — so no local alias is needed, unlike Angular, whose AOT
|
|
42
45
|
// compiler cannot trace a class through property access (.claude/rules/dotted-component-tags.md).
|
|
43
46
|
export { StatusBar } from './modules/status-bar.js';
|
|
47
|
+
// What survives of `Image` once the primitive is the `<image>` tag: RN's statics namespace, no
|
|
48
|
+
// view. Grouped with the runtime modules rather than the components for exactly that reason.
|
|
49
|
+
// `IImageStatics` is not re-exported here — `./components` already carries it, straight from the
|
|
50
|
+
// shared package, and a second export of one name is a build error rather than a convenience.
|
|
51
|
+
export { Image } from './modules/image.js';
|
|
44
52
|
// Portal: same-surface content relocation, the twin of React's createPortal and Angular's
|
|
45
53
|
// PortalDirective/PortalOutletDirective. `mount` takes an already-mounted host node in THIS
|
|
46
54
|
// surface (a ref off a rendered component) or the surface itself; reaching a second, separately
|