@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
package/build/jsx-runtime.d.ts
CHANGED
|
@@ -1,9 +1,46 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ICrossTypedIntrinsics } from '@symbiote-native/components';
|
|
2
2
|
import type { ISymbioteNode } from '@symbiote-native/engine';
|
|
3
|
+
import type { IViewProps } from './components/view-props';
|
|
4
|
+
import type { ITextProps } from './components/text-props';
|
|
5
|
+
import type { IPressableProps } from './components/pressable-props';
|
|
6
|
+
import type { IButtonProps } from './components/button-props';
|
|
7
|
+
import type { IImageProps } from './components/image-props';
|
|
8
|
+
import type { IImageBackgroundProps } from './components/image-background-props';
|
|
9
|
+
import type { IInputAccessoryViewProps } from './components/input-accessory-view-props';
|
|
10
|
+
import type { IRefreshControlProps } from './components/refresh-control-props';
|
|
11
|
+
import type { ISafeAreaViewProps } from './components/safe-area-view-props';
|
|
12
|
+
import type { ITouchableNativeFeedbackProps } from './components/touchable-native-feedback/touchable-native-feedback-props';
|
|
13
|
+
import type { ITouchableOpacityProps, ITouchableHighlightProps } from './components/touchable-props';
|
|
14
|
+
import type { ITouchableWithoutFeedbackProps } from './components/touchable-without-feedback/touchable-without-feedback-props';
|
|
15
|
+
import type { IScrollViewProps } from './components/scroll-view-props';
|
|
16
|
+
import type { ISwitchProps } from './components/switch-props';
|
|
17
|
+
import type { ITextInputProps } from './components/text-input-props';
|
|
18
|
+
import type { IModalProps } from './components/modal';
|
|
19
|
+
import type { IActivityIndicatorProps } from './components/activity-indicator-props';
|
|
3
20
|
type ISymbioteHostAttributes = {
|
|
4
21
|
readonly children?: JSX.Element;
|
|
5
22
|
readonly [attribute: string]: unknown;
|
|
6
23
|
};
|
|
24
|
+
interface ICrossedPrimitiveProps {
|
|
25
|
+
view: IViewProps;
|
|
26
|
+
text: ITextProps;
|
|
27
|
+
pressable: IPressableProps;
|
|
28
|
+
button: IButtonProps;
|
|
29
|
+
image: IImageProps;
|
|
30
|
+
'image-background': IImageBackgroundProps;
|
|
31
|
+
'input-accessory-view': IInputAccessoryViewProps;
|
|
32
|
+
'refresh-control': IRefreshControlProps;
|
|
33
|
+
'safe-area-view': ISafeAreaViewProps;
|
|
34
|
+
'touchable-native-feedback': ITouchableNativeFeedbackProps;
|
|
35
|
+
'touchable-opacity': ITouchableOpacityProps;
|
|
36
|
+
'touchable-highlight': ITouchableHighlightProps;
|
|
37
|
+
'touchable-without-feedback': ITouchableWithoutFeedbackProps;
|
|
38
|
+
'scroll-view': IScrollViewProps;
|
|
39
|
+
switch: ISwitchProps;
|
|
40
|
+
'text-input': ITextInputProps;
|
|
41
|
+
modal: IModalProps;
|
|
42
|
+
'activity-indicator': IActivityIndicatorProps;
|
|
43
|
+
}
|
|
7
44
|
export declare namespace JSX {
|
|
8
45
|
type Element = ISymbioteNode | IArrayElement | IFunctionElement | (string & {}) | number | boolean | null | undefined;
|
|
9
46
|
interface IArrayElement extends Array<Element> {
|
|
@@ -20,7 +57,7 @@ export declare namespace JSX {
|
|
|
20
57
|
}
|
|
21
58
|
interface IntrinsicAttributes {
|
|
22
59
|
}
|
|
23
|
-
interface IntrinsicElements extends
|
|
60
|
+
interface IntrinsicElements extends ICrossTypedIntrinsics<ISymbioteHostAttributes, ICrossedPrimitiveProps> {
|
|
24
61
|
}
|
|
25
62
|
}
|
|
26
63
|
export {};
|
|
@@ -44,10 +44,6 @@ export declare const Animated: {
|
|
|
44
44
|
event: typeof event;
|
|
45
45
|
forkEvent: typeof forkEvent;
|
|
46
46
|
unforkEvent: typeof unforkEvent;
|
|
47
|
-
View: (props: import("./create-animated-component").IAnimatedComponentProps) => import("@symbiote-native/solid/jsx-runtime").JSX.Element;
|
|
48
|
-
Text: (props: import("./create-animated-component").IAnimatedComponentProps) => import("@symbiote-native/solid/jsx-runtime").JSX.Element;
|
|
49
|
-
Image: (props: import("./create-animated-component").IAnimatedComponentProps) => import("@symbiote-native/solid/jsx-runtime").JSX.Element;
|
|
50
|
-
ScrollView: IAnimatedComponent;
|
|
51
47
|
FlatList: IAnimatedComponent;
|
|
52
48
|
SectionList: IAnimatedComponent;
|
|
53
49
|
createAnimatedComponent: typeof createAnimatedComponent;
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
// The Animated namespace for @symbiote-native/solid.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
1
|
+
// The Animated namespace for @symbiote-native/solid. The value graph, easing and imperative drivers
|
|
2
|
+
// come from @symbiote-native/engine (framework-agnostic), so both halves meet here in one object and
|
|
3
|
+
// `Animated.timing(new Animated.Value(0), …).start()` reads as it does in RN.
|
|
4
|
+
//
|
|
5
|
+
// THERE IS NO `Animated.View` / `Animated.Text` / `Animated.Image` / `Animated.ScrollView`, and
|
|
6
|
+
// nothing replaced any of them: `<view style={{ opacity: someAnimatedValue }}>` is the whole API
|
|
7
|
+
// for style, and `<scroll-view onScroll={Animated.event(…)}>` is the whole API for a native-driven
|
|
8
|
+
// scroll listener. The engine resolves an AnimatedNode written into any prop of any host node
|
|
9
|
+
// (`core/engine/src/animated/host-binding.ts`) — it publishes the current value, subscribes the
|
|
10
|
+
// leaf, writes each frame through its own targeted setNativeProps commit, and releases at the
|
|
11
|
+
// commit sweep; `setEventListener` calls `bindAnimatedEvent` on any `on*` prop for EVERY host node,
|
|
12
|
+
// not just a scroll one. Those four were aliases of the wrappers, and a wrapper is a tag now, which
|
|
13
|
+
// is not a value `<Animated.X>` can call.
|
|
14
|
+
//
|
|
15
|
+
// The two below REMAIN because they are tier 3: a render prop decides the list family's output
|
|
16
|
+
// shape in JS, so there is no tag for `<Animated.X>` to be an alias of.
|
|
7
17
|
//
|
|
8
18
|
// The NATIVE DRIVER is not this module's doing and must not be re-derived here. A
|
|
9
19
|
// useNativeDriver animation promotes the value graph itself (animations/base.ts ->
|
|
10
20
|
// value.__startNativeAnimation -> graph.__makeNative), and the props leaf follows automatically
|
|
11
21
|
// through graph.__addChild. This file only assembles the surface.
|
|
12
22
|
import { AnimatedValue, AnimatedValueXY, AnimatedColor, AnimatedMock, Easing, Platform, timing, spring, decay, parallel, sequence, stagger, loop, delay, add, subtract, multiply, divide, modulo, diffClamp, event, forkEvent, unforkEvent, } from '@symbiote-native/engine';
|
|
13
|
-
import { View } from '../../components/view.jsx';
|
|
14
|
-
import { Text } from '../../components/text.jsx';
|
|
15
|
-
import { Image } from '../../components/image.js';
|
|
16
|
-
import { ScrollView } from '../../components/scroll-view';
|
|
17
23
|
import { FlatList } from '../../components/flat-list/index.jsx';
|
|
18
24
|
import { SectionList } from '../../components/section-list/index.jsx';
|
|
19
25
|
import { createAnimatedComponent } from './create-animated-component.jsx';
|
|
@@ -21,11 +27,6 @@ export { createAnimatedComponent } from './create-animated-component.jsx';
|
|
|
21
27
|
// The pure graph leaves live in the engine (they extend AnimatedWithChildren, no framework);
|
|
22
28
|
// re-exported so this adapter's Animated surface matches React's.
|
|
23
29
|
export { AnimatedProps, AnimatedStyle, AnimatedTransform, } from '@symbiote-native/engine';
|
|
24
|
-
// View/Text/Image are pure host primitives with no cycle back to here, so wrap them eagerly.
|
|
25
|
-
const AnimatedView = createAnimatedComponent(View);
|
|
26
|
-
const AnimatedText = createAnimatedComponent(Text);
|
|
27
|
-
const AnimatedImage = createAnimatedComponent(Image);
|
|
28
|
-
let animatedScrollView;
|
|
29
30
|
let animatedFlatList;
|
|
30
31
|
let animatedSectionList;
|
|
31
32
|
// The live, JS-driven driver half. RN's AnimatedImplementation.
|
|
@@ -58,13 +59,6 @@ const liveDrivers = {
|
|
|
58
59
|
// spreads `...Animated` over the same component getters.
|
|
59
60
|
const drivers = Platform.isDisableAnimations ? AnimatedMock : liveDrivers;
|
|
60
61
|
export const Animated = {
|
|
61
|
-
View: AnimatedView,
|
|
62
|
-
Text: AnimatedText,
|
|
63
|
-
Image: AnimatedImage,
|
|
64
|
-
get ScrollView() {
|
|
65
|
-
animatedScrollView ??= createAnimatedComponent(ScrollView);
|
|
66
|
-
return animatedScrollView;
|
|
67
|
-
},
|
|
68
62
|
get FlatList() {
|
|
69
63
|
animatedFlatList ??= createAnimatedComponent(FlatList);
|
|
70
64
|
return animatedFlatList;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// RN's Image STATICS — `getSize`, `prefetch`, `queryCache`, `abortPrefetch`, `resolveAssetSource`.
|
|
2
|
+
//
|
|
3
|
+
// IN `modules/`, NOT `components/`. The primitive is the `<image>` tag and its fold runs in the
|
|
4
|
+
// engine (`behaviors/image.ts`); what survives under the name `Image` is an imperative API with no
|
|
5
|
+
// view, which is what this bucket is for. Sitting in `components/` was what made a statics object
|
|
6
|
+
// read as a component.
|
|
7
|
+
//
|
|
8
|
+
// A NAMESPACE OBJECT rather than five bare functions, deliberately: `Alert`, `Share`, `Linking`
|
|
9
|
+
// and `Vibration` all take that shape, and an app porting from RN writes `Image.getSize(...)`
|
|
10
|
+
// verbatim. Shared verbatim via `imageStatics`, the same source every other adapter attaches.
|
|
11
|
+
import { imageStatics } from '@symbiote-native/components';
|
|
12
|
+
export const Image = imageStatics;
|
package/build/register.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import '@symbiote-native/components/register';
|
package/build/register.js
CHANGED
|
@@ -1,31 +1,15 @@
|
|
|
1
1
|
// Side-effect ONLY. Exports nothing, and must never be re-exported from the barrel.
|
|
2
2
|
//
|
|
3
|
-
// The
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// invisible to tsc, to vitest, and to grepping the bundle.
|
|
3
|
+
// The fourteen calls live once, in `@symbiote-native/components/register` — they were identical in
|
|
4
|
+
// all five adapters, and each copy carried a migration ledger justifying a call against a wrapper
|
|
5
|
+
// that no longer exists. What is per-adapter is only the SPELLING of the import below: every
|
|
6
|
+
// `*-tag.test.*` here reaches the registry through `import './register';`, and this adapter's own
|
|
7
|
+
// barrel guard pins that line.
|
|
9
8
|
//
|
|
10
|
-
// A
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// Only the LOWERED tags carry this — the wrapper renders `symbiote-text-input-managed` and
|
|
18
|
-
// keeps running its own lifecycle. One owner per node; see `component-names/shared.ts`.
|
|
19
|
-
registerTextInputBehavior();
|
|
20
|
-
// Same reason as TextInput: the wrapper renders `symbiote-switch-managed` and runs its own
|
|
21
|
-
// lastNativeReport/snap-back lifecycle, so the engine's copy attaches only to the bare tag.
|
|
22
|
-
registerSwitchBehavior();
|
|
23
|
-
// Image owns no runtime — its behavior is a prop FOLD and nothing else, and it is registered on the
|
|
24
|
-
// same `symbiote-image` the wrapper already emits rather than on a `-managed` twin. That is safe
|
|
25
|
-
// only because the mapping is idempotent, which `core/components/src/behaviors/image.test.ts`
|
|
26
|
-
// asserts rather than assumes; a wrapper-built node simply folds a second time and nothing moves.
|
|
27
|
-
registerImageBehavior();
|
|
28
|
-
// Fold-only, and it shares the wrapper's tag for the same reason Image does: the mapping has no
|
|
29
|
-
// aliasing at all, so a wrapper-built node folding a second time moves nothing.
|
|
30
|
-
// `core/components/src/behaviors/input-accessory-view.test.ts` asserts that rather than assuming it.
|
|
31
|
-
registerInputAccessoryViewBehavior();
|
|
9
|
+
// WHY A SIDE-EFFECT SUBPATH AND NOT A BARREL EXPORT. Metro turns on `inlineRequires` for PRODUCTION
|
|
10
|
+
// only: it moves a `require` down to the first place its binding is used as a VALUE, and
|
|
11
|
+
// `export { X } from './x'` compiles to a lazy getter. A registration reached that way never
|
|
12
|
+
// evaluates in a Release build — invisible to tsc, to vitest, and to grepping the bundle. A bare
|
|
13
|
+
// `import` placed NEXT TO a re-export of the same specifier does not help either: Babel merges the
|
|
14
|
+
// two into one dependency and the merged dependency stays lazy.
|
|
15
|
+
import '@symbiote-native/components/register';
|
package/build/renderer.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// ref) rather than read off the docs.
|
|
19
19
|
import { createRenderer } from 'solid-js/universal';
|
|
20
20
|
import { appendChild, createAnchor, createElement as createEngineElement, createRawText, dlog, insertBefore, removeChild as removeEngineChild, routeProp, setNodePressed, setProp as setEngineProp, setText as setEngineText, toPublicInstance, RAW_TEXT_COMPONENT, SymbioteSurface, } from '@symbiote-native/engine';
|
|
21
|
-
import { descriptorFor } from '@symbiote-native/components';
|
|
21
|
+
import { descriptorFor, TEXT_INPUT_MULTILINE_TAG, } from '@symbiote-native/components';
|
|
22
22
|
function isSurface(node) {
|
|
23
23
|
return node instanceof SymbioteSurface;
|
|
24
24
|
}
|
|
@@ -129,16 +129,10 @@ function foldTextValue(node, key, value) {
|
|
|
129
129
|
const fold = TEXT_FOLDS.get(key);
|
|
130
130
|
return fold === undefined ? value : fold(value);
|
|
131
131
|
}
|
|
132
|
-
// The alias fold, at the RENDERER
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
// compiles it, dynamic value included), but a hand-written `<symbiote-view id="x">` is not, and it
|
|
137
|
-
// committed `id` — a key Fabric does not know — while the component committed `nativeID`. Measured
|
|
138
|
-
// by mounting both forms and diffing committed key NAMES; totals were identical and said nothing.
|
|
139
|
-
//
|
|
140
|
-
// The transform's rename STAYS. It is not redundant: it means the markup path arrives here already
|
|
141
|
-
// spelled `nativeID`, so the common case never takes the branch below with a key to rewrite.
|
|
132
|
+
// The alias fold, at the RENDERER — the only place it can live now that an app writes the tag
|
|
133
|
+
// itself. A bare `<view id="x">` has no wrapper to inherit the fold from, and `id` is a key Fabric
|
|
134
|
+
// does not know, so it committed nothing while the component committed `nativeID`. Measured by
|
|
135
|
+
// mounting both forms and diffing committed key NAMES; totals were identical and said nothing.
|
|
142
136
|
//
|
|
143
137
|
// One string comparison rather than a Map lookup, because this sits on the per-prop write path —
|
|
144
138
|
// 32 001 prop writes on a benchmark create, where a Map.get is the kind of cost the engine spent
|
|
@@ -147,8 +141,42 @@ function foldTextValue(node, key, value) {
|
|
|
147
141
|
// both constants from `HOST_PRIMITIVES` and fails the moment a second pair appears.
|
|
148
142
|
const ALIAS_FROM = 'id';
|
|
149
143
|
const ALIAS_TO = 'nativeID';
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
// `multiline` selects between TWO Fabric views, so the TAG decides and no prop write moves a node
|
|
145
|
+
// between them. Two of the three paths that can build the node resolve it earlier — the wrapper
|
|
146
|
+
// CONSUMES the prop to pick its intrinsic, a lowering transform reads a literal at compile time —
|
|
147
|
+
// and an author writing the tag by hand has neither, which leaves two silent divergences.
|
|
148
|
+
//
|
|
149
|
+
// The shared behavior (`core/components/src/behaviors/text-input.ts`) already makes the PAYLOAD
|
|
150
|
+
// follow the tag. The complaint has to live here instead of there: `foldPayload` runs inside the
|
|
151
|
+
// commit, so a throw from it lands a tick later as an uncaught exception with no frame naming the
|
|
152
|
+
// call site — measured, a test awaiting the mount sees `nothing committed` rather than the error.
|
|
153
|
+
const MULTILINE_PROP = 'multiline';
|
|
154
|
+
function assertMultilineMatchesTag(node, value) {
|
|
155
|
+
if ((value === true) === (node.props[MULTILINE_PROP] === true))
|
|
156
|
+
return;
|
|
157
|
+
throw new Error(`multiline={${String(value)}} contradicts the tag: <text-input> and ` +
|
|
158
|
+
`<text-input-multiline> are different Fabric views and no prop write moves a node ` +
|
|
159
|
+
`between them. Pick the tag (a runtime choice needs a <Show> around both).`);
|
|
160
|
+
}
|
|
161
|
+
// RN gives `id` UNCONDITIONAL priority when both are set (View.js:77-79,
|
|
162
|
+
// `processedProps.nativeID = id`), and `foldHostBag` reproduces that by deleting the source key out
|
|
163
|
+
// of a whole bag. This renderer never sees a bag — it folds one key at a time — so precedence would
|
|
164
|
+
// otherwise come out as source order, and `<view id nativeID>` would keep the stale legacy value
|
|
165
|
+
// while `<view nativeID id>` would not. The wrapper hid that; a bare tag does not.
|
|
166
|
+
//
|
|
167
|
+
// So the node remembers that its nativeID came from an `id`, and a later raw `nativeID` write loses
|
|
168
|
+
// to it. Off the hot path in every ordinary case: nothing is touched unless the prop being written
|
|
169
|
+
// is one of these two names.
|
|
170
|
+
const aliasedNodes = new WeakSet();
|
|
171
|
+
function foldAliasKey(node, name, value) {
|
|
172
|
+
if (name === ALIAS_FROM) {
|
|
173
|
+
if (value === undefined)
|
|
174
|
+
aliasedNodes.delete(node);
|
|
175
|
+
else
|
|
176
|
+
aliasedNodes.add(node);
|
|
177
|
+
return ALIAS_TO;
|
|
178
|
+
}
|
|
179
|
+
return name;
|
|
152
180
|
}
|
|
153
181
|
const nodeOps = {
|
|
154
182
|
createElement(tag) {
|
|
@@ -161,6 +189,10 @@ const nodeOps = {
|
|
|
161
189
|
const node = createEngineElement(descriptor.component, descriptor.isText, tag);
|
|
162
190
|
if (descriptor.isText)
|
|
163
191
|
seedTextDefaults(node);
|
|
192
|
+
// Only the multiline tag is seeded: writing `multiline: false` on the single-line one would add
|
|
193
|
+
// a key the wrapper's payload does not carry, i.e. a divergence in the other direction.
|
|
194
|
+
if (tag === TEXT_INPUT_MULTILINE_TAG)
|
|
195
|
+
setEngineProp(node, MULTILINE_PROP, true);
|
|
164
196
|
// Graft the imperative public-instance API (measure / setNativeProps / focus / …) onto the raw
|
|
165
197
|
// node so a `ref` to a host element exposes it exactly like React's getPublicInstance.
|
|
166
198
|
// toPublicInstance mutates in place and returns the SAME node identity, so the engine's commit
|
|
@@ -196,11 +228,15 @@ const nodeOps = {
|
|
|
196
228
|
setProperty(node, name, value) {
|
|
197
229
|
if (isSurface(node))
|
|
198
230
|
return;
|
|
231
|
+
if (name === MULTILINE_PROP)
|
|
232
|
+
assertMultilineMatchesTag(node, value);
|
|
233
|
+
if (name === ALIAS_TO && aliasedNodes.has(node))
|
|
234
|
+
return;
|
|
199
235
|
// routeProp makes the prop-vs-event decision from the node's ViewConfig (onPress on a View
|
|
200
236
|
// becomes a listener; onTintColor on a Switch stays a prop), and centralizes the class+style
|
|
201
237
|
// merge. Shared with React and Vue — never re-implement an `onX` check here
|
|
202
238
|
// (symbiote-engine-core §2).
|
|
203
|
-
routeProp(node, foldAliasKey(name), foldTextValue(node, name, value));
|
|
239
|
+
routeProp(node, foldAliasKey(node, name, value), foldTextValue(node, name, value));
|
|
204
240
|
requestCommit();
|
|
205
241
|
},
|
|
206
242
|
insertNode(parent, node, anchor) {
|
|
@@ -232,10 +268,27 @@ const nodeOps = {
|
|
|
232
268
|
requestCommit();
|
|
233
269
|
},
|
|
234
270
|
removeNode,
|
|
271
|
+
// Reverse of `getFirstChild`'s `childHost` redirect: a child under a composed primitive's slot
|
|
272
|
+
// has `node.parent` pointing at the SLOT, but `cleanChildren`'s `isParent = getParentNode(el)
|
|
273
|
+
// === parent` compares against the OWNER — the same reference the JSX-level `insert()` call was
|
|
274
|
+
// given. Left un-redirected, that comparison is false for every child of a bare `<scroll-view>`
|
|
275
|
+
// (or any other slotted primitive) with a following sibling, so `isParent` never fires,
|
|
276
|
+
// `removeNode` is never called on a full array clear, and the old children are orphaned: still
|
|
277
|
+
// committed to Fabric, still retained, and re-diffed against on the next reconciliation —
|
|
278
|
+
// reproduced as unbounded RAM growth and an increasingly slow "Clear" on the benchmark screen.
|
|
279
|
+
// One level up only: `childHost` is always a DIRECT child the owner built (see `buildStructure`),
|
|
280
|
+
// never a deeper descendant.
|
|
235
281
|
getParentNode(node) {
|
|
236
282
|
if (isSurface(node))
|
|
237
283
|
return undefined;
|
|
238
|
-
|
|
284
|
+
const parent = node.parent ?? activeSurface;
|
|
285
|
+
if (parent === undefined || isSurface(parent))
|
|
286
|
+
return parent;
|
|
287
|
+
const grandparent = parent.parent;
|
|
288
|
+
if (grandparent !== undefined && grandparent.childHost === parent) {
|
|
289
|
+
return grandparent;
|
|
290
|
+
}
|
|
291
|
+
return parent;
|
|
239
292
|
},
|
|
240
293
|
// Anchors are NOT filtered out of this or getNextSibling, deliberately. solid-js/universal keeps
|
|
241
294
|
// its own record of which nodes it put where and re-derives positions through these two lookups
|
|
@@ -243,8 +296,18 @@ const nodeOps = {
|
|
|
243
296
|
// getFirstChild(parent) to replace text in place). Hiding a node the runtime itself inserted
|
|
244
297
|
// desyncs that record from the real tree. Anchors are invisible to FABRIC — the commit walk skips
|
|
245
298
|
// them — not to tree traversal.
|
|
299
|
+
//
|
|
300
|
+
// `childHost` redirected, same as the engine's own `appendChild`/`insertBefore`/`removeChild`
|
|
301
|
+
// (`hostFor`, core/engine/src/node.ts): a composed primitive's real children live on the slot it
|
|
302
|
+
// built, not on the owner it was asked to insert into, and `insert()`'s reconciliation reads this
|
|
303
|
+
// to know what is ALREADY there before deciding what to insert/move/remove. Reading the owner's
|
|
304
|
+
// own (single-child) array here would have every dynamic child on a bare `<scroll-view>` diffed
|
|
305
|
+
// against the wrong node — invisible on VirtualizedList, which never calls `insert()` on the
|
|
306
|
+
// owner directly, and wrong for any app writing `<scroll-view>{dynamicChildren}</scroll-view>`.
|
|
246
307
|
getFirstChild(node) {
|
|
247
|
-
|
|
308
|
+
if (isSurface(node))
|
|
309
|
+
return node.children[0];
|
|
310
|
+
return (node.childHost ?? node).children[0];
|
|
248
311
|
},
|
|
249
312
|
getNextSibling(node) {
|
|
250
313
|
if (isSurface(node))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function withStableKeys(props: () =>
|
|
1
|
+
export declare function withStableKeys<TProps extends object>(props: () => TProps): () => Record<string, unknown>;
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
// the actual diffing. Never hand-roll that diff instead.
|
|
13
13
|
//
|
|
14
14
|
// Lives here rather than beside either caller because BOTH reach for it: descriptorToSolid wraps
|
|
15
|
-
// a render fn's Descriptor props, and
|
|
16
|
-
//
|
|
17
|
-
//
|
|
15
|
+
// a render fn's Descriptor props, and a component that spreads a folded bag onto a bare `<view>`
|
|
16
|
+
// (Pressable, KeyboardAvoidingView) wraps its own — the same function, the same two-branch key
|
|
17
|
+
// set. This is the one capability a bare tag does NOT inherit from the wrappers that used to own
|
|
18
|
+
// it, so any Solid component folding props through a whole-object transform belongs on it too.
|
|
18
19
|
export function withStableKeys(props) {
|
|
19
20
|
const seen = new Set();
|
|
20
21
|
return () => {
|
|
@@ -22,9 +23,9 @@ export function withStableKeys(props) {
|
|
|
22
23
|
const widened = {};
|
|
23
24
|
for (const key of seen)
|
|
24
25
|
widened[key] = undefined;
|
|
25
|
-
for (const key of Object.
|
|
26
|
+
for (const [key, value] of Object.entries(next)) {
|
|
26
27
|
seen.add(key);
|
|
27
|
-
widened[key] =
|
|
28
|
+
widened[key] = value;
|
|
28
29
|
}
|
|
29
30
|
return widened;
|
|
30
31
|
};
|
package/metro-css-parser.cjs
CHANGED
|
@@ -5,4 +5,5 @@
|
|
|
5
5
|
// .cjs, not .js: this package is "type": "module", but Metro's babelTransformerPath loading needs
|
|
6
6
|
// a require()-able file. createCssMetroTransformer() is a factory — Metro needs the actual
|
|
7
7
|
// {transform, getCacheKey} object it returns, not the css-parser package barrel.
|
|
8
|
-
module.exports =
|
|
8
|
+
module.exports =
|
|
9
|
+
require('@symbiote-native/css-parser').createCssMetroTransformer();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/solid",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "SymbioteNative's Solid adapter — a solid-js/universal createRenderer driving real native iOS/Android views through the same engine as every other adapter.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -49,16 +49,11 @@
|
|
|
49
49
|
"default": "./build/jsx-runtime.js"
|
|
50
50
|
},
|
|
51
51
|
"./babel-preset": "./babel-preset.cjs",
|
|
52
|
-
"./metro-css-parser": "./metro-css-parser.cjs"
|
|
53
|
-
"./state-style": {
|
|
54
|
-
"types": "./build/state-style.d.ts",
|
|
55
|
-
"default": "./build/state-style.js"
|
|
56
|
-
}
|
|
52
|
+
"./metro-css-parser": "./metro-css-parser.cjs"
|
|
57
53
|
},
|
|
58
54
|
"files": [
|
|
59
55
|
"build",
|
|
60
56
|
"babel-preset.cjs",
|
|
61
|
-
"babel-lower-host-primitives.cjs",
|
|
62
57
|
"metro-css-parser.cjs"
|
|
63
58
|
],
|
|
64
59
|
"publishConfig": {
|
|
@@ -67,18 +62,18 @@
|
|
|
67
62
|
"dependencies": {
|
|
68
63
|
"babel-preset-solid": "^1.9.12",
|
|
69
64
|
"typescript": "~6.0.0",
|
|
70
|
-
"@symbiote-native/components": "
|
|
65
|
+
"@symbiote-native/components": "2.0.0",
|
|
71
66
|
"@symbiote-native/css-parser": "0.5.0"
|
|
72
67
|
},
|
|
73
68
|
"peerDependencies": {
|
|
74
69
|
"solid-js": ">=1.9.0",
|
|
75
70
|
"react-native": ">=0.86",
|
|
76
|
-
"@symbiote-native/engine": "^0.
|
|
71
|
+
"@symbiote-native/engine": "^0.5.0"
|
|
77
72
|
},
|
|
78
73
|
"devDependencies": {
|
|
79
74
|
"@babel/core": "^7.25.2",
|
|
80
75
|
"solid-js": "^1.9.14",
|
|
81
|
-
"@symbiote-native/engine": "0.
|
|
76
|
+
"@symbiote-native/engine": "0.5.0",
|
|
82
77
|
"@symbiote-native/test-utils": "0.3.0"
|
|
83
78
|
},
|
|
84
79
|
"scripts": {
|