@symbiote-native/svelte 0.1.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/LICENSE +21 -0
- package/README.md +161 -0
- package/build/bootstrap.d.ts +7 -0
- package/build/bootstrap.js +17 -0
- package/build/class-value.d.ts +6 -0
- package/build/class-value.js +81 -0
- package/build/components/RefreshControl.svelte +38 -0
- package/build/components/RefreshControl.svelte.d.ts +5 -0
- package/build/components/SafeAreaView.svelte +34 -0
- package/build/components/SafeAreaView.svelte.d.ts +5 -0
- package/build/components/Text.svelte +25 -0
- package/build/components/Text.svelte.d.ts +5 -0
- package/build/components/View.svelte +41 -0
- package/build/components/View.svelte.d.ts +5 -0
- package/build/components/activity-indicator/activity-indicator-platform.android.d.ts +3 -0
- package/build/components/activity-indicator/activity-indicator-platform.android.js +5 -0
- package/build/components/activity-indicator/activity-indicator-platform.d.ts +1 -0
- package/build/components/activity-indicator/activity-indicator-platform.ios.d.ts +3 -0
- package/build/components/activity-indicator/activity-indicator-platform.ios.js +6 -0
- package/build/components/activity-indicator/activity-indicator-platform.js +5 -0
- package/build/components/activity-indicator/activity-indicator-props.d.ts +5 -0
- package/build/components/activity-indicator/activity-indicator-props.js +1 -0
- package/build/components/activity-indicator/index.svelte +57 -0
- package/build/components/activity-indicator/index.svelte.d.ts +5 -0
- package/build/components/button-props.d.ts +5 -0
- package/build/components/button-props.js +1 -0
- package/build/components/button.svelte +71 -0
- package/build/components/button.svelte.d.ts +5 -0
- package/build/components/flat-list/flat-list-props.d.ts +10 -0
- package/build/components/flat-list/flat-list-props.js +1 -0
- package/build/components/flat-list/index.svelte +254 -0
- package/build/components/flat-list/index.svelte.d.ts +79 -0
- package/build/components/image/image-logic.d.ts +2 -0
- package/build/components/image/image-logic.js +125 -0
- package/build/components/image/image-props.d.ts +5 -0
- package/build/components/image/image-props.js +1 -0
- package/build/components/image/index.d.ts +3 -0
- package/build/components/image/index.js +10 -0
- package/build/components/image/index.svelte +66 -0
- package/build/components/image/index.svelte.d.ts +5 -0
- package/build/components/image-background/image-background-props.d.ts +10 -0
- package/build/components/image-background/image-background-props.js +1 -0
- package/build/components/image-background/index.svelte +81 -0
- package/build/components/image-background/index.svelte.d.ts +5 -0
- package/build/components/index.d.ts +50 -0
- package/build/components/index.js +24 -0
- package/build/components/input-accessory-view/index.svelte +48 -0
- package/build/components/input-accessory-view/index.svelte.d.ts +5 -0
- package/build/components/input-accessory-view/input-accessory-view-props.d.ts +11 -0
- package/build/components/input-accessory-view/input-accessory-view-props.js +1 -0
- package/build/components/keyboard-avoiding-view/index.svelte +135 -0
- package/build/components/keyboard-avoiding-view/index.svelte.d.ts +5 -0
- package/build/components/keyboard-avoiding-view/keyboard-avoiding-view-props.d.ts +15 -0
- package/build/components/keyboard-avoiding-view/keyboard-avoiding-view-props.js +1 -0
- package/build/components/modal/index.svelte +126 -0
- package/build/components/modal/index.svelte.d.ts +5 -0
- package/build/components/modal/modal-props.d.ts +24 -0
- package/build/components/modal/modal-props.js +1 -0
- package/build/components/pressable/index.svelte +160 -0
- package/build/components/pressable/index.svelte.d.ts +5 -0
- package/build/components/pressable/pressable-props.d.ts +27 -0
- package/build/components/pressable/pressable-props.js +1 -0
- package/build/components/refresh-control-props.d.ts +19 -0
- package/build/components/refresh-control-props.js +1 -0
- package/build/components/safe-area-view-props.d.ts +10 -0
- package/build/components/safe-area-view-props.js +1 -0
- package/build/components/scroll-view/index.svelte +298 -0
- package/build/components/scroll-view/index.svelte.d.ts +18 -0
- package/build/components/scroll-view/scroll-view-platform-types.d.ts +3 -0
- package/build/components/scroll-view/scroll-view-platform-types.js +1 -0
- package/build/components/scroll-view/scroll-view-platform.android.d.ts +3 -0
- package/build/components/scroll-view/scroll-view-platform.android.js +3 -0
- package/build/components/scroll-view/scroll-view-platform.d.ts +1 -0
- package/build/components/scroll-view/scroll-view-platform.ios.d.ts +3 -0
- package/build/components/scroll-view/scroll-view-platform.ios.js +3 -0
- package/build/components/scroll-view/scroll-view-platform.js +4 -0
- package/build/components/scroll-view/scroll-view-props.d.ts +78 -0
- package/build/components/scroll-view/scroll-view-props.js +1 -0
- package/build/components/scroll-view/scroll-view-sticky-context.d.ts +7 -0
- package/build/components/scroll-view/scroll-view-sticky-context.js +1 -0
- package/build/components/scroll-view/sticky-header-props.d.ts +10 -0
- package/build/components/scroll-view/sticky-header-props.js +1 -0
- package/build/components/scroll-view/sticky-header.svelte +254 -0
- package/build/components/scroll-view/sticky-header.svelte.d.ts +5 -0
- package/build/components/section-list/index.svelte +107 -0
- package/build/components/section-list/index.svelte.d.ts +57 -0
- package/build/components/section-list/section-list-props.d.ts +1 -0
- package/build/components/section-list/section-list-props.js +1 -0
- package/build/components/switch/index.svelte +123 -0
- package/build/components/switch/index.svelte.d.ts +5 -0
- package/build/components/switch/switch-platform-types.d.ts +5 -0
- package/build/components/switch/switch-platform-types.js +1 -0
- package/build/components/switch/switch-platform.android.d.ts +3 -0
- package/build/components/switch/switch-platform.android.js +8 -0
- package/build/components/switch/switch-platform.d.ts +1 -0
- package/build/components/switch/switch-platform.ios.d.ts +3 -0
- package/build/components/switch/switch-platform.ios.js +7 -0
- package/build/components/switch/switch-platform.js +5 -0
- package/build/components/switch/switch-props.d.ts +5 -0
- package/build/components/switch/switch-props.js +1 -0
- package/build/components/text-input/index.svelte +300 -0
- package/build/components/text-input/index.svelte.d.ts +12 -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 +23 -0
- package/build/components/text-props.js +1 -0
- package/build/components/touchable-base-props.d.ts +10 -0
- package/build/components/touchable-base-props.js +1 -0
- package/build/components/touchable-highlight/index.svelte +39 -0
- package/build/components/touchable-highlight/index.svelte.d.ts +5 -0
- package/build/components/touchable-highlight/touchable-highlight-props.d.ts +5 -0
- package/build/components/touchable-highlight/touchable-highlight-props.js +1 -0
- package/build/components/touchable-native-feedback/index.d.ts +9 -0
- package/build/components/touchable-native-feedback/index.js +14 -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-native-feedback/touchable-native-feedback.svelte +44 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback.svelte.d.ts +5 -0
- package/build/components/touchable-opacity/index.svelte +124 -0
- package/build/components/touchable-opacity/index.svelte.d.ts +5 -0
- package/build/components/touchable-opacity/touchable-opacity-props.d.ts +5 -0
- package/build/components/touchable-opacity/touchable-opacity-props.js +1 -0
- package/build/components/touchable-without-feedback/index.svelte +19 -0
- package/build/components/touchable-without-feedback/index.svelte.d.ts +5 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +2 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.js +1 -0
- package/build/components/view-props.d.ts +29 -0
- package/build/components/view-props.js +1 -0
- package/build/components/virtualized-list/index.svelte +702 -0
- package/build/components/virtualized-list/index.svelte.d.ts +79 -0
- package/build/components/virtualized-list/virtualized-list-props.d.ts +70 -0
- package/build/components/virtualized-list/virtualized-list-props.js +73 -0
- package/build/components/virtualized-section-list/index.svelte +206 -0
- package/build/components/virtualized-section-list/index.svelte.d.ts +57 -0
- package/build/components/virtualized-section-list/virtualized-section-list-props.d.ts +62 -0
- package/build/components/virtualized-section-list/virtualized-section-list-props.js +1 -0
- package/build/create-tunnel/index.d.ts +3 -0
- package/build/create-tunnel/index.js +3 -0
- package/build/create-tunnel/tunnel-in.svelte +24 -0
- package/build/create-tunnel/tunnel-in.svelte.d.ts +9 -0
- package/build/create-tunnel/tunnel-out.svelte +14 -0
- package/build/create-tunnel/tunnel-out.svelte.d.ts +7 -0
- package/build/create-tunnel/tunnel.d.ts +9 -0
- package/build/create-tunnel/tunnel.js +9 -0
- package/build/descriptor-to-svelte.d.ts +7 -0
- package/build/descriptor-to-svelte.js +104 -0
- package/build/dom-shim/comment.d.ts +9 -0
- package/build/dom-shim/comment.js +24 -0
- package/build/dom-shim/document-fragment.d.ts +8 -0
- package/build/dom-shim/document-fragment.js +23 -0
- package/build/dom-shim/document.d.ts +23 -0
- package/build/dom-shim/document.js +61 -0
- package/build/dom-shim/element.d.ts +22 -0
- package/build/dom-shim/element.js +130 -0
- package/build/dom-shim/index.d.ts +5 -0
- package/build/dom-shim/index.js +7 -0
- package/build/dom-shim/patch-globals.d.ts +2 -0
- package/build/dom-shim/patch-globals.js +94 -0
- package/build/dom-shim/shim-node.d.ts +30 -0
- package/build/dom-shim/shim-node.js +207 -0
- package/build/dom-shim/text.d.ts +14 -0
- package/build/dom-shim/text.js +97 -0
- package/build/host-instance.d.ts +5 -0
- package/build/host-instance.js +48 -0
- package/build/index.d.ts +28 -0
- package/build/index.js +77 -0
- package/build/modules/accessibility-info/index.android.d.ts +2 -0
- package/build/modules/accessibility-info/index.android.js +4 -0
- package/build/modules/accessibility-info/index.d.ts +1 -0
- package/build/modules/accessibility-info/index.ios.d.ts +2 -0
- package/build/modules/accessibility-info/index.ios.js +3 -0
- package/build/modules/accessibility-info/index.js +5 -0
- package/build/modules/animated/AnimatedImage.svelte +115 -0
- package/build/modules/animated/AnimatedImage.svelte.d.ts +5 -0
- package/build/modules/animated/AnimatedScrollView.svelte +92 -0
- package/build/modules/animated/AnimatedScrollView.svelte.d.ts +17 -0
- package/build/modules/animated/AnimatedText.svelte +58 -0
- package/build/modules/animated/AnimatedText.svelte.d.ts +5 -0
- package/build/modules/animated/AnimatedView.svelte +95 -0
- package/build/modules/animated/AnimatedView.svelte.d.ts +5 -0
- package/build/modules/animated/animated-component-props.d.ts +7 -0
- package/build/modules/animated/animated-component-props.js +1 -0
- package/build/modules/animated/index.d.ts +58 -0
- package/build/modules/animated/index.js +69 -0
- package/build/modules/app-registry/index.d.ts +7 -0
- package/build/modules/app-registry/index.js +28 -0
- package/build/modules/status-bar/index.d.ts +5 -0
- package/build/modules/status-bar/index.js +21 -0
- package/build/modules/status-bar/index.svelte +20 -0
- package/build/modules/status-bar/index.svelte.d.ts +4 -0
- package/build/native-view-bridge.d.ts +4 -0
- package/build/native-view-bridge.js +27 -0
- package/build/preprocessor/forbid-web-only-constructs.d.ts +8 -0
- package/build/preprocessor/forbid-web-only-constructs.js +204 -0
- package/build/preprocessor/scoped-styles.d.ts +10 -0
- package/build/preprocessor/scoped-styles.js +399 -0
- package/build/render.d.ts +7 -0
- package/build/render.js +46 -0
- package/build/root-element.d.ts +3 -0
- package/build/root-element.js +27 -0
- package/build/runes/attachments.d.ts +5 -0
- package/build/runes/attachments.js +73 -0
- package/build/runes/dimensions-value.d.ts +4 -0
- package/build/runes/dimensions-value.js +30 -0
- package/build/runes/media-query.d.ts +8 -0
- package/build/runes/media-query.js +38 -0
- package/build/runes/use-color-scheme.svelte.d.ts +4 -0
- package/build/runes/use-color-scheme.svelte.js +25 -0
- package/build/runes/use-window-dimensions.svelte.d.ts +4 -0
- package/build/runes/use-window-dimensions.svelte.js +43 -0
- package/build/runes/window.d.ts +6 -0
- package/build/runes/window.js +28 -0
- package/build/scope-token.d.ts +2 -0
- package/build/scope-token.js +33 -0
- package/build/style-scope.d.ts +1 -0
- package/build/style-scope.js +29 -0
- package/metro-css-parser.cjs +9 -0
- package/metro-svelte-transformer.cjs +156 -0
- package/package.json +83 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AnimatedValue } from '@symbiote-native/engine';
|
|
2
|
+
export declare const SCROLL_VIEW_STICKY_CONTEXT_KEY: unique symbol;
|
|
3
|
+
export type IScrollViewStickyContext = {
|
|
4
|
+
scrollAnimatedValue: AnimatedValue;
|
|
5
|
+
getInverted: () => boolean | undefined;
|
|
6
|
+
getViewportHeight: () => number | undefined;
|
|
7
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SCROLL_VIEW_STICKY_CONTEXT_KEY = Symbol('symbiote-scroll-view-sticky');
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { AnimatedValue, ISymbioteEvent } from '@symbiote-native/engine';
|
|
3
|
+
export type IStickyHeaderComponentProps = {
|
|
4
|
+
nextHeaderLayoutY?: number;
|
|
5
|
+
onLayout?: (event: ISymbioteEvent) => void;
|
|
6
|
+
scrollAnimatedValue?: AnimatedValue;
|
|
7
|
+
inverted?: boolean;
|
|
8
|
+
scrollViewHeight?: number;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
// ScrollViewStickyHeader: the Svelte twin of React's/Vue's per-header sticky component
|
|
3
|
+
// (adapters/react/src/components/scroll-view/sticky-header.tsx,
|
|
4
|
+
// adapters/vue/src/components/scroll-view/sticky-header.ts). Drives the shared `reduceSticky`
|
|
5
|
+
// state machine (@symbiote-native/components/state/sticky-header-reducer); this component
|
|
6
|
+
// supplies only Svelte's lifecycle: the folded state cell, the interpolation + listener wiring,
|
|
7
|
+
// the debounce setTimeout, and a reactive re-render trigger.
|
|
8
|
+
//
|
|
9
|
+
// NOT auto-wrapped by `stickyHeaderIndices` on ScrollView (it only ever sees an opaque children
|
|
10
|
+
// Snippet, nothing to index/wrap - see scroll-view-props.ts's KNOWN GAP). App code composes
|
|
11
|
+
// this directly around the section that should stick:
|
|
12
|
+
//
|
|
13
|
+
// <ScrollView>
|
|
14
|
+
// <ScrollViewStickyHeader>
|
|
15
|
+
// <Text>Section A</Text>
|
|
16
|
+
// </ScrollViewStickyHeader>
|
|
17
|
+
// <Row />
|
|
18
|
+
// ...
|
|
19
|
+
// </ScrollView>
|
|
20
|
+
//
|
|
21
|
+
// (VirtualizedList's own stickyHeaderIndices IS auto-wrapped, since it walks an indexable cell
|
|
22
|
+
// list - see virtualized-list/index.svelte.)
|
|
23
|
+
//
|
|
24
|
+
// `nextHeaderLayoutY` (the collision point where this header gets pushed off by the next sticky
|
|
25
|
+
// header) has no automatic cross-talk wiring for a manually-composed header here (no
|
|
26
|
+
// headerLayoutYs map exists without auto-wrapping). Left undefined, a header sticks
|
|
27
|
+
// indefinitely once pinned - correct for the common single-header case. An app with multiple
|
|
28
|
+
// manually-composed headers can pass it explicitly if it tracks the next header's own y.
|
|
29
|
+
//
|
|
30
|
+
// Native-Animated-driven, like React's ScrollViewStickyHeader.js: the pinned translateY rides
|
|
31
|
+
// as the raw AnimatedInterpolation node in style.transform on a real Animated.View
|
|
32
|
+
// (AnimatedView.svelte), whose `__makeNative()` connects it straight to the native shadow tree,
|
|
33
|
+
// so once native the pin tracks scroll on the UI thread with zero JS per frame - the
|
|
34
|
+
// interpolation's own .addListener() below only drives the separate debounced
|
|
35
|
+
// `passthroughAnimatedPropExplicitValues` (RN's own hit-testing sync).
|
|
36
|
+
//
|
|
37
|
+
// reduceSticky's 'layout' case carries an `alreadyAtThisGeometry` guard (sticky-header-reducer.ts):
|
|
38
|
+
// without it, a redundant onLayout reporting the SAME y/height (which every onLayout consumer
|
|
39
|
+
// must tolerate) rebuilds the interpolation graph and reconnects it natively on every pass, and
|
|
40
|
+
// each such commit can itself provoke another relayout - an unbounded same-tick ping-pong that
|
|
41
|
+
// crashes with Svelte's `effect_update_depth_exceeded`. Fixed at the shared layer so every
|
|
42
|
+
// adapter gets it, not just this one.
|
|
43
|
+
//
|
|
44
|
+
// Bootstrap note: the FIRST tick must reach this component's interpolation via JS (the parent's
|
|
45
|
+
// own scroll-forwarding, NOT `attachStickyScroll`'s native event attach) — `__makeNative()`
|
|
46
|
+
// cascades to its PARENT first (interpolation-node.ts), so once `wantsNative` flips true off
|
|
47
|
+
// the first settled debounce, the whole chain promotes to native automatically. If
|
|
48
|
+
// `scrollAnimatedValue` were made native UP FRONT, its JS listener cascade would be permanently
|
|
49
|
+
// disabled (AnimatedWithChildren.__callListeners skips children once isNative) before a single
|
|
50
|
+
// tick ever reached this component - a bootstrap deadlock, not a flaky native module. This is
|
|
51
|
+
// why ScrollView/VirtualizedList force `nativeStickyAvailable = false`: the correct bootstrap
|
|
52
|
+
// path, not a workaround.
|
|
53
|
+
import type { IStickyHeaderComponentProps } from './sticky-header-props';
|
|
54
|
+
|
|
55
|
+
export type { IStickyHeaderComponentProps };
|
|
56
|
+
|
|
57
|
+
// Diagnostic-only: a module-scope counter so every instance across every ScrollViewStickyHeader
|
|
58
|
+
// in a device log is individually identifiable (see the mount/unmount dlog calls below).
|
|
59
|
+
let stickyHeaderInstanceSeq = 0;
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<script lang="ts">
|
|
63
|
+
import { getContext } from 'svelte';
|
|
64
|
+
import {
|
|
65
|
+
createInitialStickyState,
|
|
66
|
+
reduceSticky,
|
|
67
|
+
readLayoutNumber,
|
|
68
|
+
STICKY_HEADER_Z_INDEX,
|
|
69
|
+
type IStickyAction,
|
|
70
|
+
type IStickyEffect,
|
|
71
|
+
type IStickyReducerInputs,
|
|
72
|
+
} from '@symbiote-native/components';
|
|
73
|
+
import { AnimatedValue, Platform, dlog, type AnimatedInterpolation, type ISymbioteEvent } from '@symbiote-native/engine';
|
|
74
|
+
import AnimatedView from '../../modules/animated/AnimatedView.svelte';
|
|
75
|
+
import {
|
|
76
|
+
SCROLL_VIEW_STICKY_CONTEXT_KEY,
|
|
77
|
+
type IScrollViewStickyContext,
|
|
78
|
+
} from './scroll-view-sticky-context';
|
|
79
|
+
import { pickAttachmentProps } from '../../runes/attachments';
|
|
80
|
+
|
|
81
|
+
let {
|
|
82
|
+
nextHeaderLayoutY,
|
|
83
|
+
onLayout: onLayoutProp,
|
|
84
|
+
scrollAnimatedValue: scrollAnimatedValueProp,
|
|
85
|
+
inverted: invertedProp,
|
|
86
|
+
scrollViewHeight: scrollViewHeightProp,
|
|
87
|
+
children,
|
|
88
|
+
...passthrough
|
|
89
|
+
}: IStickyHeaderComponentProps = $props();
|
|
90
|
+
|
|
91
|
+
// Resolve the scroll wiring from the parent ScrollView's context when not given explicitly.
|
|
92
|
+
// A fresh standalone AnimatedValue is the fallback for a header used outside a ScrollView
|
|
93
|
+
// (never sticks, but never crashes either).
|
|
94
|
+
const stickyContext = getContext<IScrollViewStickyContext | undefined>(SCROLL_VIEW_STICKY_CONTEXT_KEY);
|
|
95
|
+
$effect(() => {
|
|
96
|
+
if (stickyContext === undefined && scrollAnimatedValueProp === undefined) {
|
|
97
|
+
dlog('ScrollViewStickyHeader used outside a ScrollView — sticky positioning is a no-op');
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// svelte-ignore state_referenced_locally -- captured ONCE by identity; it does not change
|
|
101
|
+
// after mount, so re-deriving it would be wrong.
|
|
102
|
+
const scrollAnimatedValue =
|
|
103
|
+
scrollAnimatedValueProp ?? stickyContext?.scrollAnimatedValue ?? new AnimatedValue(0);
|
|
104
|
+
const inverted = $derived(invertedProp ?? stickyContext?.getInverted());
|
|
105
|
+
const scrollViewHeight = $derived(scrollViewHeightProp ?? stickyContext?.getViewportHeight());
|
|
106
|
+
|
|
107
|
+
// The one folded state cell (RN's scattered useState/useRef collapsed into IStickyHeaderState),
|
|
108
|
+
// mutated in place by reduceSticky. Named `stickyState`, not `state`: a local binding literally
|
|
109
|
+
// called `state` collides with the `$state` rune (Svelte's store_rune_conflict warning).
|
|
110
|
+
const stickyState = createInitialStickyState();
|
|
111
|
+
// Bumped on every 'apply-passthrough' effect so the passthrough $derived below re-reads
|
|
112
|
+
// stickyState.translateY (RN's forceRender()).
|
|
113
|
+
let version = $state(0);
|
|
114
|
+
|
|
115
|
+
// The interpolation node currently bound into style.transform — held by IDENTITY ($state.raw),
|
|
116
|
+
// rebuilt on every 'rebuild-interpolation' effect.
|
|
117
|
+
let animatedTranslateY = $state.raw<AnimatedInterpolation>(
|
|
118
|
+
scrollAnimatedValue.interpolate({ inputRange: [-1, 0], outputRange: [0, 0] }),
|
|
119
|
+
);
|
|
120
|
+
let listenerId: string | undefined;
|
|
121
|
+
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
122
|
+
|
|
123
|
+
function inputs(): IStickyReducerInputs {
|
|
124
|
+
return { os: Platform.OS, inverted, scrollViewHeight, nextHeaderLayoutY };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function runEffects(effects: IStickyEffect[]): void {
|
|
128
|
+
for (const effect of effects) {
|
|
129
|
+
switch (effect.kind) {
|
|
130
|
+
case 'rebuild-interpolation': {
|
|
131
|
+
// Logs the actual input/output ranges (not just `changedKeys=style`) so a device log
|
|
132
|
+
// shows what gets built right at a header collision; own layoutY identifies which
|
|
133
|
+
// header this is.
|
|
134
|
+
dlog(
|
|
135
|
+
`ScrollViewStickyHeader[y=${stickyState.layoutY}] rebuild-interpolation ` +
|
|
136
|
+
`inputRange=${JSON.stringify(effect.inputRange)} outputRange=${JSON.stringify(effect.outputRange)} ` +
|
|
137
|
+
`nextHeaderLayoutY=${String(nextHeaderLayoutY)}`,
|
|
138
|
+
);
|
|
139
|
+
// Detach the old listener, build a fresh interpolation onto the shared scroll value, and
|
|
140
|
+
// wire the settled-value listener (drives ONLY the debounced hit-testing passthrough
|
|
141
|
+
// below — the visible pin rides the native AnimatedProps connection in the markup).
|
|
142
|
+
if (listenerId !== undefined) animatedTranslateY.removeListener(listenerId);
|
|
143
|
+
const next = scrollAnimatedValue.interpolate({
|
|
144
|
+
inputRange: effect.inputRange,
|
|
145
|
+
outputRange: effect.outputRange,
|
|
146
|
+
});
|
|
147
|
+
listenerId = next.addListener(({ value }: { value: number | string }): void => {
|
|
148
|
+
if (typeof value === 'number') dispatch({ kind: 'animated-tick', value });
|
|
149
|
+
});
|
|
150
|
+
animatedTranslateY = next;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case 'schedule-debounce':
|
|
154
|
+
if (debounceTimer !== undefined) clearTimeout(debounceTimer);
|
|
155
|
+
debounceTimer = setTimeout(() => {
|
|
156
|
+
debounceTimer = undefined;
|
|
157
|
+
dispatch({ kind: 'debounce-fired', value: effect.value });
|
|
158
|
+
}, effect.delay);
|
|
159
|
+
break;
|
|
160
|
+
case 'apply-passthrough':
|
|
161
|
+
// The actual committed translateY — a freeze at a huge/wrong number here is the
|
|
162
|
+
// visible-disappearance symptom to watch for.
|
|
163
|
+
dlog(`ScrollViewStickyHeader[y=${stickyState.layoutY}] apply-passthrough translateY=${effect.translateY}`);
|
|
164
|
+
version += 1;
|
|
165
|
+
break;
|
|
166
|
+
case 'record-header-y':
|
|
167
|
+
// No auto-wrap cross-talk map in this adapter (see the module header comment) — nothing
|
|
168
|
+
// to record into.
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function dispatch(action: IStickyAction): void {
|
|
175
|
+
runEffects(reduceSticky(stickyState, action, inputs()).effects);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Rebuild when the collision/viewport inputs change; also does the initial identity build.
|
|
179
|
+
$effect(() => {
|
|
180
|
+
void inverted;
|
|
181
|
+
void scrollViewHeight;
|
|
182
|
+
void nextHeaderLayoutY;
|
|
183
|
+
dispatch({ kind: 'inputs-changed' });
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Logs mount/unmount so a device log shows whether a header index gets destroyed+recreated (a
|
|
187
|
+
// fresh instance id right after an unmount of the same index) as it crosses the
|
|
188
|
+
// forcedStickyCell/windowed-cell boundary in VirtualizedList's {#each}, or stays one instance.
|
|
189
|
+
const instanceId = ++stickyHeaderInstanceSeq;
|
|
190
|
+
dlog(`ScrollViewStickyHeader#${instanceId} mount`);
|
|
191
|
+
|
|
192
|
+
// Detach the listener + clear the debounce on unmount.
|
|
193
|
+
$effect(() => {
|
|
194
|
+
return (): void => {
|
|
195
|
+
dlog(`ScrollViewStickyHeader#${instanceId}[y=${stickyState.layoutY}] unmount`);
|
|
196
|
+
if (listenerId !== undefined) animatedTranslateY.removeListener(listenerId);
|
|
197
|
+
if (debounceTimer !== undefined) clearTimeout(debounceTimer);
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
function handleLayout(event: ISymbioteEvent): void {
|
|
202
|
+
const y = readLayoutNumber(event, 'y');
|
|
203
|
+
const height = readLayoutNumber(event, 'height');
|
|
204
|
+
// Keep the previous value when a field is absent (RN sets state only on a defined read).
|
|
205
|
+
dispatch({
|
|
206
|
+
kind: 'layout',
|
|
207
|
+
y: y ?? stickyState.layoutY,
|
|
208
|
+
height: height ?? stickyState.layoutHeight,
|
|
209
|
+
});
|
|
210
|
+
dlog(`ScrollViewStickyHeader layout y=${y} height=${height}`);
|
|
211
|
+
onLayoutProp?.(event);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// The EXPLICIT debounced translateY overrides the committed transform for hit-testing, while
|
|
215
|
+
// `animatedTranslateY` (in the markup below) does the smooth native-driven pin — per RN
|
|
216
|
+
// ScrollViewStickyHeader.js, and AnimatedView.svelte's own passthroughAnimatedPropExplicitValues
|
|
217
|
+
// contract (readPassthroughStyle).
|
|
218
|
+
const passthroughAnimatedPropExplicitValues = $derived.by(() => {
|
|
219
|
+
void version;
|
|
220
|
+
return stickyState.translateY !== null
|
|
221
|
+
? { style: { transform: [{ translateY: stickyState.translateY }] } }
|
|
222
|
+
: null;
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Kept as ITS OWN derived, reading only `animatedTranslateY` — NOT folded into `bag` below.
|
|
226
|
+
// `bag` also carries `passthroughAnimatedPropExplicitValues`, which changes value on every
|
|
227
|
+
// 'apply-passthrough' tick (every scroll frame); if `style` lived in that same derived it would
|
|
228
|
+
// get a fresh object identity on every tick even though the interpolation node never changed.
|
|
229
|
+
// animated-props-runtime.ts's reconcile() relies on `style`'s reference staying stable across
|
|
230
|
+
// passthrough-only ticks to skip rebuilding the native AnimatedProps graph — without this
|
|
231
|
+
// split, every scroll frame tore down and reconnected the native node, and a reconnect right as
|
|
232
|
+
// scrolling stopped left the header frozen at its post-reset default until the next tick.
|
|
233
|
+
const animatedStyle = $derived.by(() => ({
|
|
234
|
+
transform: [{ translateY: animatedTranslateY }],
|
|
235
|
+
zIndex: STICKY_HEADER_Z_INDEX,
|
|
236
|
+
}));
|
|
237
|
+
|
|
238
|
+
// collapsable:false keeps the wrapper a real Yoga node; zIndex makes the pinned header paint
|
|
239
|
+
// OVER the rows scrolling under it.
|
|
240
|
+
const bag = $derived.by(() => ({
|
|
241
|
+
style: animatedStyle,
|
|
242
|
+
onLayout: handleLayout,
|
|
243
|
+
collapsable: false,
|
|
244
|
+
passthroughAnimatedPropExplicitValues,
|
|
245
|
+
}));
|
|
246
|
+
|
|
247
|
+
// `{@attach}` rides in the rest object as a symbol-keyed entry; forwarded onto AnimatedView,
|
|
248
|
+
// which owns this component's actual host node.
|
|
249
|
+
const attachments = $derived(pickAttachmentProps(passthrough));
|
|
250
|
+
</script>
|
|
251
|
+
|
|
252
|
+
<AnimatedView {...bag} {...attachments}>
|
|
253
|
+
{@render children?.()}
|
|
254
|
+
</AnimatedView>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IStickyHeaderComponentProps } from './sticky-header-props';
|
|
2
|
+
export type { IStickyHeaderComponentProps };
|
|
3
|
+
declare const StickyHeader: import("svelte").Component<IStickyHeaderComponentProps, {}, "">;
|
|
4
|
+
type StickyHeader = ReturnType<typeof StickyHeader>;
|
|
5
|
+
export default StickyHeader;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
// SectionList: the public list-of-sections component. A thin wrapper over
|
|
3
|
+
// VirtualizedSectionList, mirroring RN's layering (SectionList -> VirtualizedSectionList ->
|
|
4
|
+
// VirtualizedList). All section-flattening / windowing / imperative-scroll logic lives below;
|
|
5
|
+
// this layer re-exposes the same surface under the SectionList name and delegates the handle
|
|
6
|
+
// (a Svelte `bind:this` on THIS component resolves to whatever THIS component exports, so the
|
|
7
|
+
// wrapper must re-declare + delegate rather than re-exporting the child's exports directly) —
|
|
8
|
+
// the Svelte twin of the React/Vue SectionList.
|
|
9
|
+
import type { ISectionListProps, ISectionListHandle, ISection } from './section-list-props';
|
|
10
|
+
|
|
11
|
+
export type { ISectionListProps, ISectionListHandle, ISection };
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<script lang="ts" generics="ItemT">
|
|
15
|
+
import VirtualizedSectionList from '../virtualized-section-list/index.svelte';
|
|
16
|
+
import { pickAccessibilityProps } from '../virtualized-list/virtualized-list-props';
|
|
17
|
+
import type {
|
|
18
|
+
IVirtualizedSectionListHandle,
|
|
19
|
+
IScrollViewHandle,
|
|
20
|
+
} from '../virtualized-list/virtualized-list-props';
|
|
21
|
+
import type { ISectionListProps as IProps } from './section-list-props';
|
|
22
|
+
import { pickAttachmentProps } from '../../runes/attachments';
|
|
23
|
+
import type { ISymbioteNode } from '@symbiote-native/engine';
|
|
24
|
+
|
|
25
|
+
let props: IProps<ItemT> = $props();
|
|
26
|
+
|
|
27
|
+
let inner = $state.raw<IVirtualizedSectionListHandle | null>(null);
|
|
28
|
+
|
|
29
|
+
// Forwarded as a component-prop spread onto VirtualizedSectionList (a compiled Svelte
|
|
30
|
+
// component, not a symbiote-* host tag — see flat-list/index.svelte's identical comment).
|
|
31
|
+
const accessibilityProps = $derived(pickAccessibilityProps(props));
|
|
32
|
+
|
|
33
|
+
export function scrollToLocation(params: {
|
|
34
|
+
sectionIndex: number;
|
|
35
|
+
itemIndex: number;
|
|
36
|
+
viewOffset?: number;
|
|
37
|
+
viewPosition?: number;
|
|
38
|
+
animated?: boolean;
|
|
39
|
+
}): void {
|
|
40
|
+
inner?.scrollToLocation(params);
|
|
41
|
+
}
|
|
42
|
+
export function flashScrollIndicators(): void {
|
|
43
|
+
inner?.flashScrollIndicators();
|
|
44
|
+
}
|
|
45
|
+
export function getNativeScrollRef(): IScrollViewHandle | null {
|
|
46
|
+
return inner?.getNativeScrollRef() ?? null;
|
|
47
|
+
}
|
|
48
|
+
export function getScrollableNode(): IScrollViewHandle | null {
|
|
49
|
+
return inner?.getScrollableNode() ?? null;
|
|
50
|
+
}
|
|
51
|
+
export function getScrollResponder(): IScrollViewHandle | null {
|
|
52
|
+
return inner?.getScrollResponder() ?? null;
|
|
53
|
+
}
|
|
54
|
+
export function getScrollNode(): ISymbioteNode | null {
|
|
55
|
+
return inner?.getScrollNode() ?? null;
|
|
56
|
+
}
|
|
57
|
+
export function recordInteraction(): void {
|
|
58
|
+
inner?.recordInteraction();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// `{@attach}` arrives as a symbol-keyed prop, which naming individual props below drops.
|
|
62
|
+
// Re-spread just those onto the inner list, which owns the real host node.
|
|
63
|
+
const attachments = $derived(pickAttachmentProps(props));
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<VirtualizedSectionList
|
|
67
|
+
bind:this={inner}
|
|
68
|
+
{...accessibilityProps}
|
|
69
|
+
{...attachments}
|
|
70
|
+
sections={props.sections}
|
|
71
|
+
item={props.item}
|
|
72
|
+
sectionHeader={props.sectionHeader}
|
|
73
|
+
sectionFooter={props.sectionFooter}
|
|
74
|
+
sectionSeparator={props.sectionSeparator}
|
|
75
|
+
separator={props.separator}
|
|
76
|
+
header={props.header}
|
|
77
|
+
footer={props.footer}
|
|
78
|
+
empty={props.empty}
|
|
79
|
+
keyExtractor={props.keyExtractor}
|
|
80
|
+
stickySectionHeadersEnabled={props.stickySectionHeadersEnabled}
|
|
81
|
+
inverted={props.inverted}
|
|
82
|
+
extraData={props.extraData}
|
|
83
|
+
onEndReached={props.onEndReached}
|
|
84
|
+
onEndReachedThreshold={props.onEndReachedThreshold}
|
|
85
|
+
onStartReached={props.onStartReached}
|
|
86
|
+
onStartReachedThreshold={props.onStartReachedThreshold}
|
|
87
|
+
onRefresh={props.onRefresh}
|
|
88
|
+
refreshing={props.refreshing}
|
|
89
|
+
progressViewOffset={props.progressViewOffset}
|
|
90
|
+
initialNumToRender={props.initialNumToRender}
|
|
91
|
+
initialScrollIndex={props.initialScrollIndex}
|
|
92
|
+
maxToRenderPerBatch={props.maxToRenderPerBatch}
|
|
93
|
+
updateCellsBatchingPeriod={props.updateCellsBatchingPeriod}
|
|
94
|
+
windowSize={props.windowSize}
|
|
95
|
+
maintainVisibleContentPosition={props.maintainVisibleContentPosition}
|
|
96
|
+
onScroll={props.onScroll}
|
|
97
|
+
onScrollBeginDrag={props.onScrollBeginDrag}
|
|
98
|
+
onScrollEndDrag={props.onScrollEndDrag}
|
|
99
|
+
onMomentumScrollBegin={props.onMomentumScrollBegin}
|
|
100
|
+
onMomentumScrollEnd={props.onMomentumScrollEnd}
|
|
101
|
+
scrollEventThrottle={props.scrollEventThrottle}
|
|
102
|
+
keyboardShouldPersistTaps={props.keyboardShouldPersistTaps}
|
|
103
|
+
keyboardDismissMode={props.keyboardDismissMode}
|
|
104
|
+
style={props.style}
|
|
105
|
+
contentContainerStyle={props.contentContainerStyle}
|
|
106
|
+
class={props.class}
|
|
107
|
+
/>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ISectionListProps, ISectionListHandle, ISection } from './section-list-props';
|
|
2
|
+
export type { ISectionListProps, ISectionListHandle, ISection };
|
|
3
|
+
import type { IScrollViewHandle } from '../virtualized-list/virtualized-list-props';
|
|
4
|
+
import type { ISectionListProps as IProps } from './section-list-props';
|
|
5
|
+
import type { ISymbioteNode } from '@symbiote-native/engine';
|
|
6
|
+
declare function $$render<ItemT>(): {
|
|
7
|
+
props: IProps<ItemT>;
|
|
8
|
+
exports: {
|
|
9
|
+
scrollToLocation: (params: {
|
|
10
|
+
sectionIndex: number;
|
|
11
|
+
itemIndex: number;
|
|
12
|
+
viewOffset?: number;
|
|
13
|
+
viewPosition?: number;
|
|
14
|
+
animated?: boolean;
|
|
15
|
+
}) => void;
|
|
16
|
+
flashScrollIndicators: () => void;
|
|
17
|
+
getNativeScrollRef: () => IScrollViewHandle | null;
|
|
18
|
+
getScrollableNode: () => IScrollViewHandle | null;
|
|
19
|
+
getScrollResponder: () => IScrollViewHandle | null;
|
|
20
|
+
getScrollNode: () => ISymbioteNode | null;
|
|
21
|
+
recordInteraction: () => void;
|
|
22
|
+
};
|
|
23
|
+
bindings: "";
|
|
24
|
+
slots: {};
|
|
25
|
+
events: {};
|
|
26
|
+
};
|
|
27
|
+
declare class __sveltets_Render<ItemT> {
|
|
28
|
+
props(): ReturnType<typeof $$render<ItemT>>['props'];
|
|
29
|
+
events(): ReturnType<typeof $$render<ItemT>>['events'];
|
|
30
|
+
slots(): ReturnType<typeof $$render<ItemT>>['slots'];
|
|
31
|
+
bindings(): "";
|
|
32
|
+
exports(): {
|
|
33
|
+
scrollToLocation: (params: {
|
|
34
|
+
sectionIndex: number;
|
|
35
|
+
itemIndex: number;
|
|
36
|
+
viewOffset?: number;
|
|
37
|
+
viewPosition?: number;
|
|
38
|
+
animated?: boolean;
|
|
39
|
+
}) => void;
|
|
40
|
+
flashScrollIndicators: () => void;
|
|
41
|
+
getNativeScrollRef: () => IScrollViewHandle | null;
|
|
42
|
+
getScrollableNode: () => IScrollViewHandle | null;
|
|
43
|
+
getScrollResponder: () => IScrollViewHandle | null;
|
|
44
|
+
getScrollNode: () => ISymbioteNode | null;
|
|
45
|
+
recordInteraction: () => void;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
interface $$IsomorphicComponent {
|
|
49
|
+
new <ItemT>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<ItemT>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<ItemT>['props']>, ReturnType<__sveltets_Render<ItemT>['events']>, ReturnType<__sveltets_Render<ItemT>['slots']>> & {
|
|
50
|
+
$$bindings?: ReturnType<__sveltets_Render<ItemT>['bindings']>;
|
|
51
|
+
} & ReturnType<__sveltets_Render<ItemT>['exports']>;
|
|
52
|
+
<ItemT>(internal: unknown, props: ReturnType<__sveltets_Render<ItemT>['props']> & {}): ReturnType<__sveltets_Render<ItemT>['exports']>;
|
|
53
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
54
|
+
}
|
|
55
|
+
declare const Index: $$IsomorphicComponent;
|
|
56
|
+
type Index<ItemT> = InstanceType<typeof Index<ItemT>>;
|
|
57
|
+
export default Index;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { IVirtualizedSectionListProps as ISectionListProps, IVirtualizedSectionListHandle as ISectionListHandle, ISection, } from '../virtualized-section-list/virtualized-section-list-props';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
// Switch: the first STATE-machine component in the Svelte adapter (View/Text are
|
|
3
|
+
// render-only). Reuses the shared logic verbatim (switchReducer / valueFromChange /
|
|
4
|
+
// shouldSnapBack, exactly like React's useReducer + Vue's ref-based reducer wiring) and
|
|
5
|
+
// calls renderSwitch() itself for the prop assembly — its Descriptor is always exactly one
|
|
6
|
+
// `symbiote-switch` node with zero children (core/components/src/view/render-switch.ts). Root
|
|
7
|
+
// stays a literal tag (bind:this needs a known tag); `createDescriptorChildrenSync` is still
|
|
8
|
+
// wired for its (always-empty) children, matching every other category-1 component uniformly
|
|
9
|
+
// — the same reason React still routes a childless Switch through descriptorToReact rather
|
|
10
|
+
// than special-casing it. See svelte-adapter-dom-shim skill §19.
|
|
11
|
+
//
|
|
12
|
+
// `value` is `$bindable()` (2026-08-15): `<Switch bind:value={x}>` round-trips a native toggle
|
|
13
|
+
// into `x` with no extra plumbing at the call site. See handleChange's own comment for exactly
|
|
14
|
+
// when the echo fires and why it is gated on `onValueChange` being absent — the full reasoning
|
|
15
|
+
// (and why an ungated echo would silently defeat the existing snap-back correction) lives next
|
|
16
|
+
// to TextInput's identical mechanism in text-input/index.svelte, plus the skill's §7 note.
|
|
17
|
+
import type { ISwitchProps } from './switch-props';
|
|
18
|
+
|
|
19
|
+
export type { ISwitchProps };
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<script lang="ts">
|
|
23
|
+
import {
|
|
24
|
+
switchReducer,
|
|
25
|
+
createInitialSwitchState,
|
|
26
|
+
shouldSnapBack,
|
|
27
|
+
valueFromChange,
|
|
28
|
+
renderSwitch,
|
|
29
|
+
} from '@symbiote-native/components';
|
|
30
|
+
import { dispatchViewCommand, dlog, type ISymbioteEvent } from '@symbiote-native/engine';
|
|
31
|
+
import { PLATFORM } from './switch-platform';
|
|
32
|
+
import { createDescriptorChildrenSync } from '../../descriptor-to-svelte';
|
|
33
|
+
import { createAttachmentsSync } from '../../runes/attachments';
|
|
34
|
+
import type { ShimElement } from '../../dom-shim';
|
|
35
|
+
|
|
36
|
+
let {
|
|
37
|
+
value = $bindable(),
|
|
38
|
+
onValueChange,
|
|
39
|
+
disabled,
|
|
40
|
+
trackColor,
|
|
41
|
+
thumbColor,
|
|
42
|
+
ios_backgroundColor,
|
|
43
|
+
style,
|
|
44
|
+
class: className,
|
|
45
|
+
...passthrough
|
|
46
|
+
}: ISwitchProps = $props();
|
|
47
|
+
|
|
48
|
+
// $state.raw, NOT $state: this holds the shim element by IDENTITY (the same
|
|
49
|
+
// shallowRef-not-ref concern Vue's switch/shared.ts documents). $state() would deep-proxy
|
|
50
|
+
// the object, and every imperative command (dispatchViewCommand) reads `.engineNode` off
|
|
51
|
+
// the RAW ShimElement the engine's WeakMap-keyed mirror actually knows about — a Proxy
|
|
52
|
+
// wrapper would miss on every lookup and silently no-op.
|
|
53
|
+
let hostShim = $state.raw<ShimElement | null>(null);
|
|
54
|
+
let switchState = $state(createInitialSwitchState());
|
|
55
|
+
|
|
56
|
+
const fabricValue = $derived(value === true);
|
|
57
|
+
|
|
58
|
+
function handleChange(event: ISymbioteEvent): void {
|
|
59
|
+
const next = valueFromChange(event);
|
|
60
|
+
dlog(
|
|
61
|
+
`Switch onChange value=${String(next)} eventCount=${String(event.nativeEvent.eventCount)}`,
|
|
62
|
+
);
|
|
63
|
+
if (next === undefined) return;
|
|
64
|
+
onValueChange?.(next, event);
|
|
65
|
+
// $bindable() sugar: with no onValueChange nothing could reject this report, so mirror it
|
|
66
|
+
// straight into the bound value. A caller that ALSO supplies onValueChange keeps full
|
|
67
|
+
// accept/reject control via the snap-back effect below — see TextInput's identical
|
|
68
|
+
// mechanism (text-input/index.svelte) for why echoing unconditionally would break that.
|
|
69
|
+
if (onValueChange === undefined) value = next;
|
|
70
|
+
switchState = switchReducer(switchState, { type: 'native-reported', value: next });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Snap-back: when native reported a value the parent rejected (the value prop did not
|
|
74
|
+
// change), command the JS value back down — the controlled-Switch correction RN does via
|
|
75
|
+
// SwitchCommands.setValue/setNativeValue. Mirrors React's useLayoutEffect / Vue's post-flush
|
|
76
|
+
// watch; Svelte's $effect reruns whenever switchState or fabricValue changes, which is the
|
|
77
|
+
// same trigger set. Verified against a real compiled mount
|
|
78
|
+
// (mount-pipeline.smoke.test.ts + switch.smoke.test.ts): the shim's insertOne() calls
|
|
79
|
+
// makeLive() synchronously as part of the SAME appendChild the compiler emits before
|
|
80
|
+
// bind:this fires, so hostShim.engineNode is always populated by the time this first runs.
|
|
81
|
+
$effect(() => {
|
|
82
|
+
const engineNode = hostShim?.engineNode;
|
|
83
|
+
if (engineNode === undefined) return;
|
|
84
|
+
if (!shouldSnapBack(switchState, fabricValue)) {
|
|
85
|
+
dlog(
|
|
86
|
+
`Switch snap-back no-op reported=${String(switchState.lastNativeReport)} value=${fabricValue}`,
|
|
87
|
+
);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
dlog(
|
|
91
|
+
`Switch ${PLATFORM.snapBackCommand} snap-back reported=${String(switchState.lastNativeReport)} value=${fabricValue}`,
|
|
92
|
+
);
|
|
93
|
+
dispatchViewCommand(engineNode, PLATFORM.snapBackCommand, [fabricValue]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const descriptor = $derived(
|
|
97
|
+
renderSwitch(
|
|
98
|
+
{
|
|
99
|
+
value: fabricValue,
|
|
100
|
+
disabled,
|
|
101
|
+
trackColor,
|
|
102
|
+
thumbColor,
|
|
103
|
+
ios_backgroundColor,
|
|
104
|
+
style,
|
|
105
|
+
passthrough: { ...passthrough, class: className, onChange: handleChange },
|
|
106
|
+
},
|
|
107
|
+
PLATFORM,
|
|
108
|
+
),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const syncChildren = createDescriptorChildrenSync();
|
|
112
|
+
$effect(() => {
|
|
113
|
+
syncChildren(hostShim, descriptor.children);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// See View.svelte's note on `{@attach}`.
|
|
117
|
+
const syncAttachments = createAttachmentsSync();
|
|
118
|
+
$effect(() => {
|
|
119
|
+
syncAttachments(hostShim, passthrough);
|
|
120
|
+
});
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<symbiote-switch p={descriptor.props} bind:this={hostShim} />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './switch-platform.ios';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Base / default: re-exports the iOS build. Metro overrides this with switch-platform.ios.ts /
|
|
2
|
+
// switch-platform.android.ts on a real host; under tsc/vitest the host config resolves here.
|
|
3
|
+
// Filename is the selector, no Platform.OS read — same convention as every other adapter's
|
|
4
|
+
// switch platform split.
|
|
5
|
+
export * from './switch-platform.ios.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|