@symbiote-native/solid 1.0.0 → 3.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 +159 -86
- package/babel-preset.cjs +0 -5
- package/build/components/activity-indicator-props.d.ts +5 -0
- package/build/components/activity-indicator-props.js +1 -0
- package/build/components/button-props.d.ts +5 -0
- package/build/components/button-props.js +1 -0
- package/build/components/flat-list/index.jsx +4 -4
- package/build/components/{image-background.d.ts → image-background-props.d.ts} +2 -3
- package/build/components/image-background-props.js +1 -0
- package/build/components/image-props.d.ts +5 -0
- package/build/components/image-props.js +6 -0
- package/build/components/{input-accessory-view.d.ts → input-accessory-view-props.d.ts} +1 -2
- package/build/components/input-accessory-view-props.js +5 -0
- package/build/components/keyboard-avoiding-view.jsx +15 -6
- package/build/components/modal.jsx +6 -6
- package/build/components/{pressable.d.ts → pressable-props.d.ts} +10 -7
- package/build/components/pressable-props.js +13 -0
- package/build/components/{refresh-control.d.ts → refresh-control-props.d.ts} +3 -3
- package/build/components/refresh-control-props.js +7 -0
- package/build/components/safe-area-view-props.d.ts +10 -0
- package/build/components/safe-area-view-props.js +5 -0
- package/build/components/{scroll-view/shared.d.ts → scroll-view-props.d.ts} +7 -13
- package/build/components/scroll-view-props.js +29 -0
- package/build/components/switch-props.d.ts +6 -0
- package/build/components/switch-props.js +5 -0
- package/build/components/text-input-props.d.ts +8 -0
- package/build/components/text-input-props.js +1 -0
- package/build/components/{text.d.ts → text-props.d.ts} +3 -3
- package/build/components/text-props.js +11 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +9 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback-props.js +1 -0
- package/build/components/touchable-props.d.ts +20 -0
- package/build/components/touchable-props.js +1 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +7 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.js +1 -0
- package/build/components/{view.d.ts → view-props.d.ts} +2 -3
- package/build/components/view-props.js +11 -0
- package/build/components/virtualized-list/index.android.js +5 -4
- package/build/components/virtualized-list/index.ios.js +6 -6
- package/build/components/virtualized-list/shared.d.ts +1 -3
- package/build/components/virtualized-list/shared.jsx +95 -102
- package/build/components/virtualized-list/sticky-header.d.ts +6 -0
- package/build/components/{scroll-view → virtualized-list}/sticky-header.jsx +28 -36
- package/build/components.d.ts +20 -30
- package/build/components.js +6 -33
- package/build/create-portal/index.jsx +3 -3
- package/build/descriptor-to-solid.js +1 -1
- package/build/host-instance.js +2 -2
- package/build/index.d.ts +1 -0
- package/build/index.js +11 -3
- package/build/jsx-runtime.d.ts +39 -2
- package/build/modules/animated/index.d.ts +26 -23
- package/build/modules/animated/index.js +16 -22
- package/build/modules/image.d.ts +3 -0
- package/build/modules/image.js +12 -0
- package/build/register.d.ts +1 -1
- package/build/register.js +12 -28
- package/build/renderer.js +74 -61
- package/build/utils/stable-keys.d.ts +1 -1
- package/build/utils/stable-keys.js +6 -5
- package/metro-css-parser.cjs +2 -1
- package/package.json +6 -11
- package/babel-lower-host-primitives.cjs +0 -323
- package/build/components/activity-indicator/index.android.d.ts +0 -2
- package/build/components/activity-indicator/index.android.js +0 -11
- package/build/components/activity-indicator/index.d.ts +0 -1
- package/build/components/activity-indicator/index.ios.d.ts +0 -2
- package/build/components/activity-indicator/index.ios.js +0 -10
- package/build/components/activity-indicator/index.js +0 -4
- package/build/components/activity-indicator/shared.d.ts +0 -8
- package/build/components/activity-indicator/shared.js +0 -51
- package/build/components/button.d.ts +0 -7
- package/build/components/button.jsx +0 -45
- package/build/components/image-background.jsx +0 -74
- package/build/components/image.d.ts +0 -9
- package/build/components/image.js +0 -75
- package/build/components/input-accessory-view.jsx +0 -39
- package/build/components/pressable.jsx +0 -211
- package/build/components/refresh-control.jsx +0 -36
- package/build/components/safe-area-view.d.ts +0 -11
- package/build/components/safe-area-view.jsx +0 -24
- package/build/components/scroll-view/index.android.d.ts +0 -2
- package/build/components/scroll-view/index.android.js +0 -11
- package/build/components/scroll-view/index.d.ts +0 -2
- package/build/components/scroll-view/index.ios.d.ts +0 -2
- package/build/components/scroll-view/index.ios.js +0 -8
- package/build/components/scroll-view/index.js +0 -9
- package/build/components/scroll-view/shared.jsx +0 -334
- package/build/components/scroll-view/sticky-header.d.ts +0 -18
- package/build/components/switch/index.android.d.ts +0 -2
- package/build/components/switch/index.android.js +0 -12
- package/build/components/switch/index.d.ts +0 -1
- package/build/components/switch/index.ios.d.ts +0 -2
- package/build/components/switch/index.ios.js +0 -11
- package/build/components/switch/index.js +0 -4
- package/build/components/switch/shared.d.ts +0 -11
- package/build/components/switch/shared.js +0 -87
- package/build/components/text-input.d.ts +0 -10
- package/build/components/text-input.js +0 -290
- package/build/components/text.jsx +0 -27
- package/build/components/touchable/index.d.ts +0 -22
- package/build/components/touchable/index.jsx +0 -247
- package/build/components/touchable-native-feedback/index.d.ts +0 -16
- package/build/components/touchable-native-feedback/index.jsx +0 -68
- package/build/components/view.jsx +0 -49
- package/build/state-style.d.ts +0 -1
- package/build/state-style.js +0 -13
package/README.md
CHANGED
|
@@ -1,76 +1,101 @@
|
|
|
1
1
|
# @symbiote-native/solid
|
|
2
2
|
|
|
3
|
-
SolidJS adapter for SymbioteNative.
|
|
4
|
-
the same `@symbiote-native/engine` every other adapter uses
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
The **SolidJS adapter** for [SymbioteNative](../../README.md) — compiled Solid JSX drives real
|
|
4
|
+
native iOS/Android views through the same `@symbiote-native/engine` every other adapter uses,
|
|
5
|
+
with React's renderer never in the path. It targets `createRenderer` from `solid-js/universal`,
|
|
6
|
+
Solid's own custom-renderer API — no shimming of private internals, no compiler running beside
|
|
7
|
+
Metro, no custom Babel transformer path.
|
|
8
|
+
|
|
9
|
+
<div align="center">
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
> New to SymbioteNative? The [root README](../../README.md) has the architecture.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @symbiote-native/solid react-native solid-js
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`react-native` and `solid-js` stay your app's own top-level dependencies — this package only
|
|
26
|
+
replaces the JS renderer that drives them. `npx @symbiote-native/cli new --framework solid` wires
|
|
27
|
+
the Babel preset, Metro config, and `jsxImportSource` below for a new app; wiring them into an
|
|
28
|
+
existing one still follows [`examples/solid`](../../examples/solid) rather than a generator.
|
|
16
29
|
|
|
17
|
-
Solid
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
Targets **Solid 1.9**, deliberately not 2.0 (RC as of 2026-08) — 2.0 moves the package to
|
|
31
|
+
`@solidjs/universal`, changes `RendererOptions`, and changes the compiled-output shape. Moving to
|
|
32
|
+
it is a rewrite of `src/renderer.ts`, not a range bump.
|
|
20
33
|
|
|
21
|
-
|
|
22
|
-
Vue, Svelte and Angular) — it carries no `"private"` flag, so `changeset publish` and the
|
|
23
|
-
pkg.pr.new canary cover it the same as every other adapter.
|
|
34
|
+
---
|
|
24
35
|
|
|
25
|
-
##
|
|
36
|
+
## Use it
|
|
26
37
|
|
|
27
|
-
Solid
|
|
28
|
-
|
|
29
|
-
`RendererOptions`' ten methods mapped onto the engine's mutation API — no shimming of private
|
|
30
|
-
internals (the Svelte adapter's approach, forced by an unmerged upstream PR), no compiler running
|
|
31
|
-
beside Metro (Angular's AOT), and no custom `babelTransformerPath` at all.
|
|
38
|
+
The app is ordinary Solid — the native primitives are plain lowercase intrinsic tags, no import
|
|
39
|
+
needed:
|
|
32
40
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
`createElement` a staticProps argument) and changed the compiled-output shape (`use()` →
|
|
36
|
-
`applyRef`/`ref`, `effect(fn, init)` → `effect(fn, effectFn)`). Moving to it is a rewrite of
|
|
37
|
-
`src/renderer.ts`, not a range bump.
|
|
41
|
+
```jsx
|
|
42
|
+
import { createSignal } from 'solid-js';
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
export default function App() {
|
|
45
|
+
const [count, setCount] = createSignal(0);
|
|
46
|
+
return (
|
|
47
|
+
<view style={{ padding: 24 }}>
|
|
48
|
+
<text>Taps: {count()}</text>
|
|
49
|
+
<pressable onPress={() => setCount(c => c + 1)}>
|
|
50
|
+
<text>Tap me</text>
|
|
51
|
+
</pressable>
|
|
52
|
+
</view>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
40
56
|
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
The zero-config entry wires the RN-backed host seams and registers the app in one call — this is
|
|
58
|
+
what [`examples/solid`](../../examples/solid) actually uses:
|
|
43
59
|
|
|
44
60
|
```js
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
],
|
|
52
|
-
};
|
|
61
|
+
// index.js
|
|
62
|
+
import { createApp } from '@symbiote-native/solid/bootstrap';
|
|
63
|
+
import App from './App';
|
|
64
|
+
import { name as appName } from './app.json';
|
|
65
|
+
|
|
66
|
+
createApp(App).mount(appName);
|
|
53
67
|
```
|
|
54
68
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
the Svelte example hit (`lifecycle_function_unavailable` on the first mount).
|
|
69
|
+
For anything the defaults don't cover, drive the lower-level seam directly — the same
|
|
70
|
+
`registerRunnable` seam every other adapter uses:
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
```js
|
|
73
|
+
// index.js
|
|
74
|
+
import { AppRegistry } from 'react-native';
|
|
75
|
+
import { mount } from '@symbiote-native/solid';
|
|
76
|
+
import App from './App';
|
|
77
|
+
import { name as appName } from './app.json';
|
|
78
|
+
|
|
79
|
+
AppRegistry.registerRunnable(appName, ({ rootTag }) => {
|
|
80
|
+
mount(rootTag, App);
|
|
81
|
+
});
|
|
82
|
+
```
|
|
63
83
|
|
|
64
|
-
|
|
84
|
+
`babel.config.js` needs the adapter's preset LAST — Babel applies presets in reverse order, so
|
|
85
|
+
listing it last runs it first, claiming the JSX before RN's own React-JSX transform can:
|
|
65
86
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
```js
|
|
88
|
+
module.exports = {
|
|
89
|
+
presets: ['module:@react-native/babel-preset', '@symbiote-native/solid/babel-preset'],
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Primitives are plain intrinsic tags — `<view>`/`<text>`/`<pressable>`, no component involved
|
|
94
|
+
|
|
95
|
+
An app writes the lowercase tags directly (`view`, `text`, `pressable`, …) — `babel-preset-solid`'s
|
|
96
|
+
own `isComponent` check already treats a lowercase name as an element, so JSX compiles straight to
|
|
97
|
+
`createElement`/`setProp` calls against `symbiote-view` etc., no import and nothing to opt into.
|
|
98
|
+
Nothing rewrites the source on the way there, so this adapter carries no Babel plugin of its own.
|
|
74
99
|
|
|
75
100
|
## `./renderer` is a compiler target, not a convenience export
|
|
76
101
|
|
|
@@ -96,46 +121,94 @@ cite the runtime line that forces them.
|
|
|
96
121
|
through those lookups, so hiding a node it inserted itself desyncs its bookkeeping from the tree.
|
|
97
122
|
Anchors are invisible to Fabric, not to traversal.
|
|
98
123
|
|
|
99
|
-
|
|
124
|
+
`tsconfig.json` points `jsxImportSource` at this package — the whole typing setup, no
|
|
125
|
+
`solid-env.d.ts`, no per-file pragma:
|
|
100
126
|
|
|
101
127
|
```jsonc
|
|
102
|
-
|
|
103
|
-
"jsx": "preserve",
|
|
104
|
-
"jsxImportSource": "@symbiote-native/solid"
|
|
128
|
+
{ "jsx": "preserve", "jsxImportSource": "@symbiote-native/solid" }
|
|
105
129
|
```
|
|
106
130
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
131
|
+
Solid's own control-flow `Switch`/`Match` are **not** re-exported from this package — `Switch`
|
|
132
|
+
collides with RN's `Switch` component, which every adapter must export under that name. Import
|
|
133
|
+
the control-flow pair from `solid-js` directly.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Parity — and the one gap
|
|
138
|
+
|
|
139
|
+
Solid reaches the same primitives, runtime modules, `Animated` on both drivers, gestures,
|
|
140
|
+
accessibility, and the `VirtualizedList` family as React, Vue, Svelte, and Angular, verified
|
|
141
|
+
on-device on iOS and Android — the root README's ["How Fast, Against Stock React
|
|
142
|
+
Native"](../../README.md#how-fast-against-stock-react-native) section has the measured numbers.
|
|
143
|
+
That parity is **structural, not hand-copied**: the component logic (state machines +
|
|
144
|
+
render functions) is written **once** in `@symbiote-native/components`, and Solid supplies only
|
|
145
|
+
its lifecycle via `descriptorToSolid`. `Portal` (same-surface content relocation) and
|
|
146
|
+
`createTunnel` (cross-surface) both ship, built over the universal renderer rather than
|
|
147
|
+
`solid-js/web`'s DOM-bound equivalents; `Dynamic` stays absent by design — nothing here needs it.
|
|
148
|
+
|
|
149
|
+
The one deliberate gap, same as every other non-React adapter — **third-party React component
|
|
150
|
+
packages** (`@react-native-community/slider` used directly) run only under the React adapter:
|
|
151
|
+
their body calls React hooks off the React dispatcher, which is null under Solid.
|
|
152
|
+
`@symbiote-native/slider` (this repo's own wrapper) _does_ ship a real Solid build through the
|
|
153
|
+
same `createNode`-by-ViewConfig path Solid uses for its own primitives.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## A Solid-specific gotcha — Fast Refresh, not Metro's export conditions
|
|
158
|
+
|
|
159
|
+
A compiled Solid component is a function with an uppercase name (`function App(props)`) —
|
|
160
|
+
exactly what react-refresh's `isLikelyComponentType` heuristic accepts as a React component.
|
|
161
|
+
Metro's HMR then tries to patch a React Fiber tree that doesn't exist here (no `react-reconciler`
|
|
162
|
+
in this path), and the update is silently swallowed: no error, no visible change.
|
|
163
|
+
`metro.config.js` needs `unstable_forceFullRefreshPatterns: [/\.tsx$/]` to force every `.tsx`
|
|
164
|
+
edit to a full reload instead.
|
|
111
165
|
|
|
112
|
-
|
|
166
|
+
`solid-js`'s export map does have a `node` branch pointing at its SSR build — the same shape that
|
|
167
|
+
broke the Svelte canary — but Metro's own default condition set (`['react-native']`, contributed
|
|
168
|
+
by `@react-native/metro-config`) never includes `node`, so the SSR branch is unreachable and the
|
|
169
|
+
client build resolves correctly with no extra `unstable_conditionNames` config. Adding one would
|
|
170
|
+
not fix anything that's broken and would actively drop `'react-native'` from the condition set
|
|
171
|
+
(`mergeConfig` replaces the array rather than extending it) — leave it alone.
|
|
113
172
|
|
|
114
|
-
|
|
115
|
-
'JSX.IntrinsicElements'` instead of building fine and failing on device. Augmenting solid-js's
|
|
116
|
-
namespace merges into its HTML/SVG list, so `<div>` stayed legal there. The Svelte adapter needed
|
|
117
|
-
a whole preprocessor for the same guarantee.
|
|
118
|
-
- **JSX children are type-checked at all.** solid-js's `JSX.Element` names the DOM's `Node`, which
|
|
119
|
-
does not resolve without a DOM lib — and `skipLibCheck` swallows that error rather than reporting
|
|
120
|
-
it, leaving `Element` as `any` and every JSX child position unchecked. The visible symptom was
|
|
121
|
-
small (a render-prop parameter needed an explicit annotation); the cause was not.
|
|
173
|
+
---
|
|
122
174
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
175
|
+
## Run it
|
|
176
|
+
|
|
177
|
+
[`examples/solid`](../../examples/solid) is a stock React Native 0.86 app — the steps are
|
|
178
|
+
identical to the [React adapter](../react/README.md#run-it), just swap the directory:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
cd examples/solid
|
|
182
|
+
npm install
|
|
183
|
+
bundle install # first time only — installs CocoaPods itself
|
|
184
|
+
bundle exec pod install # fetch native pods
|
|
185
|
+
|
|
186
|
+
# terminal 1 — Metro (DEBUG=1 turns on diagnostic logs)
|
|
187
|
+
DEBUG=1 npm start --reset-cache
|
|
188
|
+
|
|
189
|
+
# terminal 2 — build + launch
|
|
190
|
+
npm run ios # iOS simulator
|
|
191
|
+
npm run android # Android emulator
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Test it
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
pnpm test # vitest, from the workspace root — headless, fake Fabric slot
|
|
200
|
+
```
|
|
128
201
|
|
|
129
|
-
|
|
202
|
+
The canary's own real-device verification is the perf benchmark run (see [Run
|
|
203
|
+
it](#run-it) plus the root README's device numbers), not an automated on-device suite —
|
|
204
|
+
`examples/solid` is the one canary without a Detox `e2e/` setup yet, unlike React/Vue/Svelte/
|
|
205
|
+
Angular. Why headless testing comes for free otherwise: see
|
|
206
|
+
[Testing](../../README.md#testing).
|
|
130
207
|
|
|
131
|
-
|
|
132
|
-
per-surface renderer through — the active surface is module state, set by `mount`. The Svelte adapter
|
|
133
|
-
reached the same single-root conclusion for its own reasons.
|
|
208
|
+
---
|
|
134
209
|
|
|
135
210
|
## Reference
|
|
136
211
|
|
|
137
|
-
- The canary app: `examples/solid`
|
|
138
|
-
- The engine API this targets: the `symbiote-engine-core` skill, `core/engine/src/node.ts`.
|
|
139
|
-
- Building/porting an adapter: the `symbiote-new-adapter` skill.
|
|
212
|
+
- The canary app: [`examples/solid`](../../examples/solid).
|
|
140
213
|
- The nodeOps table this mirrors: `adapters/vue/src/renderer/index.ts`.
|
|
141
214
|
- The same seam on an ANSI target: `wolf-tui/packages/solid/src/renderer/node-ops.ts`.
|
package/babel-preset.cjs
CHANGED
|
@@ -24,11 +24,6 @@
|
|
|
24
24
|
// React-JSX transform gets a chance to claim the same JSX nodes.
|
|
25
25
|
module.exports = function symbioteSolidBabelPreset(_api, options = {}) {
|
|
26
26
|
return {
|
|
27
|
-
// Babel runs PLUGINS before PRESETS, which is exactly the order this needs: the lowering
|
|
28
|
-
// rewrites <View>/<Text> to their intrinsic tags while they are still JSX, so babel-preset-solid
|
|
29
|
-
// then compiles them as elements (createElement + setProp) instead of as components
|
|
30
|
-
// (createComponent + a props Proxy). Reversed, there would be nothing left to rewrite.
|
|
31
|
-
plugins: [require('./babel-lower-host-primitives.cjs')],
|
|
32
27
|
presets: [
|
|
33
28
|
[
|
|
34
29
|
require('babel-preset-solid'),
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IActivityIndicatorProps as IActivityIndicatorBaseProps } from '@symbiote-native/components';
|
|
2
|
+
import type { IClassNameValue } from '@symbiote-native/engine';
|
|
3
|
+
export type IActivityIndicatorProps = IActivityIndicatorBaseProps & {
|
|
4
|
+
class?: IClassNameValue;
|
|
5
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -66,7 +66,7 @@ export function FlatList(props) {
|
|
|
66
66
|
: props.columnWrapperStyle,
|
|
67
67
|
]);
|
|
68
68
|
function renderRow(info) {
|
|
69
|
-
return (<
|
|
69
|
+
return (<view style={rowStyle()}>
|
|
70
70
|
<Index each={info().item.items}>
|
|
71
71
|
{(item, column) => {
|
|
72
72
|
// Called ONCE and untracked, the shape VirtualizedList's own cell build uses: a
|
|
@@ -79,10 +79,10 @@ export function FlatList(props) {
|
|
|
79
79
|
// separators handle — the divider sits between rows, not between columns.
|
|
80
80
|
separators: info().separators,
|
|
81
81
|
})));
|
|
82
|
-
return
|
|
82
|
+
return <view style={COLUMN_STYLE}>{content}</view>;
|
|
83
83
|
}}
|
|
84
84
|
</Index>
|
|
85
|
-
</
|
|
85
|
+
</view>);
|
|
86
86
|
}
|
|
87
87
|
// The divider between rows shows real items (last of the row above, first of the row below), so
|
|
88
88
|
// the caller's separator, typed on ItemT, never sees the IRow wrapper.
|
|
@@ -113,6 +113,6 @@ export function FlatList(props) {
|
|
|
113
113
|
// rebuilds (RN documents changing it live as unsupported — "change the key prop" — which is the
|
|
114
114
|
// same fresh start) while every other prop change re-props the live list.
|
|
115
115
|
return (<Show when={isMultiColumn()} fallback={<VirtualizedList {...passthrough} data={props.data} getItem={(_source, index) => props.data[index]} getItemCount={() => props.data.length} renderItem={props.renderItem} keyExtractor={props.keyExtractor} ItemSeparatorComponent={props.ItemSeparatorComponent} onViewableItemsChanged={props.onViewableItemsChanged} viewabilityConfigCallbackPairs={props.viewabilityConfigCallbackPairs}/>}>
|
|
116
|
-
<VirtualizedList {...passthrough} data={rows()} getItem={(_source, index) => rows()[index]} getItemCount={() => rows().length} renderItem={renderRow} keyExtractor={rowKeyExtractor} ItemSeparatorComponent={rowSeparatorComponent()} onViewableItemsChanged={rowViewability()} viewabilityConfigCallbackPairs={rowViewabilityPairs()}/>
|
|
116
|
+
<VirtualizedList {...passthrough} data={rows()} getItem={(_source, index) => rows()[index]} getItemCount={() => rows().length} renderItem={renderRow} keyExtractor={(row) => rowKeyExtractor(row, props.keyExtractor)} ItemSeparatorComponent={rowSeparatorComponent()} onViewableItemsChanged={rowViewability()} viewabilityConfigCallbackPairs={rowViewabilityPairs()}/>
|
|
117
117
|
</Show>);
|
|
118
118
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
import type { IImageProps } from '@symbiote-native/components';
|
|
2
|
+
import type { IClassNameValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
1
3
|
import type { JSX } from '../jsx-runtime';
|
|
2
|
-
import { type IImageProps } from '@symbiote-native/components';
|
|
3
|
-
import { type IClassNameValue, type IStyleProp, type IViewStyle } from '@symbiote-native/engine';
|
|
4
4
|
export interface IImageBackgroundProps extends Omit<IImageProps, 'style'> {
|
|
5
5
|
style?: IStyleProp<IViewStyle>;
|
|
6
6
|
imageStyle?: IStyleProp<IViewStyle> | string;
|
|
7
7
|
class?: IClassNameValue;
|
|
8
8
|
children?: JSX.Element;
|
|
9
9
|
}
|
|
10
|
-
export declare function ImageBackground(props: IImageBackgroundProps): JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// The `<image>` tag's prop surface. `renderImage`'s whole fold — the source / src / srcSet
|
|
2
|
+
// resolution, the width+height fold into style, resizeMode and tintColor read back off the style,
|
|
3
|
+
// alt -> accessibilityLabel, the array shape native expects — runs in the tag's own behavior
|
|
4
|
+
// (`registerImageBehavior`, wired by `../register`), so the wrapper that used to call it is gone.
|
|
5
|
+
// The Image STATICS moved to `../modules/image`: an imperative API with no view.
|
|
6
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSX } from '../jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import type { IAccessibilityProps, IAriaProps } from '@symbiote-native/components';
|
|
3
3
|
import type { IClassNameValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
4
4
|
export interface IInputAccessoryViewProps extends IAccessibilityProps, IAriaProps {
|
|
5
5
|
nativeID?: string;
|
|
@@ -8,4 +8,3 @@ export interface IInputAccessoryViewProps extends IAccessibilityProps, IAriaProp
|
|
|
8
8
|
class?: IClassNameValue;
|
|
9
9
|
children?: JSX.Element;
|
|
10
10
|
}
|
|
11
|
-
export declare function InputAccessoryView(props: IInputAccessoryViewProps): JSX.Element;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// The `<input-accessory-view>` tag's prop surface. The wrapper that used to carry it is gone —
|
|
2
|
+
// `renderInputAccessoryView`'s nativeID / backgroundColor / style mapping runs in the tag's own
|
|
3
|
+
// behavior (`registerInputAccessoryViewBehavior`, wired by `../register`), and the aria/role fold
|
|
4
|
+
// runs in the engine's `fabricProps`.
|
|
5
|
+
export {};
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
// keyboard event without re-subscribing (React re-runs its useEffect for that; here the closure
|
|
24
24
|
// already sees the live value).
|
|
25
25
|
import { createMemo, createSignal, onCleanup, splitProps } from 'solid-js';
|
|
26
|
-
import { computeInset, keyboardAvoidingEventNamesFor, readKeyboardFrame, readLayoutFrame, readPrefersCrossFadeTransitions, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from '@symbiote-native/components';
|
|
26
|
+
import { computeInset, configureKeyboardAvoidingAnimation, keyboardAvoidingEventNamesFor, readKeyboardAnimationTiming, readKeyboardFrame, readLayoutFrame, readPrefersCrossFadeTransitions, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from '@symbiote-native/components';
|
|
27
27
|
import { Keyboard, Platform, dlog, } from '@symbiote-native/engine';
|
|
28
|
-
import {
|
|
28
|
+
import { withStableKeys } from '../utils/stable-keys.js';
|
|
29
29
|
// Read by KeyboardAvoidingView itself; everything else (the aria/role aliases, accessibility*,
|
|
30
30
|
// testID, class) forwards onto the wrapper View, which folds them once through
|
|
31
31
|
// resolveAccessibilityProps and widens the vanished-key case with withStableKeys.
|
|
@@ -62,6 +62,7 @@ export function KeyboardAvoidingView(props) {
|
|
|
62
62
|
const onShow = (payload) => {
|
|
63
63
|
const keyboard = readKeyboardFrame(payload);
|
|
64
64
|
const offset = local.keyboardVerticalOffset ?? DEFAULT_VERTICAL_OFFSET;
|
|
65
|
+
const previousInset = inset();
|
|
65
66
|
const next = computeInset(frame, keyboard, offset, {
|
|
66
67
|
behavior: local.behavior,
|
|
67
68
|
// The inset CURRENTLY applied (RN's this.state.bottom). Read from the signal at event time so
|
|
@@ -69,9 +70,12 @@ export function KeyboardAvoidingView(props) {
|
|
|
69
70
|
// frame shorter by exactly that much and core adds it back to cancel the shrink. Without it
|
|
70
71
|
// each further keyboard event computes a smaller overlap and the view walks back down under
|
|
71
72
|
// the keyboard. Core gates the correction on 'height'; the other modes ignore the value.
|
|
72
|
-
previousInset
|
|
73
|
+
previousInset,
|
|
73
74
|
prefersCrossFadeTransitions,
|
|
74
75
|
});
|
|
76
|
+
// RN's `_updateBottomIfNecessary` skips the animation when the inset did not change.
|
|
77
|
+
if (next !== previousInset)
|
|
78
|
+
configureKeyboardAvoidingAnimation(readKeyboardAnimationTiming(payload), local.enabled !== false);
|
|
75
79
|
dlog(`KeyboardAvoidingView show -> inset ${next}`);
|
|
76
80
|
setInset(next);
|
|
77
81
|
};
|
|
@@ -144,9 +148,14 @@ export function KeyboardAvoidingView(props) {
|
|
|
144
148
|
// Last, so KAV's own measure always runs; the caller's handler is invoked from inside it.
|
|
145
149
|
onLayout: handleLayout,
|
|
146
150
|
}));
|
|
151
|
+
// The half `View` used to supply, now that the wrapper is a bare tag: `spread` has no removal
|
|
152
|
+
// pass, so a key that stops being emitted (`rest` is a splitProps proxy, and `wrapperStyle` is
|
|
153
|
+
// absent in some behaviors) would keep its last value on the native view forever
|
|
154
|
+
// (.claude/rules/solid-descriptor-bridge.md §1).
|
|
155
|
+
const stableWrapperProps = withStableKeys(wrapperProps);
|
|
147
156
|
// 'nested' ('position') pushes the children in an inner View by `bottom: inset`; the wrapper modes
|
|
148
157
|
// ('padding' / 'height' / no behavior) adjust the single wrapper directly.
|
|
149
|
-
return (<
|
|
150
|
-
{isNested() ? (<
|
|
151
|
-
</
|
|
158
|
+
return (<view {...stableWrapperProps()}>
|
|
159
|
+
{isNested() ? (<view style={innerStyle()}>{local.children}</view>) : (local.children)}
|
|
160
|
+
</view>);
|
|
152
161
|
}
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
// user's components, which is the case the render-fn boundary rules OUT of core
|
|
19
19
|
// (.claude/rules/component-render-fn-boundary.md), so the bridge would have nothing to do with the
|
|
20
20
|
// half that matters and would hand back a root whose container child we would then have to reach
|
|
21
|
-
// into to `insert` into. renderModal always paints the SAME two-tag shape (one
|
|
22
|
-
// wrapping one collapsable
|
|
21
|
+
// into to `insert` into. renderModal always paints the SAME two-tag shape (one modal host
|
|
22
|
+
// wrapping one collapsable view container; only prop VALUES vary), so the tags are written
|
|
23
23
|
// out and the computed props read off the fixed positions — the same call React's createElement
|
|
24
24
|
// chain, Vue's h() chain and Svelte's markup all make. `children` then reaches the container
|
|
25
25
|
// through the compiler's own `insert`, which is the point of view.tsx's header.
|
|
@@ -100,7 +100,7 @@ export function Modal(props) {
|
|
|
100
100
|
style: local.style,
|
|
101
101
|
passthrough: resolveAccessibilityProps(rest),
|
|
102
102
|
});
|
|
103
|
-
// root =
|
|
103
|
+
// root = modal > [container]; the user children nest UNDER the container View, never as
|
|
104
104
|
// a direct sibling of the host (RN's modal content layout, render-modal.ts).
|
|
105
105
|
const container = () => {
|
|
106
106
|
const [first] = descriptor().children;
|
|
@@ -118,8 +118,8 @@ export function Modal(props) {
|
|
|
118
118
|
class: local.class,
|
|
119
119
|
}));
|
|
120
120
|
return (<Show when={shouldRender()}>
|
|
121
|
-
<
|
|
122
|
-
<
|
|
123
|
-
</
|
|
121
|
+
<modal {...hostBag()}>
|
|
122
|
+
<view {...containerBag()}>{local.children}</view>
|
|
123
|
+
</modal>
|
|
124
124
|
</Show>);
|
|
125
125
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Ref } from 'solid-js';
|
|
2
2
|
import type { JSX } from '../jsx-runtime';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import type { IAccessibilityProps, IAriaProps, IPressHandler, IPressState, IPressableAndroidRippleConfig, IRectOffset } from '@symbiote-native/components';
|
|
4
|
+
import type { IClassNameValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
5
|
+
import type { IHostInstance } from '../host-instance';
|
|
5
6
|
export type { IPressState, IPressableAndroidRippleConfig, } from '@symbiote-native/components';
|
|
6
7
|
type IPressableStyle = IStyleProp<IViewStyle> | ((state: IPressState) => IStyleProp<IViewStyle>);
|
|
7
|
-
type IPressableChildren = JSX.Element | ((state: Accessor<IPressState>) => JSX.Element);
|
|
8
8
|
export interface IPressableProps extends IAccessibilityProps, IAriaProps {
|
|
9
9
|
onPress?: IPressHandler;
|
|
10
10
|
onPressIn?: IPressHandler;
|
|
@@ -13,10 +13,14 @@ export interface IPressableProps extends IAccessibilityProps, IAriaProps {
|
|
|
13
13
|
onLongPress?: IPressHandler;
|
|
14
14
|
delayLongPress?: number;
|
|
15
15
|
disabled?: boolean;
|
|
16
|
+
focusable?: boolean;
|
|
16
17
|
cancelable?: boolean;
|
|
18
|
+
blockNativeResponder?: boolean;
|
|
17
19
|
hitSlop?: IRectOffset;
|
|
20
|
+
testOnly_pressed?: boolean;
|
|
18
21
|
pressRetentionOffset?: IRectOffset;
|
|
19
22
|
unstable_pressDelay?: number;
|
|
23
|
+
minPressDuration?: number;
|
|
20
24
|
android_ripple?: IPressableAndroidRippleConfig;
|
|
21
25
|
android_disableSound?: boolean;
|
|
22
26
|
onHoverIn?: IPressHandler;
|
|
@@ -25,7 +29,6 @@ export interface IPressableProps extends IAccessibilityProps, IAriaProps {
|
|
|
25
29
|
delayHoverOut?: number;
|
|
26
30
|
style?: IPressableStyle;
|
|
27
31
|
class?: IClassNameValue;
|
|
28
|
-
|
|
32
|
+
ref?: Ref<IHostInstance>;
|
|
33
|
+
children?: JSX.Element;
|
|
29
34
|
}
|
|
30
|
-
export declare function Pressable(props: IPressableProps): JSX.Element;
|
|
31
|
-
export declare function TouchablePressable(props: IPressableProps): JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// The `<pressable>` tag's prop surface. The press machine — timers, the retention region, the
|
|
2
|
+
// long-press and hover legs, the `disabled`/`focusable`/`accessible` folds and the Android ripple —
|
|
3
|
+
// lives on the engine node as `core/components/src/behaviors/pressable.ts`, wired by
|
|
4
|
+
// `../register`.
|
|
5
|
+
//
|
|
6
|
+
// THERE IS NO RENDER-PROP CHILD, and that is the one surface a tag cannot carry — same fact as
|
|
7
|
+
// Vue's scoped slot (`adapters/vue/src/components/pressable-props.ts`), Solid's own version of it.
|
|
8
|
+
// Press state lives on the engine node and never crosses back into Solid's reactivity as an
|
|
9
|
+
// accessor, so `{state => …}` has no channel any more. A `style` CALLBACK still works — the engine
|
|
10
|
+
// resolves it at both values of `pressed` (`isStyleCallback`, `core/engine/src/node.ts`) — and a
|
|
11
|
+
// `:active` CSS rule is cheaper still. A CHILD that needs the state has to be given it: mirror
|
|
12
|
+
// `onPressIn`/`onPressOut` into a local signal, which is what any app styling a descendant does.
|
|
13
|
+
export {};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { JSX } from '../jsx-runtime';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import type { IAccessibilityProps, IAriaProps } from '@symbiote-native/components';
|
|
3
|
+
import type { IClassNameValue } from '@symbiote-native/engine';
|
|
4
4
|
export interface IRefreshControlProps extends IAccessibilityProps, IAriaProps {
|
|
5
5
|
refreshing: boolean;
|
|
6
|
+
id?: string;
|
|
6
7
|
onRefresh?: () => void | Promise<void>;
|
|
7
8
|
tintColor?: string;
|
|
8
9
|
title?: string;
|
|
@@ -15,4 +16,3 @@ export interface IRefreshControlProps extends IAccessibilityProps, IAriaProps {
|
|
|
15
16
|
class?: IClassNameValue;
|
|
16
17
|
children?: JSX.Element;
|
|
17
18
|
}
|
|
18
|
-
export declare function RefreshControl(props: IRefreshControlProps): JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// The `<refresh-control>` tag's prop surface. On iOS the tag resolves to Fabric's PullToRefreshView,
|
|
2
|
+
// which lives INSIDE a scroll view as a childless sibling before the content container; on Android it
|
|
3
|
+
// is AndroidSwipeRefreshLayout and WRAPS the scroll view instead. Which shape is built is the scroll
|
|
4
|
+
// view's job, and the controlled-spinner handshake — mirror what native reported, command the value
|
|
5
|
+
// back down when the app's `refreshing` disagrees — is the tag's own behavior
|
|
6
|
+
// (`registerRefreshControlBehavior`, wired by `../register`).
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { JSX } from '../jsx-runtime';
|
|
2
|
+
import type { IAccessibilityProps, IAriaProps } from '@symbiote-native/components';
|
|
3
|
+
import type { IClassNameValue, IStyleProp, ISymbioteEvent, IViewStyle } from '@symbiote-native/engine';
|
|
4
|
+
export interface ISafeAreaViewProps extends IAccessibilityProps, IAriaProps {
|
|
5
|
+
style?: IStyleProp<IViewStyle>;
|
|
6
|
+
id?: string;
|
|
7
|
+
class?: IClassNameValue;
|
|
8
|
+
onLayout?: (event: ISymbioteEvent) => void;
|
|
9
|
+
children?: JSX.Element;
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// The `<safe-area-view>` tag's prop surface. The wrapper forwarded a bag and nothing else — the
|
|
2
|
+
// host does all the inset math, there is no JS-side layout anywhere in this project, and the
|
|
3
|
+
// `id -> nativeID` alias plus the aria/role fold both run below the adapter (`foldHostBag` and the
|
|
4
|
+
// engine's `fabricProps`). So there was nothing left for a component to hold.
|
|
5
|
+
export {};
|