@symbiote-native/react 0.3.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 +24 -9
- package/build/bootstrap.js +1 -1
- package/build/components/activity-indicator/shared.js +1 -1
- package/build/components/button.js +1 -1
- package/build/components/flat-list/index.js +5 -2
- package/build/components/input-accessory-view/index.js +6 -1
- package/build/components/keyboard-avoiding-view/index.js +32 -12
- package/build/components/modal/index.js +1 -1
- package/build/components/pressable/index.d.ts +2 -1
- package/build/components/pressable/index.js +17 -5
- package/build/components/safe-area-view/index.d.ts +1 -0
- package/build/components/scroll-view/index.android.js +16 -6
- package/build/components/scroll-view/index.ios.js +3 -1
- package/build/components/scroll-view/shared.js +56 -7
- package/build/components/scroll-view/sticky-header.d.ts +11 -2
- package/build/components/scroll-view/sticky-header.js +53 -11
- package/build/components/text-input/index.js +15 -2
- package/build/components/touchable/index.d.ts +2 -0
- package/build/components/touchable/index.js +131 -29
- package/build/components/touchable-native-feedback/index.js +1 -1
- package/build/components/virtualized-list/index.js +64 -24
- package/build/components/virtualized-section-list/index.d.ts +5 -0
- package/build/components/virtualized-section-list/index.js +24 -4
- package/build/components.d.ts +4 -4
- package/build/components.js +19 -17
- package/build/create-portal/index.js +1 -1
- package/build/create-tunnel/index.js +1 -1
- package/build/hooks/use-window-dimensions.js +1 -1
- package/build/host-config.d.ts +2 -2
- package/build/host-config.js +42 -11
- package/build/host-instance.js +1 -1
- package/build/index.d.ts +8 -7
- package/build/index.js +1 -1
- 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 +10 -2
- package/build/modules/animated/index.d.ts +1 -1
- package/build/modules/animated/index.js +1 -1
- package/build/modules/status-bar/index.ios.js +1 -1
- package/build/render.js +16 -8
- package/build/state-style.d.ts +1 -0
- package/build/state-style.js +13 -0
- package/package.json +24 -8
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// useImperativeHandle + the per-entry render dispatch). Lower layer in RN's
|
|
7
7
|
// SectionList -> VirtualizedSectionList -> VirtualizedList stack.
|
|
8
8
|
import { createElement, useImperativeHandle, useRef, } from 'react';
|
|
9
|
-
import { dlog, Platform } from '@symbiote-native/engine';
|
|
9
|
+
import { dlog, Platform, } from '@symbiote-native/engine';
|
|
10
10
|
import { flattenSections, resolveStickySectionHeaders, scrollLocationToFlatIndex, sectionEntryKey, unwrapEntryItem, } from '@symbiote-native/components';
|
|
11
11
|
import { VirtualizedList, } from '../virtualized-list/index.js';
|
|
12
12
|
function resolveSeparator(component) {
|
|
@@ -20,7 +20,10 @@ export function VirtualizedSectionList(props) {
|
|
|
20
20
|
const { ref, sections, renderItem, renderSectionHeader, renderSectionFooter, SectionSeparatorComponent,
|
|
21
21
|
// Pulled out of `rest`: the user's separator is typed on ItemT, but the inner VirtualizedList
|
|
22
22
|
// streams ISectionEntry<ItemT>, so we wrap it to unwrap each entry back to its ItemT.
|
|
23
|
-
ItemSeparatorComponent, keyExtractor,
|
|
23
|
+
ItemSeparatorComponent, keyExtractor,
|
|
24
|
+
// Also pulled out of `rest`: the inner list streams entries, the user's callback expects
|
|
25
|
+
// sections. See entryItemLayout below.
|
|
26
|
+
getItemLayout, stickySectionHeadersEnabled, ...rest } = props;
|
|
24
27
|
const { entries, headerIndices } = flattenSections(sections, SectionSeparatorComponent !== undefined);
|
|
25
28
|
// RN sticks section headers by default only on iOS; Android does not unless asked. headerIndices
|
|
26
29
|
// are the flat positions of every section header; VirtualizedList forwards the in-window ones.
|
|
@@ -58,10 +61,14 @@ export function VirtualizedSectionList(props) {
|
|
|
58
61
|
const renderEntry = (info) => {
|
|
59
62
|
const entry = info.item;
|
|
60
63
|
if (entry.kind === 'header') {
|
|
61
|
-
return renderSectionHeader
|
|
64
|
+
return renderSectionHeader
|
|
65
|
+
? renderSectionHeader({ section: entry.section })
|
|
66
|
+
: undefined;
|
|
62
67
|
}
|
|
63
68
|
if (entry.kind === 'footer') {
|
|
64
|
-
return renderSectionFooter
|
|
69
|
+
return renderSectionFooter
|
|
70
|
+
? renderSectionFooter({ section: entry.section })
|
|
71
|
+
: undefined;
|
|
65
72
|
}
|
|
66
73
|
if (entry.kind === 'section-separator') {
|
|
67
74
|
return resolveSeparator(SectionSeparatorComponent);
|
|
@@ -83,6 +90,18 @@ export function VirtualizedSectionList(props) {
|
|
|
83
90
|
trailingItem: unwrapEntryItem(entryProps.trailingItem),
|
|
84
91
|
});
|
|
85
92
|
const entryKeyExtractor = (entry, index) => sectionEntryKey(entry, index, keyExtractor);
|
|
93
|
+
// Hand the callback `sections`, not the entries: RN's inner VirtualizedList gets
|
|
94
|
+
// `data={this.props.sections}` (VirtualizedSectionList.js:216) while ours streams the FLATTENED
|
|
95
|
+
// entries, so the same user code would otherwise see a different argument here than on RN.
|
|
96
|
+
//
|
|
97
|
+
// UPSTREAM-DIVERGENCE(react-native): the flat INDEX matches RN's (two rows per section, header
|
|
98
|
+
// and footer) only while SectionSeparatorComponent is unset. With it, flattenSections emits an
|
|
99
|
+
// extra 'section-separator' row per boundary that RN renders inside the neighbouring cell, so
|
|
100
|
+
// indices shift by one per boundary from the second section on. Deliberate - that row is how this
|
|
101
|
+
// adapter paints the separator; a caller combining the two must account for it.
|
|
102
|
+
const entryItemLayout = getItemLayout === undefined
|
|
103
|
+
? undefined
|
|
104
|
+
: (_entries, index) => getItemLayout(sections, index);
|
|
86
105
|
return createElement((VirtualizedList), {
|
|
87
106
|
ref: listRef,
|
|
88
107
|
data: entries,
|
|
@@ -90,6 +109,7 @@ export function VirtualizedSectionList(props) {
|
|
|
90
109
|
getItemCount: () => entries.length,
|
|
91
110
|
renderItem: renderEntry,
|
|
92
111
|
keyExtractor: entryKeyExtractor,
|
|
112
|
+
getItemLayout: entryItemLayout,
|
|
93
113
|
stickyHeaderIndices,
|
|
94
114
|
ItemSeparatorComponent: entrySeparatorComponent,
|
|
95
115
|
...rest,
|
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';
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// call site (e.g. a ref to a persistent "overlay host" View near your app root) — not a second,
|
|
11
11
|
// independently-mounted SymbioteSurface. Cross-surface targets aren't wired: resetAfterCommit only
|
|
12
12
|
// fires for the primary root's own container, so a different surface's tree would never repaint.
|
|
13
|
-
import { isSymbioteNode, SymbioteSurface } from '@symbiote-native/engine';
|
|
13
|
+
import { isSymbioteNode, SymbioteSurface, } from '@symbiote-native/engine';
|
|
14
14
|
import reconciler from '../host-config.js';
|
|
15
15
|
export function createPortal(children, container, key) {
|
|
16
16
|
if (!(container instanceof SymbioteSurface) && !isSymbioteNode(container)) {
|
|
@@ -15,7 +15,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
15
15
|
// nowhere to bounce back to. `Out` lives in whichever surface should PAINT the content; `In`
|
|
16
16
|
// lives anywhere - same surface or a different one - and never touches a Fabric node
|
|
17
17
|
// directly, so there's no "target must already be mounted" guard to satisfy.
|
|
18
|
-
import { Fragment, useEffect, useId, useSyncExternalStore } from 'react';
|
|
18
|
+
import { Fragment, useEffect, useId, useSyncExternalStore, } from 'react';
|
|
19
19
|
export function createTunnel() {
|
|
20
20
|
const items = new Map();
|
|
21
21
|
const listeners = new Set();
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// subscribing to close the gap between the render-time get and the effect-time
|
|
5
5
|
// listener. Only the window metrics changing triggers a re-render.
|
|
6
6
|
import { useEffect, useState } from 'react';
|
|
7
|
-
import { Dimensions } from '@symbiote-native/engine';
|
|
7
|
+
import { Dimensions, } from '@symbiote-native/engine';
|
|
8
8
|
export function useWindowDimensions() {
|
|
9
9
|
const [dimensions, setDimensions] = useState(() => Dimensions.get('window'));
|
|
10
10
|
useEffect(() => {
|
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, 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;
|
|
@@ -70,7 +79,9 @@ const reconciler = createReconciler({
|
|
|
70
79
|
getRootHostContext: () => ({ isInsideText: false }),
|
|
71
80
|
getChildHostContext(parentHostContext, type) {
|
|
72
81
|
const isInsideText = descriptorFor(type).isText;
|
|
73
|
-
return parentHostContext.isInsideText === isInsideText
|
|
82
|
+
return parentHostContext.isInsideText === isInsideText
|
|
83
|
+
? parentHostContext
|
|
84
|
+
: { isInsideText };
|
|
74
85
|
},
|
|
75
86
|
getPublicInstance: instance => toPublicInstance(instance),
|
|
76
87
|
prepareForCommit: () => null,
|
|
@@ -88,12 +99,12 @@ const reconciler = createReconciler({
|
|
|
88
99
|
},
|
|
89
100
|
shouldSetTextContent: () => false,
|
|
90
101
|
createInstance(type, props, _container, hostContext) {
|
|
91
|
-
const descriptor = descriptorFor(type);
|
|
102
|
+
const descriptor = descriptorFor(resolveIntrinsicTag(type, props));
|
|
92
103
|
if (hostContext.isInsideText && !descriptor.isText) {
|
|
93
104
|
throw new Error(`<${type}> can't be nested inside <Text>`);
|
|
94
105
|
}
|
|
95
106
|
const node = createElement(descriptor.component, descriptor.isText);
|
|
96
|
-
applyProps(node, props);
|
|
107
|
+
applyProps(node, foldHostBag(type, props));
|
|
97
108
|
return node;
|
|
98
109
|
},
|
|
99
110
|
createTextInstance(text, _container, hostContext) {
|
|
@@ -125,8 +136,14 @@ const reconciler = createReconciler({
|
|
|
125
136
|
removeChild(container, child);
|
|
126
137
|
},
|
|
127
138
|
finalizeInitialChildren: () => false,
|
|
128
|
-
commitUpdate(node,
|
|
129
|
-
|
|
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));
|
|
130
147
|
},
|
|
131
148
|
commitTextUpdate(node, _oldText, newText) {
|
|
132
149
|
setText(node, newText);
|
|
@@ -134,8 +151,12 @@ const reconciler = createReconciler({
|
|
|
134
151
|
resetTextContent: () => { },
|
|
135
152
|
hideTextInstance: node => setText(node, ''),
|
|
136
153
|
unhideTextInstance: (node, text) => setText(node, text),
|
|
137
|
-
|
|
138
|
-
|
|
154
|
+
// `Activity mode="hidden"` and a re-suspending `Suspense` both reach the tree through these.
|
|
155
|
+
// They were no-ops, so hidden content went on painting — stacked on top of whatever replaced
|
|
156
|
+
// it — and only the mount/unmount half of Activity appeared to work. The engine owns the
|
|
157
|
+
// reversible half (see setNodeHidden): the author's own style stays untouched underneath.
|
|
158
|
+
hideInstance: node => setNodeHidden(node, true),
|
|
159
|
+
unhideInstance: node => setNodeHidden(node, false),
|
|
139
160
|
beforeActiveInstanceBlur: () => { },
|
|
140
161
|
afterActiveInstanceBlur: () => { },
|
|
141
162
|
detachDeletedInstance: () => { },
|
|
@@ -146,7 +167,9 @@ const reconciler = createReconciler({
|
|
|
146
167
|
currentUpdatePriority = priority;
|
|
147
168
|
},
|
|
148
169
|
getCurrentUpdatePriority: () => currentUpdatePriority,
|
|
149
|
-
resolveUpdatePriority: () => currentUpdatePriority !== NoEventPriority
|
|
170
|
+
resolveUpdatePriority: () => currentUpdatePriority !== NoEventPriority
|
|
171
|
+
? currentUpdatePriority
|
|
172
|
+
: DefaultEventPriority,
|
|
150
173
|
maySuspendCommit: () => false,
|
|
151
174
|
NotPendingTransition: null,
|
|
152
175
|
// react's Context IS the runtime value react-reconciler's ReactContext wants
|
|
@@ -166,4 +189,12 @@ const reconciler = createReconciler({
|
|
|
166
189
|
suspendInstance: () => { },
|
|
167
190
|
waitForCommitToBeReady: () => null,
|
|
168
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
|
+
}
|
|
169
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';
|
|
@@ -7,9 +8,9 @@ export type { IImageProps, IImageSource, IImageSourceProp, IResizeMode, IImageSi
|
|
|
7
8
|
export { ImageBackground } from './components/image-background';
|
|
8
9
|
export type { IImageBackgroundProps } from './components/image-background';
|
|
9
10
|
export { ScrollView } from './components/scroll-view';
|
|
10
|
-
export type { IScrollViewProps, IScrollViewHandle } from './components/scroll-view';
|
|
11
|
+
export type { IScrollViewProps, IScrollViewHandle, } from './components/scroll-view';
|
|
11
12
|
export { TextInput } from './components/text-input';
|
|
12
|
-
export type { ITextInputProps, ITextInputHandle } from './components/text-input';
|
|
13
|
+
export type { ITextInputProps, ITextInputHandle, } from './components/text-input';
|
|
13
14
|
export { InputAccessoryView } from './components/input-accessory-view';
|
|
14
15
|
export type { IInputAccessoryViewProps } from './components/input-accessory-view';
|
|
15
16
|
export { KeyboardAvoidingView } from './components/keyboard-avoiding-view';
|
|
@@ -35,12 +36,12 @@ export type { IButtonProps } from './components/button';
|
|
|
35
36
|
export { FlatList } from './components/flat-list';
|
|
36
37
|
export type { IFlatListProps, IFlatListHandle } from './components/flat-list';
|
|
37
38
|
export { SectionList } from './components/section-list';
|
|
38
|
-
export type { ISectionListProps, ISectionListHandle, ISection } from './components/section-list';
|
|
39
|
+
export type { ISectionListProps, ISectionListHandle, ISection, } from './components/section-list';
|
|
39
40
|
export { VirtualizedSectionList } from './components/virtualized-section-list';
|
|
40
41
|
export type { IVirtualizedSectionListProps, IVirtualizedSectionListHandle, } from './components/virtualized-section-list';
|
|
41
42
|
export { VirtualizedList } from './components/virtualized-list';
|
|
42
43
|
export type { IVirtualizedListProps, IVirtualizedListHandle, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, } from './components/virtualized-list';
|
|
43
|
-
export type { IViewStyle, ITextStyle, IFlexAlign, IFlexJustify } from './utils/styles';
|
|
44
|
+
export type { IViewStyle, ITextStyle, IFlexAlign, IFlexJustify, } from './utils/styles';
|
|
44
45
|
export { mount, unmount } from './render';
|
|
45
46
|
export { createPortal, type IPortalContainer } from './create-portal';
|
|
46
47
|
export { createTunnel, type ITunnel } from './create-tunnel';
|
|
@@ -51,11 +52,11 @@ export { AppRegistry, setHostRegistrar } from './modules/app-registry';
|
|
|
51
52
|
export type { IComponentProvider, IAppParameters, IRunnable, IHostRegistrar, IWrapperComponentProvider, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from './modules/app-registry';
|
|
52
53
|
export { Animated, createAnimatedComponent } from './modules/animated';
|
|
53
54
|
export { Platform, StyleSheet } from '@symbiote-native/engine';
|
|
54
|
-
export { PlatformColor, DynamicColorIOS, processColor } from '@symbiote-native/engine';
|
|
55
|
+
export { PlatformColor, DynamicColorIOS, processColor, } from '@symbiote-native/engine';
|
|
55
56
|
export type { IColorValue, IOpaqueColorValue, IDynamicColorIOSTuple, } from '@symbiote-native/engine';
|
|
56
57
|
export { setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, } from '@symbiote-native/engine';
|
|
57
58
|
export { dlog, isDebug } from '@symbiote-native/engine';
|
|
58
|
-
export type { INativeViewConfig, INativeViewConfigSource } from '@symbiote-native/engine';
|
|
59
|
+
export type { INativeViewConfig, INativeViewConfigSource, } from '@symbiote-native/engine';
|
|
59
60
|
export type { IPlatformStatic, IPlatformOSType, IPlatformConstantsIOS, IPlatformConstantsAndroid, IPlatformSelectSpec, } from '@symbiote-native/engine';
|
|
60
61
|
export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, LayoutAnimation, PixelRatio, } from '@symbiote-native/engine';
|
|
61
62
|
export type { IAlertType, IAlertButtonStyle, IAlertButton, IAlertButtons, IAlertOptions, IShareContent, IShareOptions, IShareAction, IActionSheetIOSOptions, IShareActionSheetIOSOptions, IShareActionSheetError, IUrlEvent, II18nManagerConstants, IDisplayMetrics, IDisplayMetricsAndroid, IDimensionsPayload, IDimensionsSet, IDimensionsKey, IDimensionsChangeListener, IDimensionsStatic, IColorSchemeName, IColorSchemePreference, IAppStateStatus, IAppStateEvent, IKeyboardEventName, IKeyboardEvent, IKeyboardMetrics, IBackPressEventName, IBackPressHandler, IPermission, IPermissionStatus, IRationale, ILayoutAnimationType, ILayoutAnimationProperty, ILayoutAnimationConfig, ILayoutAnimationAnim, IPixelRatioStatic, } from '@symbiote-native/engine';
|
|
@@ -69,5 +70,5 @@ export { InteractionManager } from '@symbiote-native/engine';
|
|
|
69
70
|
export type { IInteractionEvent, ISimpleTask, IPromiseTask, ITask, IHandle, } from '@symbiote-native/engine';
|
|
70
71
|
export { TouchableNativeFeedback } from './components/touchable-native-feedback';
|
|
71
72
|
export type { ITouchableNativeFeedbackProps, INativeFeedbackBackground, IThemeAttrBackground, IRippleBackground, } from './components/touchable-native-feedback';
|
|
72
|
-
export type { ISymbioteEvent, ISymbioteNode, IRootTag } from '@symbiote-native/engine';
|
|
73
|
+
export type { ISymbioteEvent, ISymbioteNode, IRootTag, } from '@symbiote-native/engine';
|
|
73
74
|
export type { ICellLayout, ISeparatorProps, ISeparators, IModalOrientationChangeEvent, IPressableAndroidRippleConfig, IEnterKeyHint, IInputMode, ISubmitBehavior, ITextInputSelection, IImageStatics, } from '@symbiote-native/components';
|
package/build/index.js
CHANGED
|
@@ -49,7 +49,7 @@ export { Platform, StyleSheet } from '@symbiote-native/engine';
|
|
|
49
49
|
// Color utilities: PlatformColor / DynamicColorIOS build opaque platform colors;
|
|
50
50
|
// processColor runs a color through the injected platform processor. All pure /
|
|
51
51
|
// seam-backed, so they live in shared and the adapter re-exports them.
|
|
52
|
-
export { PlatformColor, DynamicColorIOS, processColor } from '@symbiote-native/engine';
|
|
52
|
+
export { PlatformColor, DynamicColorIOS, processColor, } from '@symbiote-native/engine';
|
|
53
53
|
// The three app-entry seams, wired once on a real host, so the barrel exposes them together.
|
|
54
54
|
// setNativeViewConfigSource hands the engine RN's ViewConfig registry, which is how third-party
|
|
55
55
|
// Fabric views auto-derive their metadata:
|
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;
|
|
@@ -74,11 +80,13 @@ export function createAnimatedComponent(Component) {
|
|
|
74
80
|
const passthroughStyle = readPassthroughStyle(passthrough);
|
|
75
81
|
if (passthroughStyle !== undefined) {
|
|
76
82
|
reduced.style =
|
|
77
|
-
reduced.style === undefined
|
|
83
|
+
reduced.style === undefined
|
|
84
|
+
? passthroughStyle
|
|
85
|
+
: [reduced.style, passthroughStyle];
|
|
78
86
|
}
|
|
79
87
|
const childProps = Object.assign(Object.create(null), reduced, { ref: captureRef });
|
|
80
88
|
return createElement(Component, childProps);
|
|
81
89
|
}
|
|
82
|
-
AnimatedComponent.displayName = `Animated(${Component
|
|
90
|
+
AnimatedComponent.displayName = `Animated(${baseNameOf(Component)})`;
|
|
83
91
|
return AnimatedComponent;
|
|
84
92
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AnimatedValue, AnimatedValueXY, AnimatedColor, timing, spring, decay, parallel, sequence, stagger, loop, delay, add, subtract, multiply, divide, modulo, diffClamp, event, forkEvent, unforkEvent } from '@symbiote-native/engine';
|
|
2
2
|
import { createAnimatedComponent } from './create-animated-component';
|
|
3
3
|
export { createAnimatedComponent } from './create-animated-component';
|
|
4
|
-
export { AnimatedProps, AnimatedStyle, AnimatedTransform } from '@symbiote-native/engine';
|
|
4
|
+
export { AnimatedProps, AnimatedStyle, AnimatedTransform, } from '@symbiote-native/engine';
|
|
5
5
|
export declare const Animated: {
|
|
6
6
|
Value: typeof AnimatedValue;
|
|
7
7
|
ValueXY: typeof AnimatedValueXY;
|
|
@@ -14,7 +14,7 @@ import { AnimatedMock } from '@symbiote-native/engine';
|
|
|
14
14
|
export { createAnimatedComponent } from './create-animated-component.js';
|
|
15
15
|
// The pure graph leaves now live in @symbiote-native/engine (they extend AnimatedWithChildren,
|
|
16
16
|
// no React); re-exported here so @symbiote-native/react's Animated surface is unchanged.
|
|
17
|
-
export { AnimatedProps, AnimatedStyle, AnimatedTransform } from '@symbiote-native/engine';
|
|
17
|
+
export { AnimatedProps, AnimatedStyle, AnimatedTransform, } from '@symbiote-native/engine';
|
|
18
18
|
// View/Text/Image are pure host primitives, so wrap them eagerly.
|
|
19
19
|
const AnimatedView = createAnimatedComponent(View);
|
|
20
20
|
const AnimatedText = createAnimatedComponent(Text);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// it, which is also what a bare '@symbiote-native/engine' import resolves to under vitest — so
|
|
11
11
|
// this file's own engine import is already vitest-correct, no platform-selection subtlety here.
|
|
12
12
|
import { useEffect } from 'react';
|
|
13
|
-
import { applyStatusBarProps, statusBarImperative } from '@symbiote-native/engine';
|
|
13
|
+
import { applyStatusBarProps, statusBarImperative, } from '@symbiote-native/engine';
|
|
14
14
|
// StatusBar renders null and applies its props to the native module in an effect,
|
|
15
15
|
// on mount and on every prop change. Simplification vs RN: RN maintains a
|
|
16
16
|
// prop-merge stack so nested StatusBars compose (deepest/last wins). This module
|
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) {
|
|
@@ -11,10 +11,20 @@ function errorReporter(origin) {
|
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
const onUncaughtError = errorReporter('react render (no error boundary)');
|
|
14
|
-
// A boundary handled it,
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
14
|
+
// A boundary handled it, so it does NOT reach the native redbox — it goes to , off unless
|
|
15
|
+
// DEBUG is set.
|
|
16
|
+
//
|
|
17
|
+
// This deliberately DIVERGES from upstream, whose nativeOnCaughtError calls the same
|
|
18
|
+
// showErrorDialog as the uncaught path. The argument for upstream's choice is that the boundary
|
|
19
|
+
// decides what the USER sees, not whether the developer hears. The argument against, and the one
|
|
20
|
+
// this project takes: writing an ErrorBoundary IS the developer saying "I know this can throw and
|
|
21
|
+
// I am handling it here". Answering that with a full-screen redbox over the fallback the app just
|
|
22
|
+
// rendered contradicts the thing the app asked for, and it is the only adapter here that did it —
|
|
23
|
+
// Solid's ErrorBoundary is silent, and its canary reads as correct next to React's alarming one.
|
|
24
|
+
// An UNCAUGHT error still hits the redbox; the difference is exactly whether someone claimed it.
|
|
25
|
+
const onCaughtError = (error, info) => {
|
|
26
|
+
dlog(() => `react render (caught by error boundary): ${String(error)}${info.componentStack ?? ''}`);
|
|
27
|
+
};
|
|
18
28
|
// React recovered on its own (a concurrent render retried and succeeded), so this is not fatal -
|
|
19
29
|
// but it is still a real error the app swallowed a first render over. RN keeps React's
|
|
20
30
|
// defaultOnRecoverableError here, which reports it globally.
|
|
@@ -30,10 +40,8 @@ const onRecoverableError = errorReporter('react render (recovered)');
|
|
|
30
40
|
// to compare against Vue/Angular's microtask-coalesced requestCommit(). Kept
|
|
31
41
|
// behind DEBUG per <keep_logs_gate_behind_DEBUG>, never removed.
|
|
32
42
|
setEventDispatcher(run => {
|
|
33
|
-
withDiscretePriority(run);
|
|
34
43
|
dlog('react event-dispatch: forced flushSyncWork');
|
|
35
|
-
|
|
36
|
-
reconciler.flushSyncWork();
|
|
44
|
+
flushExternalUpdate(run);
|
|
37
45
|
});
|
|
38
46
|
const containers = new Map();
|
|
39
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",
|