@symbiote-native/react 0.4.0 → 1.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 +20 -5
- package/build/components/pressable/index.d.ts +1 -0
- package/build/components/pressable/index.js +17 -5
- package/build/components/safe-area-view/index.d.ts +1 -0
- package/build/components/text-input/index.js +8 -0
- package/build/components/touchable/index.js +1 -1
- package/build/components/touchable-native-feedback/index.js +1 -1
- package/build/components.d.ts +4 -4
- package/build/components.js +19 -17
- package/build/host-config.d.ts +2 -2
- package/build/host-config.js +30 -7
- package/build/host-instance.js +1 -1
- package/build/index.d.ts +1 -0
- package/build/jsx.d.ts +18 -0
- package/build/jsx.js +1 -0
- package/build/modules/animated/create-animated-component.d.ts +2 -1
- package/build/modules/animated/create-animated-component.js +7 -1
- package/build/render.js +2 -4
- package/build/state-style.d.ts +1 -0
- package/build/state-style.js +13 -0
- package/package.json +24 -8
package/README.md
CHANGED
|
@@ -54,9 +54,24 @@ export default function App() {
|
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
The
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
The zero-config entry wires the RN-backed host seams (colors, images, device events,
|
|
58
|
+
third-party ViewConfigs) and registers the app in one call — this is what
|
|
59
|
+
[`examples/react`](../../examples/react) actually uses:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
// index.js
|
|
63
|
+
import { registerApp } from '@symbiote-native/react/bootstrap';
|
|
64
|
+
import App from './App';
|
|
65
|
+
import { name as appName } from './app.json';
|
|
66
|
+
|
|
67
|
+
registerApp(App, { appName });
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`registerApp` mirrors bare RN's own `AppRegistry.registerComponent(appName, () => App)` idiom,
|
|
71
|
+
minus the manual host wiring in front of it — RN's own renderer never runs; `nativeFabric-
|
|
72
|
+
UIManager` is driven directly. For anything the defaults don't cover (a custom color processor,
|
|
73
|
+
image resolver, or manual control over the mount callback), drive the lower-level seam directly —
|
|
74
|
+
a raw _runnable_ instead of a registered component:
|
|
60
75
|
|
|
61
76
|
```js
|
|
62
77
|
// index.js
|
|
@@ -77,8 +92,8 @@ AppRegistry.registerRunnable(appName, ({ rootTag }) => {
|
|
|
77
92
|
});
|
|
78
93
|
```
|
|
79
94
|
|
|
80
|
-
|
|
81
|
-
|
|
95
|
+
Either way, the full canary ([`App.tsx`](../../examples/react/App.tsx)) exercises every block of
|
|
96
|
+
the surface below.
|
|
82
97
|
|
|
83
98
|
---
|
|
84
99
|
|
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
// entirely in JS: android_ripple (an Android native-feedback prop on a dedicated inner View, inert
|
|
12
12
|
// on iOS), unstable_pressDelay, and pressRetentionOffset (the drift region), all handled by the
|
|
13
13
|
// shared machine; here we only wire its host + render the View.
|
|
14
|
-
import { createElement, useMemo, useRef, useState, } from 'react';
|
|
15
|
-
import { createPressHandlers, createPressRuntime, rippleProps, buildPressableListeners, resolveDisabledAccessibilityState, noteHoverNoop, DEFAULT_DELAY_LONG_PRESS_MS, } from '@symbiote-native/components';
|
|
14
|
+
import { createElement, useEffect, useMemo, useRef, useState, } from 'react';
|
|
15
|
+
import { createPressHandlers, createPressRuntime, disposePressRuntime, rippleProps, buildPressableListeners, resolveDisabledAccessibilityState, noteHoverNoop, DEFAULT_DELAY_LONG_PRESS_MS, DEFAULT_MIN_PRESS_DURATION_MS, } from '@symbiote-native/components';
|
|
16
16
|
import { View } from '../../components.js';
|
|
17
|
+
import { flushExternalUpdate } from '../../host-config.js';
|
|
17
18
|
function resolveStyle(style, state) {
|
|
18
19
|
if (typeof style === 'function')
|
|
19
20
|
return style(state);
|
|
@@ -24,8 +25,8 @@ function resolveChildren(children, state) {
|
|
|
24
25
|
return children(state);
|
|
25
26
|
return children;
|
|
26
27
|
}
|
|
27
|
-
|
|
28
|
-
const { onPress, onPressIn, onPressOut, onPressMove, onLongPress, delayLongPress = DEFAULT_DELAY_LONG_PRESS_MS, disabled, cancelable, hitSlop, pressRetentionOffset, unstable_pressDelay = 0, android_ripple, android_disableSound, onHoverIn, onHoverOut, delayHoverIn, delayHoverOut, accessibilityState, testID, style, children,
|
|
28
|
+
const PressableImpl = props => {
|
|
29
|
+
const { onPress, onPressIn, onPressOut, onPressMove, onLongPress, delayLongPress = DEFAULT_DELAY_LONG_PRESS_MS, disabled, cancelable, hitSlop, pressRetentionOffset, unstable_pressDelay = 0, __minPressDuration = DEFAULT_MIN_PRESS_DURATION_MS, android_ripple, android_disableSound, onHoverIn, onHoverOut, delayHoverIn, delayHoverOut, accessibilityState, testID, style, children,
|
|
29
30
|
// The remaining accessibility / aria props forward to View untouched; View runs
|
|
30
31
|
// resolveAccessibilityProps, so aria/role fold there, once.
|
|
31
32
|
...accessibilityRest } = props;
|
|
@@ -45,10 +46,16 @@ export const Pressable = props => {
|
|
|
45
46
|
return callback => view.measure(callback);
|
|
46
47
|
},
|
|
47
48
|
schedule: (callback, ms) => {
|
|
48
|
-
const id = setTimeout(callback, ms);
|
|
49
|
+
const id = setTimeout(() => flushExternalUpdate(callback), ms);
|
|
49
50
|
return () => clearTimeout(id);
|
|
50
51
|
},
|
|
52
|
+
now: Date.now,
|
|
51
53
|
}), []);
|
|
54
|
+
// This renderer tears insertion effects down during host updates, so use the stable passive
|
|
55
|
+
// unmount seam to cancel delayed press-in, long-press and press-out callbacks.
|
|
56
|
+
useEffect(() => () => {
|
|
57
|
+
disposePressRuntime(runtime);
|
|
58
|
+
}, [runtime]);
|
|
52
59
|
const handlers = useMemo(() => createPressHandlers({
|
|
53
60
|
onPress,
|
|
54
61
|
onPressIn,
|
|
@@ -57,6 +64,7 @@ export const Pressable = props => {
|
|
|
57
64
|
onLongPress,
|
|
58
65
|
delayLongPress,
|
|
59
66
|
unstable_pressDelay,
|
|
67
|
+
minPressDuration: __minPressDuration,
|
|
60
68
|
hitSlop,
|
|
61
69
|
pressRetentionOffset,
|
|
62
70
|
}, runtime, host), [
|
|
@@ -67,6 +75,7 @@ export const Pressable = props => {
|
|
|
67
75
|
onLongPress,
|
|
68
76
|
delayLongPress,
|
|
69
77
|
unstable_pressDelay,
|
|
78
|
+
__minPressDuration,
|
|
70
79
|
hitSlop,
|
|
71
80
|
pressRetentionOffset,
|
|
72
81
|
runtime,
|
|
@@ -98,3 +107,6 @@ export const Pressable = props => {
|
|
|
98
107
|
const inner = ripple !== undefined ? createElement(View, ripple, content) : content;
|
|
99
108
|
return createElement(View, viewProps, inner);
|
|
100
109
|
};
|
|
110
|
+
export const Pressable = PressableImpl;
|
|
111
|
+
// Relative-only composition seam; it is deliberately absent from the adapter barrel.
|
|
112
|
+
export const TouchablePressable = props => createElement(PressableImpl, { ...props, __minPressDuration: 0 });
|
|
@@ -4,6 +4,7 @@ import { type IAccessibilityProps, type IAriaProps } from '@symbiote-native/comp
|
|
|
4
4
|
import type { IStyleProp, IViewStyle } from '../../utils/styles';
|
|
5
5
|
export interface ISafeAreaViewProps extends IAccessibilityProps, IAriaProps {
|
|
6
6
|
style?: IStyleProp<IViewStyle>;
|
|
7
|
+
id?: string;
|
|
7
8
|
className?: string;
|
|
8
9
|
children?: ReactNode;
|
|
9
10
|
onLayout?: (event: ISymbioteEvent) => void;
|
|
@@ -119,6 +119,14 @@ export const TextInput = forwardRef((rawProps, forwardedRef) => {
|
|
|
119
119
|
// setSelection reuse setTextAndSelection (the same stale-safe path as a controlled write),
|
|
120
120
|
// echoing the acknowledged event count so native applies them.
|
|
121
121
|
useImperativeHandle(forwardedRef, () => ({
|
|
122
|
+
// Forwarded from the node so a TextInput ref is not poorer than any other host ref. An
|
|
123
|
+
// `expose`/`useImperativeHandle` that lists only the five TextInput methods CLOSES the
|
|
124
|
+
// node off, and the lowered path loses the opposite four — two different surfaces an app
|
|
125
|
+
// crosses between without touching its ref code. `ITextInputHandle` carries the account.
|
|
126
|
+
measure: (callback) => ref.current?.measure(callback),
|
|
127
|
+
measureInWindow: (callback) => ref.current?.measureInWindow(callback),
|
|
128
|
+
measureLayout: (relativeToNativeNode, onSuccess, onFail) => ref.current?.measureLayout(relativeToNativeNode, onSuccess, onFail),
|
|
129
|
+
setNativeProps: (nativeProps) => ref.current?.setNativeProps(nativeProps),
|
|
122
130
|
focus: () => {
|
|
123
131
|
const node = ref.current;
|
|
124
132
|
if (node !== null)
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// "without feedback" means no VISUAL, not no timing.
|
|
12
12
|
import { Children, cloneElement, createElement, isValidElement, useEffect, useRef, useState, } from 'react';
|
|
13
13
|
import { createHighlightUnderlayHandlers, createHighlightUnderlayRuntime, createTouchableFeedbackHandlers, createTouchableFeedbackRuntime, hasTouchablePressHandler, resolveHighlightExtraStyles, restingOpacityFromStyle, DEFAULT_ACTIVE_OPACITY, OPACITY_ACTIVE_GRANT_DURATION_MS, OPACITY_INACTIVE_DURATION_MS, TOUCHABLE_MIN_PRESS_DURATION_MS, } from '@symbiote-native/components';
|
|
14
|
-
import { Pressable } from '../pressable/index.js';
|
|
14
|
+
import { TouchablePressable as Pressable, } from '../pressable/index.js';
|
|
15
15
|
import { Animated } from '../../modules/animated/index.js';
|
|
16
16
|
// The real setTimeout the shared machines schedule on (core/components has no timer globals).
|
|
17
17
|
// Returns a canceller so an early release flushes the timer.
|
|
@@ -10,7 +10,7 @@ import { createElement } from 'react';
|
|
|
10
10
|
import { dlog } from '@symbiote-native/engine';
|
|
11
11
|
import { backgroundProps, canUseNativeForeground, rippleBackground, selectableBackground, selectableBackgroundBorderless, } from '@symbiote-native/components';
|
|
12
12
|
import { View } from '../../components.js';
|
|
13
|
-
import { Pressable } from '../pressable/index.js';
|
|
13
|
+
import { TouchablePressable as Pressable, } from '../pressable/index.js';
|
|
14
14
|
const TouchableNativeFeedbackImpl = props => {
|
|
15
15
|
const { background, useForeground = false, children, ...rest } = props;
|
|
16
16
|
// RN defaults a missing background to SelectableBackground() so the touchable always has
|
package/build/components.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { Ref, ReactNode } from 'react';
|
|
2
2
|
import type { ISymbioteEvent } from '@symbiote-native/engine';
|
|
3
3
|
import type { IHostInstance } from './host-instance';
|
|
4
|
-
import {
|
|
4
|
+
import type { IAccessibilityProps, IAriaProps } from '@symbiote-native/components';
|
|
5
5
|
import type { IResponderProps } from './utils/responder-props';
|
|
6
6
|
import type { IStyleProp, ITextStyle, IViewStyle } from './utils/styles';
|
|
7
7
|
export interface IViewProps extends IAccessibilityProps, IAriaProps, IResponderProps {
|
|
@@ -50,5 +50,5 @@ export interface ITextProps extends IAccessibilityProps, IAriaProps {
|
|
|
50
50
|
ref?: Ref<IHostInstance>;
|
|
51
51
|
children?: ReactNode;
|
|
52
52
|
}
|
|
53
|
-
export declare const View
|
|
54
|
-
export declare const Text
|
|
53
|
+
export declare const View = "symbiote-view";
|
|
54
|
+
export declare const Text = "symbiote-text";
|
package/build/components.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
// Host primitives exposed to user code. They
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// view
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
// Host primitives exposed to user code. They ARE the intrinsic tags — a capitalized export whose
|
|
2
|
+
// value is the tag string — so an app writes `<View>` and React commits a host element directly,
|
|
3
|
+
// with no component instance in between. The reconciler maps the tag through
|
|
4
|
+
// `descriptorFor` to a Fabric view name at commit.
|
|
5
|
+
// `View` and `Text` are the intrinsic TAGS, not components wrapping them. JSX resolves a
|
|
6
|
+
// capitalized tag to the value in scope, and a value that is a STRING is a host element to React —
|
|
7
|
+
// so `<View/>` compiles to `_jsx('symbiote-view', …)` with no component instance, while
|
|
8
|
+
// `JSX.IntrinsicElements['symbiote-view']` still supplies the strict props (a bad prop is TS2322).
|
|
9
|
+
//
|
|
10
|
+
// The two folds their component bodies used to apply both moved down a layer, which is what let
|
|
11
|
+
// the bodies go:
|
|
12
|
+
//
|
|
13
|
+
// aria / accessibility the engine folds every node in `fabricProps` (accessibility-props.ts)
|
|
14
|
+
// id -> nativeID, the renderer folds by spec in `host-config`'s createInstance
|
|
15
|
+
// Text's defaults (`foldHostBag`, driven by HOST_PRIMITIVES)
|
|
16
|
+
//
|
|
17
|
+
// A type annotation rather than `as const`: same literal type, no cast (`ts-js-best-practices`).
|
|
18
|
+
export const View = 'symbiote-view';
|
|
19
|
+
export const Text = 'symbiote-text';
|
package/build/host-config.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import createReconciler from 'react-reconciler';
|
|
2
2
|
import { SymbioteSurface, type ISymbioteNode } from '@symbiote-native/engine';
|
|
3
|
-
import { type IHostInstance } from './host-instance';
|
|
4
3
|
type IContainer = SymbioteSurface | ISymbioteNode;
|
|
5
4
|
export declare function withDiscretePriority(run: () => void): void;
|
|
6
|
-
declare const reconciler: createReconciler.Reconciler<IContainer, ISymbioteNode, ISymbioteNode, never, unknown,
|
|
5
|
+
declare const reconciler: createReconciler.Reconciler<IContainer, ISymbioteNode, ISymbioteNode, never, unknown, ISymbioteNode>;
|
|
6
|
+
export declare function flushExternalUpdate(run: () => void): void;
|
|
7
7
|
export default reconciler;
|
package/build/host-config.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// canary: a known-good driver exercising shared end to end.
|
|
5
5
|
import createReconciler from 'react-reconciler';
|
|
6
6
|
import { createContext } from 'react';
|
|
7
|
-
import { appendChild, createElement, createRawText, insertBefore, removeChild, routeProp, setNodeHidden, setText, SymbioteSurface, } from '@symbiote-native/engine';
|
|
7
|
+
import { appendChild, createElement, createRawText, insertBefore, removeChild, routeProp, setNodeComponent, setNodeHidden, setText, SymbioteSurface, } from '@symbiote-native/engine';
|
|
8
8
|
import { DefaultEventPriority, DiscreteEventPriority, NoEventPriority, } from './reconciler-constants.js';
|
|
9
9
|
import { toPublicInstance } from './host-instance.js';
|
|
10
10
|
// Intrinsic JSX type -> Fabric component name. The name table + resolver live once in
|
|
@@ -13,6 +13,14 @@ import { toPublicInstance } from './host-instance.js';
|
|
|
13
13
|
// Adding a primitive is one entry in each name table there, plus its thin
|
|
14
14
|
// component in components.ts: no host-config logic per primitive.
|
|
15
15
|
import { descriptorFor } from '@symbiote-native/components';
|
|
16
|
+
// A bare intrinsic tag has no wrapper to apply RN's per-primitive prop folds (id -> nativeID,
|
|
17
|
+
// Text's ellipsizeMode / allowFontScaling defaults), so the renderer is the layer that must.
|
|
18
|
+
// Shared with every other adapter, driven by the same HOST_PRIMITIVES spec.
|
|
19
|
+
import { foldHostBag } from '@symbiote-native/components/fold-host-bag';
|
|
20
|
+
// WHICH native view a primitive commits can depend on a prop (TextInput's `multiline`). A lowering
|
|
21
|
+
// transform decides that from source text; a bare tag has no transform, so the choice is made here,
|
|
22
|
+
// where the runtime value is known. Identity for every primitive that declares no alternative.
|
|
23
|
+
import { resolveIntrinsicTag } from '@symbiote-native/components/resolve-intrinsic';
|
|
16
24
|
// React-reserved prop keys that must never reach Fabric. `children` is the element
|
|
17
25
|
// tree (handled via appendChild). `ref`/`key` are React 19 plain props: the reconciler
|
|
18
26
|
// reads `props.ref` for commitAttachRef but does NOT strip it from the props it hands a
|
|
@@ -47,8 +55,9 @@ function isSurfaceContainer(container) {
|
|
|
47
55
|
return container instanceof SymbioteSurface;
|
|
48
56
|
}
|
|
49
57
|
let currentUpdatePriority = NoEventPriority;
|
|
50
|
-
// Run
|
|
51
|
-
//
|
|
58
|
+
// Run the callback at discrete priority. Native events and component-owned timers both enter
|
|
59
|
+
// React outside its render loop; callers pair this with flushExternalUpdate when the result must
|
|
60
|
+
// paint synchronously.
|
|
52
61
|
export function withDiscretePriority(run) {
|
|
53
62
|
const previous = currentUpdatePriority;
|
|
54
63
|
currentUpdatePriority = DiscreteEventPriority;
|
|
@@ -90,12 +99,12 @@ const reconciler = createReconciler({
|
|
|
90
99
|
},
|
|
91
100
|
shouldSetTextContent: () => false,
|
|
92
101
|
createInstance(type, props, _container, hostContext) {
|
|
93
|
-
const descriptor = descriptorFor(type);
|
|
102
|
+
const descriptor = descriptorFor(resolveIntrinsicTag(type, props));
|
|
94
103
|
if (hostContext.isInsideText && !descriptor.isText) {
|
|
95
104
|
throw new Error(`<${type}> can't be nested inside <Text>`);
|
|
96
105
|
}
|
|
97
106
|
const node = createElement(descriptor.component, descriptor.isText);
|
|
98
|
-
applyProps(node, props);
|
|
107
|
+
applyProps(node, foldHostBag(type, props));
|
|
99
108
|
return node;
|
|
100
109
|
},
|
|
101
110
|
createTextInstance(text, _container, hostContext) {
|
|
@@ -127,8 +136,14 @@ const reconciler = createReconciler({
|
|
|
127
136
|
removeChild(container, child);
|
|
128
137
|
},
|
|
129
138
|
finalizeInitialChildren: () => false,
|
|
130
|
-
commitUpdate(node,
|
|
131
|
-
|
|
139
|
+
commitUpdate(node, type, oldProps, newProps) {
|
|
140
|
+
// A primitive whose native view depends on a prop (TextInput's `multiline`) can change view on
|
|
141
|
+
// an update. The node keeps its identity — an app's ref stays live — and the commit walk
|
|
142
|
+
// re-creates the Fabric node because its viewName no longer matches the committed one.
|
|
143
|
+
setNodeComponent(node, descriptorFor(resolveIntrinsicTag(type, newProps)).component);
|
|
144
|
+
// BOTH sides folded, or the diff compares a raw `id` against a folded `nativeID` and writes
|
|
145
|
+
// the alias twice while never clearing the raw key.
|
|
146
|
+
applyUpdate(node, foldHostBag(type, oldProps), foldHostBag(type, newProps));
|
|
132
147
|
},
|
|
133
148
|
commitTextUpdate(node, _oldText, newText) {
|
|
134
149
|
setText(node, newText);
|
|
@@ -174,4 +189,12 @@ const reconciler = createReconciler({
|
|
|
174
189
|
suspendInstance: () => { },
|
|
175
190
|
waitForCommitToBeReady: () => null,
|
|
176
191
|
});
|
|
192
|
+
// Native-event and timer callbacks both update React from outside the reconciler. Put the update
|
|
193
|
+
// on the discrete lane and synchronously commit it; otherwise a delayed Pressable transition can
|
|
194
|
+
// mutate state but remain visually stale until some unrelated later event flushes React work.
|
|
195
|
+
export function flushExternalUpdate(run) {
|
|
196
|
+
withDiscretePriority(run);
|
|
197
|
+
// @ts-expect-error flushSyncWork exists at runtime in react-reconciler 0.33
|
|
198
|
+
reconciler.flushSyncWork();
|
|
199
|
+
}
|
|
177
200
|
export default reconciler;
|
package/build/host-instance.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The public instance a host ref hands back, and the ref -> tag lookup. toPublicInstance and
|
|
2
2
|
// IHostInstance live in @symbiote-native/engine (every adapter inherits the same public instance);
|
|
3
3
|
// this module re-exports them so the React surface (findNodeHandle + IHostInstance, host-config's
|
|
4
|
-
//
|
|
4
|
+
// getPublicInstance) is unchanged. findNodeHandle stays React-specific: it is the
|
|
5
5
|
// RN-shaped "ref -> native tag" lookup over the engine's getNativeTag.
|
|
6
6
|
import { getNativeTag, isSymbioteNode, toPublicInstance, } from '@symbiote-native/engine';
|
|
7
7
|
export { toPublicInstance };
|
package/build/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type { ISymbioteIntrinsicTag } from './jsx';
|
|
1
2
|
export { View, Text } from './components';
|
|
2
3
|
export type { IViewProps, ITextProps } from './components';
|
|
3
4
|
export type { IAccessibilityProps, IAriaProps, IAccessibilityRole, IRole, IAccessibilityStateValue, IAccessibilityValue, IAccessibilityActionInfo, } from '@symbiote-native/components';
|
package/build/jsx.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ISymbioteIntrinsic } from '@symbiote-native/components';
|
|
2
|
+
import type { IViewProps, ITextProps } from './components';
|
|
3
|
+
interface HostProps {
|
|
4
|
+
style?: unknown;
|
|
5
|
+
children?: import('react').ReactNode;
|
|
6
|
+
[key: string]: unknown;
|
|
7
|
+
}
|
|
8
|
+
type ILooseIntrinsics = Omit<Record<ISymbioteIntrinsic, HostProps>, 'symbiote-view' | 'symbiote-text'>;
|
|
9
|
+
declare module 'react' {
|
|
10
|
+
namespace JSX {
|
|
11
|
+
interface IntrinsicElements extends ILooseIntrinsics {
|
|
12
|
+
'symbiote-view': IViewProps;
|
|
13
|
+
'symbiote-text': ITextProps;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export type ISymbioteIntrinsicTag = keyof import('react').JSX.IntrinsicElements;
|
|
18
|
+
export {};
|
package/build/jsx.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -8,5 +8,6 @@ type IAnimatableProps = {
|
|
|
8
8
|
style?: unknown;
|
|
9
9
|
children?: unknown;
|
|
10
10
|
};
|
|
11
|
-
|
|
11
|
+
type IAnimatableBase<P extends IAnimatableProps> = ComponentType<P> | string;
|
|
12
|
+
export declare function createAnimatedComponent<P extends IAnimatableProps>(Component: IAnimatableBase<P>): ComponentType<IAnimatedComponentProps>;
|
|
12
13
|
export {};
|
|
@@ -27,6 +27,12 @@ function assignRef(ref, value) {
|
|
|
27
27
|
}
|
|
28
28
|
ref.current = value;
|
|
29
29
|
}
|
|
30
|
+
// A tag has no displayName/name, so read the label through the shape actually present.
|
|
31
|
+
function baseNameOf(base) {
|
|
32
|
+
if (typeof base === 'string')
|
|
33
|
+
return base;
|
|
34
|
+
return base.displayName ?? base.name ?? 'Anonymous';
|
|
35
|
+
}
|
|
30
36
|
export function createAnimatedComponent(Component) {
|
|
31
37
|
function AnimatedComponent(props) {
|
|
32
38
|
const { ref: forwardedRef, passthroughAnimatedPropExplicitValues: passthrough, ...rest } = props;
|
|
@@ -81,6 +87,6 @@ export function createAnimatedComponent(Component) {
|
|
|
81
87
|
const childProps = Object.assign(Object.create(null), reduced, { ref: captureRef });
|
|
82
88
|
return createElement(Component, childProps);
|
|
83
89
|
}
|
|
84
|
-
AnimatedComponent.displayName = `Animated(${Component
|
|
90
|
+
AnimatedComponent.displayName = `Animated(${baseNameOf(Component)})`;
|
|
85
91
|
return AnimatedComponent;
|
|
86
92
|
}
|
package/build/render.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// rootTag (via AppRegistry.registerRunnable); we create a surface for it and let
|
|
3
3
|
// the reconciler drive shared, which commits into nativeFabricUIManager.
|
|
4
4
|
import { createSurface, disposeRoot, setEventDispatcher, dlog, reportUncaughtError, } from '@symbiote-native/engine';
|
|
5
|
-
import reconciler, {
|
|
5
|
+
import reconciler, { flushExternalUpdate } from './host-config.js';
|
|
6
6
|
import { LegacyRoot } from './reconciler-constants.js';
|
|
7
7
|
const noop = () => { };
|
|
8
8
|
function errorReporter(origin) {
|
|
@@ -40,10 +40,8 @@ const onRecoverableError = errorReporter('react render (recovered)');
|
|
|
40
40
|
// to compare against Vue/Angular's microtask-coalesced requestCommit(). Kept
|
|
41
41
|
// behind DEBUG per <keep_logs_gate_behind_DEBUG>, never removed.
|
|
42
42
|
setEventDispatcher(run => {
|
|
43
|
-
withDiscretePriority(run);
|
|
44
43
|
dlog('react event-dispatch: forced flushSyncWork');
|
|
45
|
-
|
|
46
|
-
reconciler.flushSyncWork();
|
|
44
|
+
flushExternalUpdate(run);
|
|
47
45
|
});
|
|
48
46
|
const containers = new Map();
|
|
49
47
|
// Unmount a surface's React tree (render null → empty completeRoot, clearing the
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { resolveStateStyle, type IPressStateArgument, type IResolvedStateStyle, } from '@symbiote-native/components/state-style';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Re-export only. The implementation is shared (`@symbiote-native/components`) because two adapters
|
|
2
|
+
// had written byte-identical copies; this file exists so the code a transform EMITS keeps importing
|
|
3
|
+
// from the adapter package the app already depends on, rather than reaching past it.
|
|
4
|
+
//
|
|
5
|
+
// Imported from the SUBPATH, not the barrel: every adapter barrel re-exports
|
|
6
|
+
// `@symbiote-native/components` wholesale, so a name on that barrel silently becomes public API on
|
|
7
|
+
// all five — which is how a transform internal nearly shipped as a supported export.
|
|
8
|
+
//
|
|
9
|
+
// Present on every adapter, including those with no lowering transform yet: a per-framework subpath
|
|
10
|
+
// that four of five declare is exactly the silent gap `tests/package-subpath-parity.test.ts` exists
|
|
11
|
+
// to catch, and the file costs a re-export. Deliberately NOT on the public barrel — the emitted
|
|
12
|
+
// code names it, app code does not.
|
|
13
|
+
export { resolveStateStyle, } from '@symbiote-native/components/state-style';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "SymbioteNative's React adapter — a react-reconciler host config (mutation mode) driving real native iOS/Android views through stock React Native Fabric.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
"url": "https://github.com/OneEyed1366/symbiote-native/issues"
|
|
14
14
|
},
|
|
15
15
|
"author": "Andrey Prokopenko <psevdoproger@gmail.com>",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"react-native",
|
|
18
|
+
"symbiote-native",
|
|
19
|
+
"react",
|
|
20
|
+
"fabric",
|
|
21
|
+
"reconciler",
|
|
22
|
+
"renderer",
|
|
23
|
+
"cross-platform",
|
|
24
|
+
"mobile",
|
|
25
|
+
"ios",
|
|
26
|
+
"android"
|
|
27
|
+
],
|
|
16
28
|
"type": "module",
|
|
17
29
|
"main": "./build/index.js",
|
|
18
30
|
"module": "./build/index.js",
|
|
@@ -26,7 +38,11 @@
|
|
|
26
38
|
"types": "./build/bootstrap.d.ts",
|
|
27
39
|
"default": "./build/bootstrap.js"
|
|
28
40
|
},
|
|
29
|
-
"./metro-css-parser": "./metro-css-parser.cjs"
|
|
41
|
+
"./metro-css-parser": "./metro-css-parser.cjs",
|
|
42
|
+
"./state-style": {
|
|
43
|
+
"types": "./build/state-style.d.ts",
|
|
44
|
+
"default": "./build/state-style.js"
|
|
45
|
+
}
|
|
30
46
|
},
|
|
31
47
|
"files": [
|
|
32
48
|
"build",
|
|
@@ -37,20 +53,20 @@
|
|
|
37
53
|
},
|
|
38
54
|
"dependencies": {
|
|
39
55
|
"react-reconciler": "^0.33.0",
|
|
40
|
-
"@symbiote-native/components": "0.
|
|
41
|
-
"@symbiote-native/css-parser": "0.
|
|
56
|
+
"@symbiote-native/components": "1.0.0",
|
|
57
|
+
"@symbiote-native/css-parser": "0.5.0"
|
|
42
58
|
},
|
|
43
59
|
"peerDependencies": {
|
|
44
|
-
"@symbiote-native/engine": ">=0.1.7",
|
|
45
60
|
"react": ">=19.0.0",
|
|
46
|
-
"react-native": ">=0.86"
|
|
61
|
+
"react-native": ">=0.86",
|
|
62
|
+
"@symbiote-native/engine": "^0.4.0"
|
|
47
63
|
},
|
|
48
64
|
"devDependencies": {
|
|
49
65
|
"@types/react": "^19.2.0",
|
|
50
66
|
"@types/react-reconciler": "^0.32.2",
|
|
51
67
|
"react": "19.2.3",
|
|
52
|
-
"@symbiote-native/
|
|
53
|
-
"@symbiote-native/
|
|
68
|
+
"@symbiote-native/test-utils": "0.3.0",
|
|
69
|
+
"@symbiote-native/engine": "0.4.0"
|
|
54
70
|
},
|
|
55
71
|
"scripts": {
|
|
56
72
|
"typecheck": "tsc --build",
|