@symbiote-native/solid 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bootstrap.d.ts +7 -0
- package/build/bootstrap.js +18 -0
- package/build/components/activity-indicator/index.android.d.ts +2 -0
- package/build/components/activity-indicator/index.android.js +11 -0
- package/build/components/activity-indicator/index.d.ts +1 -0
- package/build/components/activity-indicator/index.ios.d.ts +2 -0
- package/build/components/activity-indicator/index.ios.js +10 -0
- package/build/components/activity-indicator/index.js +4 -0
- package/build/components/activity-indicator/shared.d.ts +8 -0
- package/build/components/activity-indicator/shared.js +51 -0
- package/build/components/button.d.ts +7 -0
- package/build/components/button.jsx +45 -0
- package/build/components/flat-list/index.d.ts +10 -0
- package/build/components/flat-list/index.jsx +118 -0
- package/build/components/image-background.d.ts +10 -0
- package/build/components/image-background.jsx +74 -0
- package/build/components/image.d.ts +9 -0
- package/build/components/image.js +75 -0
- package/build/components/input-accessory-view.d.ts +11 -0
- package/build/components/input-accessory-view.jsx +39 -0
- package/build/components/keyboard-avoiding-view.d.ts +15 -0
- package/build/components/keyboard-avoiding-view.jsx +152 -0
- package/build/components/modal.d.ts +24 -0
- package/build/components/modal.jsx +125 -0
- package/build/components/pressable.d.ts +30 -0
- package/build/components/pressable.jsx +185 -0
- package/build/components/refresh-control.d.ts +18 -0
- package/build/components/refresh-control.jsx +36 -0
- package/build/components/safe-area-view.d.ts +10 -0
- package/build/components/safe-area-view.jsx +24 -0
- package/build/components/scroll-view/index.android.d.ts +2 -0
- package/build/components/scroll-view/index.android.js +11 -0
- package/build/components/scroll-view/index.d.ts +2 -0
- package/build/components/scroll-view/index.ios.d.ts +2 -0
- package/build/components/scroll-view/index.ios.js +8 -0
- package/build/components/scroll-view/index.js +9 -0
- package/build/components/scroll-view/shared.d.ts +85 -0
- package/build/components/scroll-view/shared.jsx +334 -0
- package/build/components/scroll-view/sticky-header.d.ts +18 -0
- package/build/components/scroll-view/sticky-header.jsx +182 -0
- package/build/components/section-list/index.d.ts +6 -0
- package/build/components/section-list/index.jsx +8 -0
- package/build/components/switch/index.android.d.ts +2 -0
- package/build/components/switch/index.android.js +12 -0
- package/build/components/switch/index.d.ts +1 -0
- package/build/components/switch/index.ios.d.ts +2 -0
- package/build/components/switch/index.ios.js +11 -0
- package/build/components/switch/index.js +4 -0
- package/build/components/switch/shared.d.ts +11 -0
- package/build/components/switch/shared.js +87 -0
- package/build/components/text-input.d.ts +10 -0
- package/build/components/text-input.js +268 -0
- package/build/components/text.d.ts +26 -0
- package/build/components/text.jsx +27 -0
- package/build/components/touchable/index.d.ts +22 -0
- package/build/components/touchable/index.jsx +247 -0
- package/build/components/touchable-native-feedback/index.d.ts +16 -0
- package/build/components/touchable-native-feedback/index.jsx +68 -0
- package/build/components/view.d.ts +32 -0
- package/build/components/view.jsx +49 -0
- package/build/components/virtualized-list/index.android.d.ts +2 -0
- package/build/components/virtualized-list/index.android.js +9 -0
- package/build/components/virtualized-list/index.d.ts +1 -0
- package/build/components/virtualized-list/index.ios.d.ts +2 -0
- package/build/components/virtualized-list/index.ios.js +9 -0
- package/build/components/virtualized-list/index.js +4 -0
- package/build/components/virtualized-list/shared.d.ts +75 -0
- package/build/components/virtualized-list/shared.jsx +782 -0
- package/build/components/virtualized-section-list/index.d.ts +62 -0
- package/build/components/virtualized-section-list/index.jsx +145 -0
- package/build/components.d.ts +43 -0
- package/build/components.js +54 -0
- package/build/create-portal/index.d.ts +11 -0
- package/build/create-portal/index.jsx +88 -0
- package/build/create-tunnel/index.d.ts +13 -0
- package/build/create-tunnel/index.jsx +53 -0
- package/build/descriptor-to-solid.d.ts +3 -0
- package/build/descriptor-to-solid.js +91 -0
- package/build/host-instance.d.ts +2 -0
- package/build/host-instance.js +36 -0
- package/build/index.d.ts +30 -0
- package/build/index.js +96 -0
- package/build/jsx-runtime.d.ts +26 -0
- package/build/jsx-runtime.js +46 -0
- package/build/modules/animated/create-animated-component.d.ts +10 -0
- package/build/modules/animated/create-animated-component.jsx +105 -0
- package/build/modules/animated/index.d.ts +54 -0
- package/build/modules/animated/index.js +78 -0
- package/build/modules/app-registry/index.d.ts +7 -0
- package/build/modules/app-registry/index.js +37 -0
- package/build/modules/status-bar.d.ts +7 -0
- package/build/modules/status-bar.js +45 -0
- package/build/primitives/create-color-scheme.d.ts +3 -0
- package/build/primitives/create-color-scheme.js +33 -0
- package/build/primitives/create-window-dimensions.d.ts +3 -0
- package/build/primitives/create-window-dimensions.js +34 -0
- package/build/render.d.ts +7 -0
- package/build/render.js +91 -0
- package/build/renderer.d.ts +7 -0
- package/build/renderer.js +195 -0
- package/build/utils/host-ref.d.ts +3 -0
- package/build/utils/host-ref.js +22 -0
- package/build/utils/stable-keys.d.ts +1 -0
- package/build/utils/stable-keys.js +31 -0
- package/package.json +5 -5
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export { mount, unmount } from './render';
|
|
2
|
+
export { findNodeHandle } from './host-instance';
|
|
3
|
+
export type { IHostInstance } from './host-instance';
|
|
4
|
+
export * from './components';
|
|
5
|
+
export type { IAccessibilityProps, IAriaProps, IAccessibilityRole, IRole, IAccessibilityStateValue, IAccessibilityValue, IAccessibilityActionInfo, IResponderProps, IImageSource, IImageSourceProp, IResizeMode, IImageSize, IImageCacheStatus, IEnterKeyHint, IInputMode, ISubmitBehavior, } from '@symbiote-native/components';
|
|
6
|
+
export { setImageSourceResolver } from '@symbiote-native/components';
|
|
7
|
+
export { descriptorToSolid } from './descriptor-to-solid';
|
|
8
|
+
export { AppRegistry, setHostRegistrar } from './modules/app-registry';
|
|
9
|
+
export type { IComponentProvider, IWrapperComponentProvider, IAppParameters, IRunnable, IHostRegistrar, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from './modules/app-registry';
|
|
10
|
+
export { StatusBar } from './modules/status-bar';
|
|
11
|
+
export type { IStatusBarProps, IStatusBarStyle } from './modules/status-bar';
|
|
12
|
+
export { Portal } from './create-portal';
|
|
13
|
+
export type { IPortalProps, IPortalTarget } from './create-portal';
|
|
14
|
+
export { createTunnel } from './create-tunnel';
|
|
15
|
+
export type { ITunnel, ITunnelInProps } from './create-tunnel';
|
|
16
|
+
export { createColorScheme } from './primitives/create-color-scheme';
|
|
17
|
+
export { createWindowDimensions } from './primitives/create-window-dimensions';
|
|
18
|
+
export { Animated, createAnimatedComponent } from './modules/animated';
|
|
19
|
+
export type { IAnimatedComponentProps } from './modules/animated';
|
|
20
|
+
export { Platform, StyleSheet, PixelRatio, PlatformColor, DynamicColorIOS, processColor, } from '@symbiote-native/engine';
|
|
21
|
+
export { setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, } from '@symbiote-native/engine';
|
|
22
|
+
export { dlog, isDebug } from '@symbiote-native/engine';
|
|
23
|
+
export type { IViewStyle, ITextStyle, IFlexAlign, IFlexJustify, ISymbioteEvent, ISymbioteNode, IRootTag, INativeViewConfig, INativeViewConfigSource, IColorValue, IOpaqueColorValue, IDynamicColorIOSTuple, IPixelRatioStatic, IPlatformStatic, IPlatformOSType, IPlatformConstantsIOS, IPlatformConstantsAndroid, IPlatformSelectSpec, } from '@symbiote-native/engine';
|
|
24
|
+
export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, LayoutAnimation, InteractionManager, } from '@symbiote-native/engine';
|
|
25
|
+
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, IInteractionEvent, ISimpleTask, IPromiseTask, ITask, IHandle, } from '@symbiote-native/engine';
|
|
26
|
+
export { PanResponder } from '@symbiote-native/engine';
|
|
27
|
+
export type { IPanResponderGestureState, IPanResponderCallbacks, IGestureResponderHandlers, IPanResponderInstance, } from '@symbiote-native/engine';
|
|
28
|
+
export { AccessibilityInfo } from '@symbiote-native/engine';
|
|
29
|
+
export type { IAccessibilityChangeEvent, IAccessibilityChangeEventName, IAccessibilityChangeEventHandler, IAccessibilityAnnouncementFinishedEvent, IAnnounceForAccessibilityOptions, IAccessibilityEventType, } from '@symbiote-native/engine';
|
|
30
|
+
export { For, Index, Show, ErrorBoundary, Suspense, SuspenseList, } from 'solid-js';
|
package/build/index.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// @symbiote-native/solid: a Solid adapter over @symbiote-native/engine, built on Solid's own
|
|
2
|
+
// universal custom-renderer API (solid-js/universal). Compiled Solid JSX drives real native
|
|
3
|
+
// iOS/Android views; underneath, every call routes into the same mutation API every other adapter
|
|
4
|
+
// uses. All Fabric clone-on-write lives in the engine, shared cross-adapter. App code imports
|
|
5
|
+
// @symbiote-native/solid, never react-native.
|
|
6
|
+
//
|
|
7
|
+
// Layer status (symbiote-new-adapter §7): L4 in progress. The primitives, the stateful touch
|
|
8
|
+
// components, the Touchable family, Button / ImageBackground / InputAccessoryView, StatusBar and
|
|
9
|
+
// the list family are done at full parity with the React reference, and the engine-owned runtime
|
|
10
|
+
// modules are re-exported below. Content relocation is in too: `Portal` (same-surface, React's
|
|
11
|
+
// createPortal twin) and `createTunnel` (cross-surface), both written over the universal renderer
|
|
12
|
+
// — solid-js/web's DOM-bound Portal was never the route to either. This barrel grows a layer at a
|
|
13
|
+
// time on purpose, so a break localizes to the layer that introduced it.
|
|
14
|
+
//
|
|
15
|
+
// The two lifecycle primitives are spelled `createColorScheme` / `createWindowDimensions`, not
|
|
16
|
+
// `use*`: in Solid `use*` means consuming something that already exists (useContext,
|
|
17
|
+
// useTransition) while a function that creates its own state and owns a subscription is `createX`.
|
|
18
|
+
// Angular spells the same pair as services for the same idiom reason — this is a naming
|
|
19
|
+
// divergence <adapter_src_follows_framework_idioms> asks for, not a parity gap.
|
|
20
|
+
// No JSX augmentation is imported here. The host tags live in this package's own JSX namespace at
|
|
21
|
+
// ./jsx-runtime, which an app reaches by config rather than by import:
|
|
22
|
+
// `"jsxImportSource": "@symbiote-native/solid"` (see that file). The earlier
|
|
23
|
+
// `declare module 'solid-js'` shim is gone with it — one mechanism, not two.
|
|
24
|
+
export { mount, unmount } from './render.js';
|
|
25
|
+
export { findNodeHandle } from './host-instance.js';
|
|
26
|
+
export * from './components.js';
|
|
27
|
+
// The app-entry seam that hands Image RN's own source resolver; grouped with the Image types it
|
|
28
|
+
// serves rather than with the engine seams below, matching React's and Svelte's barrels.
|
|
29
|
+
export { setImageSourceResolver } from '@symbiote-native/components';
|
|
30
|
+
// descriptorToSolid: the @symbiote-native/components Descriptor → engine-node bridge. Exported for
|
|
31
|
+
// the same reason React exports descriptorToReact — an external wrapper package over a third-party
|
|
32
|
+
// native view maps its shared render fn's Descriptor through the SAME bridge the adapter uses.
|
|
33
|
+
export { descriptorToSolid } from './descriptor-to-solid.js';
|
|
34
|
+
// Safe in the barrel (unlike ./bootstrap, which imports react-native): the registry seam itself
|
|
35
|
+
// only reaches the engine and solid-js.
|
|
36
|
+
export { AppRegistry, setHostRegistrar } from './modules/app-registry/index.js';
|
|
37
|
+
// Animated: the six animated components RN exposes, over the engine's framework-agnostic value
|
|
38
|
+
// graph. Solid's JSX takes the dotted form directly — `<Animated.View/>` compiles to
|
|
39
|
+
// createComponent(Animated.View, …) — so no local alias is needed, unlike Angular, whose AOT
|
|
40
|
+
// compiler cannot trace a class through property access (.claude/rules/dotted-component-tags.md).
|
|
41
|
+
export { StatusBar } from './modules/status-bar.js';
|
|
42
|
+
// Portal: same-surface content relocation, the twin of React's createPortal and Angular's
|
|
43
|
+
// PortalDirective/PortalOutletDirective. `mount` takes an already-mounted host node in THIS
|
|
44
|
+
// surface (a ref off a rendered component) or the surface itself; reaching a second, separately
|
|
45
|
+
// mounted surface is a different mechanism by design — see ./create-portal's header.
|
|
46
|
+
export { Portal } from './create-portal/index.jsx';
|
|
47
|
+
// createTunnel: cross-surface content sharing, the case Portal deliberately does not cover. A
|
|
48
|
+
// shared store, never a reach into a foreign surface's tree — `Out` paints from whichever surface
|
|
49
|
+
// renders it. Same name and same In/Out shape as the React, Vue, Svelte and Angular versions.
|
|
50
|
+
export { createTunnel } from './create-tunnel/index.jsx';
|
|
51
|
+
// Reactive wrappers over the engine's own Appearance / Dimensions event sources. The engine keeps
|
|
52
|
+
// the subscription logic; these add only the Solid lifecycle (signal + onCleanup).
|
|
53
|
+
export { createColorScheme } from './primitives/create-color-scheme.js';
|
|
54
|
+
export { createWindowDimensions } from './primitives/create-window-dimensions.js';
|
|
55
|
+
export { Animated, createAnimatedComponent } from './modules/animated/index.js';
|
|
56
|
+
// Everything below is re-exported VERBATIM from the shared packages — no Solid half exists or is
|
|
57
|
+
// wanted, so a passthrough stub file would only add a hop (.claude/rules/barrel-passthrough.md).
|
|
58
|
+
// The app names @symbiote-native/solid and gets RN's single import root.
|
|
59
|
+
// Pure utilities and the color surface: no native event, no lifecycle, so they live in the engine
|
|
60
|
+
// per <runtime_modules_layering>.
|
|
61
|
+
export { Platform, StyleSheet, PixelRatio, PlatformColor, DynamicColorIOS, processColor, } from '@symbiote-native/engine';
|
|
62
|
+
// The three app-entry seams, wired once on a real host. setNativeViewConfigSource is how a
|
|
63
|
+
// third-party Fabric view auto-derives its metadata:
|
|
64
|
+
// setNativeViewConfigSource(name => ReactNativeViewConfigRegistry.get(name))
|
|
65
|
+
export { setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, } from '@symbiote-native/engine';
|
|
66
|
+
// Diagnostics gated by DEBUG (<keep_logs_gate_behind_DEBUG>): app code logs through the same seam
|
|
67
|
+
// the engine does, never a bare console.log.
|
|
68
|
+
export { dlog, isDebug } from '@symbiote-native/engine';
|
|
69
|
+
// Imperative runtime modules: thin JS over getNativeModule + device events, no Fabric component
|
|
70
|
+
// of their own, so there is nothing per-framework to write.
|
|
71
|
+
export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, LayoutAnimation, InteractionManager, } from '@symbiote-native/engine';
|
|
72
|
+
// PanResponder: the gesture recognizer over the engine's responder events, shared by every adapter.
|
|
73
|
+
export { PanResponder } from '@symbiote-native/engine';
|
|
74
|
+
// AccessibilityInfo comes STRAIGHT from the engine, which owns the iOS/Android split
|
|
75
|
+
// (core/engine/src/accessibility-info/). React routes it through an adapter folder of the same
|
|
76
|
+
// name, but that folder is a pure passthrough of these exact names — matching it means matching
|
|
77
|
+
// the engine module, not growing a second one here.
|
|
78
|
+
export { AccessibilityInfo } from '@symbiote-native/engine';
|
|
79
|
+
// Solid's control flow is pure reactivity with no DOM dependency, so it works verbatim over the
|
|
80
|
+
// universal renderer and is re-exported here to keep an app on one import. solid-js/web's own
|
|
81
|
+
// `Portal` is NOT among them and cannot be — it allocates its container with
|
|
82
|
+
// `document.createElement` — but the CAPABILITY is not missing: this package ships its own
|
|
83
|
+
// `Portal` over the universal renderer (./create-portal, exported above), at the same
|
|
84
|
+
// same-surface scope React and Angular chose for theirs. `Dynamic` is the one member of that DOM-bound pair still absent.
|
|
85
|
+
// It is absent on its own terms, not as fallout from Portal: nothing here needed it (a tunnel
|
|
86
|
+
// renders stored content by calling a thunk inside <For>, no dynamic component involved), and no
|
|
87
|
+
// other adapter ships a twin of it for parity to require one.
|
|
88
|
+
//
|
|
89
|
+
// Solid's own control-flow `Switch`/`Match` pair is ABSENT for a different reason: `Switch` collides
|
|
90
|
+
// head-on with RN's Switch component, which every other adapter exports under exactly that name and
|
|
91
|
+
// which P0 parity (<adapters_reach_full_feature_parity>) pins here too. The RN component wins the
|
|
92
|
+
// name; the control-flow pair keeps its canonical home, `import { Switch, Match } from 'solid-js'`
|
|
93
|
+
// (alias it there if both are needed in one file). `Match` is withheld ALONGSIDE it deliberately —
|
|
94
|
+
// re-exporting a lone `Match` while `Switch` means something else would compile fine and then fail
|
|
95
|
+
// at runtime, which is worse than a missing export.
|
|
96
|
+
export { For, Index, Show, ErrorBoundary, Suspense, SuspenseList, } from 'solid-js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ISymbioteIntrinsic } from '@symbiote-native/components';
|
|
2
|
+
import type { ISymbioteNode } from '@symbiote-native/engine';
|
|
3
|
+
type ISymbioteHostAttributes = {
|
|
4
|
+
readonly children?: JSX.Element;
|
|
5
|
+
readonly [attribute: string]: unknown;
|
|
6
|
+
};
|
|
7
|
+
export declare namespace JSX {
|
|
8
|
+
type Element = ISymbioteNode | IArrayElement | IFunctionElement | (string & {}) | number | boolean | null | undefined;
|
|
9
|
+
interface IArrayElement extends Array<Element> {
|
|
10
|
+
}
|
|
11
|
+
interface IFunctionElement {
|
|
12
|
+
(): Element;
|
|
13
|
+
}
|
|
14
|
+
interface ElementClass {
|
|
15
|
+
}
|
|
16
|
+
interface ElementAttributesProperty {
|
|
17
|
+
}
|
|
18
|
+
interface ElementChildrenAttribute {
|
|
19
|
+
children: {};
|
|
20
|
+
}
|
|
21
|
+
interface IntrinsicAttributes {
|
|
22
|
+
}
|
|
23
|
+
interface IntrinsicElements extends Record<ISymbioteIntrinsic, ISymbioteHostAttributes> {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// The package's own JSX namespace — what an app points `jsxImportSource` at:
|
|
2
|
+
//
|
|
3
|
+
// // tsconfig.json
|
|
4
|
+
// "jsx": "preserve",
|
|
5
|
+
// "jsxImportSource": "@symbiote-native/solid"
|
|
6
|
+
//
|
|
7
|
+
// TypeScript resolves the namespace from `<jsxImportSource>/jsx-runtime` and NOTHING ELSE. That
|
|
8
|
+
// holds under `jsx: "preserve"` too, which is easy to doubt — the flag is documented as naming the
|
|
9
|
+
// module the `jsx`/`jsxs` factories are imported from, and `preserve` imports nothing. Measured:
|
|
10
|
+
// pointing jsxImportSource at a package that does not exist fails with
|
|
11
|
+
// `TS2875: This JSX tag requires the module path '<source>/jsx-runtime' to exist`, on a config
|
|
12
|
+
// whose `jsx` is `preserve`. So the entry is type-only by nature — the emitted build/jsx-runtime.js
|
|
13
|
+
// is empty and never imported by anything, because babel-preset-solid rewrites JSX into calls on
|
|
14
|
+
// ./renderer instead (that is the `moduleName` option, a separate mechanism from this one).
|
|
15
|
+
//
|
|
16
|
+
// WHY OUR OWN NAMESPACE AND NOT AN AUGMENTATION OF SOLID'S. `@lightningtv/solid` (3.2.5), the
|
|
17
|
+
// most widely used universal renderer on a non-DOM host, ships exactly
|
|
18
|
+
// `declare module 'solid-js' { namespace JSX { interface IntrinsicElements { … } } }` plus
|
|
19
|
+
// `export type { JSX } from 'solid-js'`. That re-export is genuinely all it takes to make the
|
|
20
|
+
// entry resolvable, and it is what this package's own src/jsx.ts did before this file replaced it.
|
|
21
|
+
// It buys one thing: an app stops needing a `/// <reference types="@symbiote-native/solid" />` line
|
|
22
|
+
// to pull the augmentation into its program. It does NOT buy the other two, and both matter more:
|
|
23
|
+
//
|
|
24
|
+
// 1. Augmenting MERGES into solid-js's own IntrinsicElements, which lists every HTML and SVG
|
|
25
|
+
// tag. `<div>` stays valid JSX in a React Native app and fails at runtime, not at compile
|
|
26
|
+
// time. Declaring the interface fresh here is what makes an unknown tag an error.
|
|
27
|
+
// 2. solid-js's `JSX.Element` is `Node | ArrayElement | (string & {}) | number | boolean | null
|
|
28
|
+
// | undefined`, and `Node` is a DOM type. A React Native program has no DOM lib (this repo:
|
|
29
|
+
// `lib: ["ES2022"]`; the canary: RN's own config, equally DOM-less), so the name does not
|
|
30
|
+
// resolve — and `skipLibCheck: true` SUPPRESSES that error inside solid-js's .d.ts rather
|
|
31
|
+
// than surfacing it, leaving `Node` as `any`. One `any` in a union collapses the whole union,
|
|
32
|
+
// so `JSX.Element` is `any` and every JSX child position in every consuming app goes
|
|
33
|
+
// unchecked. Measured two ways: `const bogus: JSX.Element = { definitelyNotAnElement: true }`
|
|
34
|
+
// compiles, and `const node: Node = …` in a first-party file (not skipLibCheck'd) reports
|
|
35
|
+
// `TS2304: Cannot find name 'Node'`.
|
|
36
|
+
//
|
|
37
|
+
// The risk this trades against — recorded when the swap was deferred — is that our `Element` has
|
|
38
|
+
// to line up with what solid-js's own `<Show>` / `<For>` return. It does, for a reason that only
|
|
39
|
+
// holds because of point 2 above: those return solid's `JSX.Element`, which IS `any` in exactly the
|
|
40
|
+
// DOM-less programs we target, so it is assignable to anything of ours. jsx-runtime.test.tsx pins
|
|
41
|
+
// the runtime half (both compose through this renderer and re-render on a signal); the type half is
|
|
42
|
+
// carried by examples/solid's own `tsc --noEmit`. The one configuration that would break the
|
|
43
|
+
// alignment is an app that pulls in the DOM lib, giving solid's `Element` a real `Node` — recorded
|
|
44
|
+
// in `.claude/rules/solid-jsx-namespace.md`.
|
|
45
|
+
export {};
|
|
46
|
+
/* eslint-enable @typescript-eslint/no-namespace, @typescript-eslint/no-empty-object-type */
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Component, type Ref } from 'solid-js';
|
|
2
|
+
import type { JSX } from '../../jsx-runtime';
|
|
3
|
+
export interface IAnimatedComponentProps {
|
|
4
|
+
style?: unknown;
|
|
5
|
+
ref?: Ref<unknown>;
|
|
6
|
+
passthroughAnimatedPropExplicitValues?: unknown;
|
|
7
|
+
children?: JSX.Element;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
export declare function createAnimatedComponent(Base: Component<any>): (props: IAnimatedComponentProps) => JSX.Element;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// createAnimatedComponent for @symbiote-native/solid: wraps a base component so its props may
|
|
2
|
+
// carry AnimatedNodes. The Solid twin of adapters/{react,vue}/src/modules/animated/
|
|
3
|
+
// create-animated-component; grown from components/scroll-view/animated-host.tsx, which was the
|
|
4
|
+
// same body pinned to `symbiote-view` and is absorbed by this file.
|
|
5
|
+
//
|
|
6
|
+
// A GENERIC WRAP IS POSSIBLE HERE, unlike Svelte. A Solid component is a plain function and
|
|
7
|
+
// `createComponent(Comp, props)` is an ordinary runtime call over any component reference — the
|
|
8
|
+
// same call compiled JSX itself emits. Svelte's four hand-authored .svelte files exist because a
|
|
9
|
+
// compiled Svelte component is not invocable that way; Angular's six explicit @Components exist
|
|
10
|
+
// because AOT-under-Metro ships no JIT. Neither constraint applies.
|
|
11
|
+
//
|
|
12
|
+
// The per-FRAME path never comes through here: value.setValue / animation -> flushValue ->
|
|
13
|
+
// AnimatedProps.update() -> setNativeProps(node). Under the native driver not even that runs — the
|
|
14
|
+
// UI thread owns the props once the leaf is connected to the view tag. This wrap only supplies the
|
|
15
|
+
// DECLARATIVE bag (animated nodes replaced by their current values) so first paint and every
|
|
16
|
+
// non-animated recompute carry concrete numbers.
|
|
17
|
+
//
|
|
18
|
+
// THE COMMIT-TIMING TRAP IS LIVE HERE. This adapter commits through `requestCommit()`, which is
|
|
19
|
+
// microtask-coalesced, so a mount-time effect runs BEFORE the node has a Fabric tag and the native
|
|
20
|
+
// half of the reconcile would silently no-op. `whenCommitted` is the retry seam (Angular passes the
|
|
21
|
+
// same thing for the same reason). Only the NATIVE half is deferred — the leaf is always built and
|
|
22
|
+
// attached to the value graph synchronously, because a deferred build sits behind a canceller the
|
|
23
|
+
// next reconcile drops.
|
|
24
|
+
import { createEffect, mergeProps, onCleanup, splitProps, } from 'solid-js';
|
|
25
|
+
import { createAnimatedLeafLifecycle, isNativeAnimatedAvailable, isSymbioteNode, readPassthroughStyle, reduceProps, resolveHostNode, whenCommitted, } from '@symbiote-native/engine';
|
|
26
|
+
import { withStableKeys } from '../../utils/stable-keys.js';
|
|
27
|
+
// RN's prop carrying already-rasterized values that OVERRIDE the animated prop in the committed
|
|
28
|
+
// props, so the ShadowTree (hit-testing) stays current while the native driver animates.
|
|
29
|
+
const PASSTHROUGH_PROP = 'passthroughAnimatedPropExplicitValues';
|
|
30
|
+
// `Component<any>` is solid-js's own shape for "any component" (it is what ValidComponent is built
|
|
31
|
+
// from). A precise generic cannot work: the bag handed to the base is assembled at RUNTIME from a
|
|
32
|
+
// key set that grows, so no static prop type describes it. The RETURN type stays precise, so a
|
|
33
|
+
// caller of Animated.View still gets a real prop type.
|
|
34
|
+
export function createAnimatedComponent(Base) {
|
|
35
|
+
return function AnimatedComponent(props) {
|
|
36
|
+
// The passthrough is CONSUMED (it only overrides the committed style below); `children` and
|
|
37
|
+
// `ref` are pulled out so the bag accessor depends on neither the child subtree nor the ref.
|
|
38
|
+
const [local, rest] = splitProps(props, [
|
|
39
|
+
'ref',
|
|
40
|
+
'children',
|
|
41
|
+
PASSTHROUGH_PROP,
|
|
42
|
+
]);
|
|
43
|
+
// Held by IDENTITY in a plain variable — never a signal, never a store. The engine's commit
|
|
44
|
+
// mirror is a WeakMap keyed on the raw node, so any proxy wrapper becomes a different key and
|
|
45
|
+
// every native bind silently misses (symbiote-engine-core §3).
|
|
46
|
+
let node = null;
|
|
47
|
+
const lifecycle = createAnimatedLeafLifecycle('solid');
|
|
48
|
+
// NOT the native-driver switch. A useNativeDriver animation promotes the graph itself
|
|
49
|
+
// (animations/base.ts -> value.__startNativeAnimation -> graph.__makeNative) and the leaf
|
|
50
|
+
// follows through __addChild, with or without this. This is only RN's EAGER promotion for the
|
|
51
|
+
// sticky-header passthrough, and it still needs a real native module to mean anything.
|
|
52
|
+
const wantsNative = () => {
|
|
53
|
+
const passthrough = local[PASSTHROUGH_PROP];
|
|
54
|
+
return (passthrough !== undefined &&
|
|
55
|
+
passthrough !== null &&
|
|
56
|
+
isNativeAnimatedAvailable());
|
|
57
|
+
};
|
|
58
|
+
// withStableKeys: reduceProps' key set follows `rest`, which a caller can shrink between runs,
|
|
59
|
+
// and Solid's `spread` has no removal pass — a vanished key would keep its last value on the
|
|
60
|
+
// native view forever (.claude/rules/solid-descriptor-bridge.md §1). The host primitives widen
|
|
61
|
+
// their own bags too, but relying on that would make this wrap correct only by accident.
|
|
62
|
+
const bag = withStableKeys(() => {
|
|
63
|
+
const reduced = reduceProps({ ...rest });
|
|
64
|
+
const passthroughStyle = readPassthroughStyle(local[PASSTHROUGH_PROP]);
|
|
65
|
+
if (passthroughStyle !== undefined) {
|
|
66
|
+
reduced.style =
|
|
67
|
+
reduced.style === undefined
|
|
68
|
+
? passthroughStyle
|
|
69
|
+
: [reduced.style, passthroughStyle];
|
|
70
|
+
}
|
|
71
|
+
return reduced;
|
|
72
|
+
});
|
|
73
|
+
// Capture the base's public instance, resolve it to the underlying host node for the leaf
|
|
74
|
+
// (unwrapping a scroll handle), and forward the ORIGINAL instance to the caller, who expects
|
|
75
|
+
// the component's own handle. Ref shape rationale: utils/host-ref.ts.
|
|
76
|
+
const captureRef = (instance) => {
|
|
77
|
+
node = resolveHostNode(instance);
|
|
78
|
+
if (typeof local.ref === 'function')
|
|
79
|
+
local.ref(instance);
|
|
80
|
+
};
|
|
81
|
+
// createEffect, not createRenderEffect: the node has to exist first. The ref above fills it
|
|
82
|
+
// while the element is built, which is strictly before any effect runs.
|
|
83
|
+
createEffect(() => {
|
|
84
|
+
const current = { ...rest };
|
|
85
|
+
const wants = wantsNative();
|
|
86
|
+
const target = isSymbioteNode(node) ? node : null;
|
|
87
|
+
lifecycle.reconcile(current, target, wants, target === null ? undefined : bind => whenCommitted(target, bind));
|
|
88
|
+
});
|
|
89
|
+
onCleanup(() => {
|
|
90
|
+
lifecycle.teardown();
|
|
91
|
+
});
|
|
92
|
+
// ONE merge, never a spread followed by an explicit prop: mergeProps takes the first
|
|
93
|
+
// NON-undefined value scanning back-to-front, so a later explicit `undefined` would lose
|
|
94
|
+
// (.claude/rules/solid-descriptor-bridge.md §6). Safe here because the second source carries
|
|
95
|
+
// only `ref` and `children`, neither of which is the vanishing kind. The getter keeps children
|
|
96
|
+
// lazy so reading the bag never walks the child subtree.
|
|
97
|
+
const childProps = mergeProps(bag, {
|
|
98
|
+
ref: captureRef,
|
|
99
|
+
get children() {
|
|
100
|
+
return local.children;
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
return <Base {...childProps}/>;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
import { createAnimatedComponent } from './create-animated-component';
|
|
3
|
+
export { createAnimatedComponent } from './create-animated-component';
|
|
4
|
+
export type { IAnimatedComponentProps } from './create-animated-component';
|
|
5
|
+
export { AnimatedProps, AnimatedStyle, AnimatedTransform, } from '@symbiote-native/engine';
|
|
6
|
+
type IAnimatedComponent = ReturnType<typeof createAnimatedComponent>;
|
|
7
|
+
export declare const Animated: {
|
|
8
|
+
Value: typeof AnimatedValue;
|
|
9
|
+
ValueXY: typeof AnimatedValueXY;
|
|
10
|
+
Color: typeof AnimatedColor;
|
|
11
|
+
Easing: {
|
|
12
|
+
step0(n: number): number;
|
|
13
|
+
step1(n: number): number;
|
|
14
|
+
linear(t: number): number;
|
|
15
|
+
ease(t: number): number;
|
|
16
|
+
quad(t: number): number;
|
|
17
|
+
cubic(t: number): number;
|
|
18
|
+
poly(n: number): import("@symbiote-native/engine").IEasingFunction;
|
|
19
|
+
sin(t: number): number;
|
|
20
|
+
circle(t: number): number;
|
|
21
|
+
exp(t: number): number;
|
|
22
|
+
elastic(bounciness?: number): import("@symbiote-native/engine").IEasingFunction;
|
|
23
|
+
back(s?: number): import("@symbiote-native/engine").IEasingFunction;
|
|
24
|
+
bounce(t: number): number;
|
|
25
|
+
bezier(x1: number, y1: number, x2: number, y2: number): import("@symbiote-native/engine").IEasingFunction;
|
|
26
|
+
in(easing: import("@symbiote-native/engine").IEasingFunction): import("@symbiote-native/engine").IEasingFunction;
|
|
27
|
+
out(easing: import("@symbiote-native/engine").IEasingFunction): import("@symbiote-native/engine").IEasingFunction;
|
|
28
|
+
inOut(easing: import("@symbiote-native/engine").IEasingFunction): import("@symbiote-native/engine").IEasingFunction;
|
|
29
|
+
};
|
|
30
|
+
timing: typeof timing;
|
|
31
|
+
spring: typeof spring;
|
|
32
|
+
decay: typeof decay;
|
|
33
|
+
parallel: typeof parallel;
|
|
34
|
+
sequence: typeof sequence;
|
|
35
|
+
stagger: typeof stagger;
|
|
36
|
+
loop: typeof loop;
|
|
37
|
+
delay: typeof delay;
|
|
38
|
+
add: typeof add;
|
|
39
|
+
subtract: typeof subtract;
|
|
40
|
+
multiply: typeof multiply;
|
|
41
|
+
divide: typeof divide;
|
|
42
|
+
modulo: typeof modulo;
|
|
43
|
+
diffClamp: typeof diffClamp;
|
|
44
|
+
event: typeof event;
|
|
45
|
+
forkEvent: typeof forkEvent;
|
|
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
|
+
FlatList: IAnimatedComponent;
|
|
52
|
+
SectionList: IAnimatedComponent;
|
|
53
|
+
createAnimatedComponent: typeof createAnimatedComponent;
|
|
54
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// The Animated namespace for @symbiote-native/solid. createAnimatedComponent applied to this
|
|
2
|
+
// adapter's primitives gives the six animated components RN exposes; the value graph, easing and
|
|
3
|
+
// imperative drivers come from @symbiote-native/engine (framework-agnostic), so both halves meet
|
|
4
|
+
// here in one object and `Animated.timing(new Animated.Value(0), …).start()` reads as it does in
|
|
5
|
+
// RN. Solid's JSX takes the dotted form directly — `<Animated.View/>` compiles to
|
|
6
|
+
// createComponent(Animated.View, …), no local alias needed.
|
|
7
|
+
//
|
|
8
|
+
// The NATIVE DRIVER is not this module's doing and must not be re-derived here. A
|
|
9
|
+
// useNativeDriver animation promotes the value graph itself (animations/base.ts ->
|
|
10
|
+
// value.__startNativeAnimation -> graph.__makeNative), and the props leaf follows automatically
|
|
11
|
+
// through graph.__addChild. This file only assembles the surface.
|
|
12
|
+
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
|
+
import { FlatList } from '../../components/flat-list/index.jsx';
|
|
18
|
+
import { SectionList } from '../../components/section-list/index.jsx';
|
|
19
|
+
import { createAnimatedComponent } from './create-animated-component.jsx';
|
|
20
|
+
export { createAnimatedComponent } from './create-animated-component.jsx';
|
|
21
|
+
// The pure graph leaves live in the engine (they extend AnimatedWithChildren, no framework);
|
|
22
|
+
// re-exported so this adapter's Animated surface matches React's.
|
|
23
|
+
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
|
+
let animatedFlatList;
|
|
30
|
+
let animatedSectionList;
|
|
31
|
+
// The live, JS-driven driver half. RN's AnimatedImplementation.
|
|
32
|
+
const liveDrivers = {
|
|
33
|
+
Value: AnimatedValue,
|
|
34
|
+
ValueXY: AnimatedValueXY,
|
|
35
|
+
Color: AnimatedColor,
|
|
36
|
+
Easing,
|
|
37
|
+
timing,
|
|
38
|
+
spring,
|
|
39
|
+
decay,
|
|
40
|
+
parallel,
|
|
41
|
+
sequence,
|
|
42
|
+
stagger,
|
|
43
|
+
loop,
|
|
44
|
+
delay,
|
|
45
|
+
add,
|
|
46
|
+
subtract,
|
|
47
|
+
multiply,
|
|
48
|
+
divide,
|
|
49
|
+
modulo,
|
|
50
|
+
diffClamp,
|
|
51
|
+
event,
|
|
52
|
+
forkEvent,
|
|
53
|
+
unforkEvent,
|
|
54
|
+
};
|
|
55
|
+
// RN swaps the WHOLE driver namespace for the mock when the host reports isDisableAnimations
|
|
56
|
+
// (reduced motion / test env): same surface, every animation jumping to its final value with no
|
|
57
|
+
// frames. The COMPONENTS stay live in both branches — only this half is swapped, exactly as RN
|
|
58
|
+
// spreads `...Animated` over the same component getters.
|
|
59
|
+
const drivers = Platform.isDisableAnimations ? AnimatedMock : liveDrivers;
|
|
60
|
+
export const Animated = {
|
|
61
|
+
View: AnimatedView,
|
|
62
|
+
Text: AnimatedText,
|
|
63
|
+
Image: AnimatedImage,
|
|
64
|
+
get ScrollView() {
|
|
65
|
+
animatedScrollView ??= createAnimatedComponent(ScrollView);
|
|
66
|
+
return animatedScrollView;
|
|
67
|
+
},
|
|
68
|
+
get FlatList() {
|
|
69
|
+
animatedFlatList ??= createAnimatedComponent(FlatList);
|
|
70
|
+
return animatedFlatList;
|
|
71
|
+
},
|
|
72
|
+
get SectionList() {
|
|
73
|
+
animatedSectionList ??= createAnimatedComponent(SectionList);
|
|
74
|
+
return animatedSectionList;
|
|
75
|
+
},
|
|
76
|
+
createAnimatedComponent,
|
|
77
|
+
...drivers,
|
|
78
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Component } from 'solid-js';
|
|
2
|
+
import { type IAppParameters } from '@symbiote-native/engine';
|
|
3
|
+
export type IComponentProvider = () => Component;
|
|
4
|
+
export type IWrapperComponentProvider = (appParameters: IAppParameters) => Component;
|
|
5
|
+
declare const AppRegistry: import("@symbiote-native/engine").IAppRegistry<IComponentProvider, IWrapperComponentProvider>, setHostRegistrar: (registrar: import("@symbiote-native/engine").IHostRegistrar) => void;
|
|
6
|
+
export { AppRegistry, setHostRegistrar };
|
|
7
|
+
export type { IAppParameters, IRunnable, IHostRegistrar, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from '@symbiote-native/engine';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// AppRegistry: the JS entry point RN apps already use:
|
|
2
|
+
// AppRegistry.registerComponent(appKey, () => App)
|
|
3
|
+
// The registry bookkeeping (sections, host-registrar bridge, headless tasks) is
|
|
4
|
+
// framework-agnostic and lives in @symbiote-native/engine's createAppRegistry, the same one every
|
|
5
|
+
// other adapter uses; this file supplies only the one Solid-specific seam.
|
|
6
|
+
//
|
|
7
|
+
// Unlike the Svelte adapter, `IWrapperComponentProvider` is fully supported here and needs no
|
|
8
|
+
// design pass: a Solid component is an ordinary function value, so wrapping composes exactly the
|
|
9
|
+
// way Vue's `h(wrapper, null, { default: renderRoot })` does — `createComponent(Wrapper, { get
|
|
10
|
+
// children() {…} })`, which is also the literal shape compiled JSX emits for children.
|
|
11
|
+
import { createAppRegistry, dlog, } from '@symbiote-native/engine';
|
|
12
|
+
import { createComponent } from '../../renderer.js';
|
|
13
|
+
import { mount } from '../../render.js';
|
|
14
|
+
function runnableFor(componentProvider, getWrapperComponentProvider) {
|
|
15
|
+
return appParameters => {
|
|
16
|
+
dlog(`AppRegistry: mounting on rootTag ${String(appParameters.rootTag)}`);
|
|
17
|
+
const RootComponent = componentProvider();
|
|
18
|
+
const renderRoot = () => createComponent(RootComponent, appParameters.initialProps ?? {});
|
|
19
|
+
// Read live, so setWrapperComponentProvider affects every runnable's next run rather than only
|
|
20
|
+
// ones registered afterwards — same contract as Vue's.
|
|
21
|
+
const wrapperComponentProvider = getWrapperComponentProvider();
|
|
22
|
+
const mounted = wrapperComponentProvider === undefined
|
|
23
|
+
? renderRoot
|
|
24
|
+
: () => createComponent(wrapperComponentProvider(appParameters), {
|
|
25
|
+
// A getter, not a value: children must stay lazy so the root is created inside the
|
|
26
|
+
// wrapper's own reactive owner (its context providers, error boundaries and cleanups
|
|
27
|
+
// then actually cover the root). Evaluating it eagerly here would build the root
|
|
28
|
+
// outside that owner and quietly break every one of those.
|
|
29
|
+
get children() {
|
|
30
|
+
return renderRoot();
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
mount(appParameters.rootTag, mounted);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const { AppRegistry, setHostRegistrar } = createAppRegistry(runnableFor);
|
|
37
|
+
export { AppRegistry, setHostRegistrar };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type IStatusBarProps } from '@symbiote-native/engine';
|
|
2
|
+
export type { IStatusBarProps, IStatusBarStyle, IStatusBarAnimation, } from '@symbiote-native/engine';
|
|
3
|
+
declare function StatusBarComponent(props: IStatusBarProps): null;
|
|
4
|
+
declare const StatusBarWithStatics: typeof StatusBarComponent & import("@symbiote-native/engine").IStatusBarImperative;
|
|
5
|
+
export declare const StatusBar: typeof StatusBarWithStatics & {
|
|
6
|
+
readonly currentHeight?: number;
|
|
7
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// StatusBar — the Solid lifecycle half, and nothing else. The native StatusBarManager driving
|
|
2
|
+
// (applyStatusBarProps), the imperative statics (statusBarImperative) and the Android bar-height
|
|
3
|
+
// constant all live in @symbiote-native/engine, shared verbatim with React / Vue / Svelte /
|
|
4
|
+
// Angular; Metro selects the engine's index.ios.ts / index.android.ts per host, so the platform
|
|
5
|
+
// divergence never reaches this file. Solid supplies only the declarative shape: a component that
|
|
6
|
+
// renders no Fabric view and re-applies its props from a `createEffect` on mount and on every
|
|
7
|
+
// prop change.
|
|
8
|
+
//
|
|
9
|
+
// FLAT, not a folder: the file-layout rule gives a module its own folder only for real
|
|
10
|
+
// `.ios`/`.android`/`-shared` variants. React needs two files because its useEffect dep array is
|
|
11
|
+
// written by hand per platform; there is no dep array here, so one file covers both hosts —
|
|
12
|
+
// the same shape Svelte's and Angular's StatusBar landed on.
|
|
13
|
+
//
|
|
14
|
+
// Simplification vs RN, inherited from every other adapter: RN keeps a prop-merge stack so nested
|
|
15
|
+
// StatusBars compose (deepest wins); this direct-applies one component's props.
|
|
16
|
+
import { createEffect } from 'solid-js';
|
|
17
|
+
import { applyStatusBarProps, statusBarImperative, statusBarCurrentHeight, } from '@symbiote-native/engine';
|
|
18
|
+
// Renders null: StatusBar owns no host node, it drives a native module.
|
|
19
|
+
function StatusBarComponent(props) {
|
|
20
|
+
createEffect(() => {
|
|
21
|
+
// Every field is read HERE, not inside the engine, so the effect's dependency set is the whole
|
|
22
|
+
// prop surface rather than whichever subset this platform's impl happens to destructure —
|
|
23
|
+
// iOS's applyStatusBarProps never touches backgroundColor/translucent, and a props proxy read
|
|
24
|
+
// only there would silently stop re-applying after a host swap.
|
|
25
|
+
applyStatusBarProps({
|
|
26
|
+
barStyle: props.barStyle,
|
|
27
|
+
hidden: props.hidden,
|
|
28
|
+
animated: props.animated,
|
|
29
|
+
networkActivityIndicatorVisible: props.networkActivityIndicatorVisible,
|
|
30
|
+
backgroundColor: props.backgroundColor,
|
|
31
|
+
translucent: props.translucent,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const StatusBarWithStatics = Object.assign(StatusBarComponent, statusBarImperative);
|
|
37
|
+
// Android exposes the bar height as a native constant; undefined on iOS / when absent. A getter,
|
|
38
|
+
// not a value, so nothing touches native at import time.
|
|
39
|
+
Object.defineProperty(StatusBarWithStatics, 'currentHeight', {
|
|
40
|
+
get: statusBarCurrentHeight,
|
|
41
|
+
enumerable: true,
|
|
42
|
+
});
|
|
43
|
+
// currentHeight is optional, so the defineProperty-added accessor need not appear on the runtime
|
|
44
|
+
// object's inferred type for this assignment to hold (no cast) — same shape as Svelte's.
|
|
45
|
+
export const StatusBar = StatusBarWithStatics;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// createColorScheme — the Solid twin of React's `useColorScheme` hook, Vue's composable, Svelte's
|
|
2
|
+
// rune and Angular's ColorSchemeService, over the framework-agnostic Appearance module
|
|
3
|
+
// (@symbiote-native/engine). The engine owns the native subscription; this file owns only the
|
|
4
|
+
// reactive lifecycle.
|
|
5
|
+
//
|
|
6
|
+
// `primitives/` and `create*`, not `hooks/` and `use*`: Solid's ecosystem calls a composable
|
|
7
|
+
// reactive function a PRIMITIVE (solid-primitives), and reserves `use*` for consuming something
|
|
8
|
+
// that already exists — `useContext`, `useTransition`, and solid-primitives' shared/singleton
|
|
9
|
+
// variants. A primitive that creates its own state and owns a subscription is `createX`.
|
|
10
|
+
// (CLAUDE.md <adapter_src_follows_framework_idioms>: the lifecycle bucket takes the framework's
|
|
11
|
+
// own term, so a Solid `hooks/` folder would be as wrong as a Vue one.)
|
|
12
|
+
//
|
|
13
|
+
// Returns an ACCESSOR, never a snapshot: a Solid component body runs once, so a returned value
|
|
14
|
+
// would freeze at the scheme the app booted with.
|
|
15
|
+
//
|
|
16
|
+
// Outside a component or `createRoot`, `onCleanup` has no owner to attach to — Solid warns
|
|
17
|
+
// ("cleanups created outside a `createRoot` or `render` will never be run") and the Appearance
|
|
18
|
+
// listener lives for the process. The accessor still tracks correctly; only the teardown is lost.
|
|
19
|
+
import { createSignal, onCleanup } from 'solid-js';
|
|
20
|
+
import { Appearance, } from '@symbiote-native/engine';
|
|
21
|
+
export function createColorScheme() {
|
|
22
|
+
const [colorScheme, setColorScheme] = createSignal(Appearance.getColorScheme());
|
|
23
|
+
// No post-subscribe re-read, unlike React/Vue/Svelte: those subscribe from an effect, a tick
|
|
24
|
+
// after the seed read, so a change can slip between the two. Here both statements run in one
|
|
25
|
+
// synchronous tick and nothing can interleave.
|
|
26
|
+
const subscription = Appearance.addChangeListener(preferences => {
|
|
27
|
+
setColorScheme(preferences.colorScheme);
|
|
28
|
+
});
|
|
29
|
+
onCleanup(() => {
|
|
30
|
+
subscription.remove();
|
|
31
|
+
});
|
|
32
|
+
return colorScheme;
|
|
33
|
+
}
|