@symbiote-native/solid 3.2.0 → 3.3.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/components/flat-list/index.d.ts +3 -2
- package/build/components/flat-list/index.jsx +8 -10
- package/build/components/input-accessory-view-props.d.ts +4 -4
- package/build/components/keyboard-avoiding-view.d.ts +2 -2
- package/build/components/keyboard-avoiding-view.jsx +34 -114
- package/build/components/layout-conformance-props.d.ts +6 -0
- package/build/components/layout-conformance-props.js +1 -0
- package/build/components/modal.d.ts +4 -4
- package/build/components/modal.jsx +11 -11
- package/build/components/refresh-control-props.d.ts +7 -7
- package/build/components/scroll-view-props.d.ts +8 -5
- package/build/components/text-input-props.d.ts +2 -1
- package/build/components/text-props.d.ts +3 -3
- package/build/components/touchable-props.d.ts +6 -6
- package/build/components/virtualized-list/index.android.d.ts +2 -2
- package/build/components/virtualized-list/index.ios.d.ts +2 -2
- package/build/components/virtualized-list/item-content.d.ts +6 -0
- package/build/components/virtualized-list/item-content.jsx +9 -0
- package/build/components/virtualized-list/list-body.d.ts +15 -0
- package/build/components/virtualized-list/list-body.jsx +20 -0
- package/build/components/virtualized-list/list-parts.d.ts +14 -0
- package/build/components/virtualized-list/list-parts.js +27 -0
- package/build/components/virtualized-list/list-rows.d.ts +18 -0
- package/build/components/virtualized-list/list-rows.jsx +91 -0
- package/build/components/virtualized-list/list-window.d.ts +11 -0
- package/build/components/virtualized-list/list-window.js +29 -0
- package/build/components/virtualized-list/nested-scope.d.ts +7 -0
- package/build/components/virtualized-list/nested-scope.js +21 -0
- package/build/components/virtualized-list/scroll-handlers.d.ts +12 -0
- package/build/components/virtualized-list/scroll-handlers.js +31 -0
- package/build/components/virtualized-list/scroll-tree.d.ts +18 -0
- package/build/components/virtualized-list/scroll-tree.jsx +143 -0
- package/build/components/virtualized-list/shared.d.ts +3 -74
- package/build/components/virtualized-list/shared.jsx +53 -752
- package/build/components/virtualized-list/sticky-binding.d.ts +13 -0
- package/build/components/virtualized-list/sticky-binding.js +111 -0
- package/build/components/virtualized-list/sticky-header.d.ts +1 -0
- package/build/components/virtualized-list/sticky-header.jsx +11 -159
- package/build/components/virtualized-list/sticky-state.d.ts +14 -0
- package/build/components/virtualized-list/sticky-state.js +54 -0
- package/build/components/virtualized-list/use-list-driver.d.ts +22 -0
- package/build/components/virtualized-list/use-list-driver.js +128 -0
- package/build/components/virtualized-list/use-separators.d.ts +7 -0
- package/build/components/virtualized-list/use-separators.js +22 -0
- package/build/components/virtualized-list/virtualized-list-props.d.ts +86 -0
- package/build/components/virtualized-list/virtualized-list-props.js +53 -0
- package/build/components/virtualized-section-list/index.d.ts +22 -9
- package/build/components/virtualized-section-list/index.jsx +87 -115
- package/build/components/virtualized-section-list/section-item-cell.d.ts +18 -0
- package/build/components/virtualized-section-list/section-item-cell.jsx +37 -0
- package/build/components.d.ts +5 -1
- package/build/components.js +1 -0
- package/build/index.d.ts +9 -4
- package/build/index.js +20 -37
- package/build/modules/animated/index.d.ts +54 -41
- package/build/modules/animated/index.js +2 -26
- package/build/modules/app-registry/index.d.ts +3 -3
- package/build/modules/app-registry/index.js +2 -2
- package/build/modules/status-bar.js +14 -27
- package/build/primitives/create-color-scheme.d.ts +1 -1
- package/package.json +6 -6
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { JSX } from '../../jsx-runtime';
|
|
2
2
|
import { type IStyleProp, type IViewStyle } from '@symbiote-native/engine';
|
|
3
|
-
import { type IVirtualizedListHandle, type IVirtualizedListProps } from '../virtualized-list';
|
|
3
|
+
import { type ICellRendererComponent, type IVirtualizedListHandle, type IVirtualizedListProps } from '../virtualized-list';
|
|
4
4
|
export type IFlatListHandle = IVirtualizedListHandle;
|
|
5
|
-
export type IFlatListProps<ItemT> = Omit<IVirtualizedListProps<ItemT>, 'data' | 'getItem' | 'getItemCount'> & {
|
|
5
|
+
export type IFlatListProps<ItemT> = Omit<IVirtualizedListProps<ItemT>, 'data' | 'getItem' | 'getItemCount' | 'CellRendererComponent'> & {
|
|
6
6
|
data: readonly ItemT[];
|
|
7
|
+
CellRendererComponent?: ICellRendererComponent<unknown>;
|
|
7
8
|
numColumns?: number;
|
|
8
9
|
columnWrapperStyle?: IStyleProp<IViewStyle> | string;
|
|
9
10
|
};
|
|
@@ -25,8 +25,9 @@
|
|
|
25
25
|
// caller receives that list's own handle — FlatList's handle IS VirtualizedList's (RN says the
|
|
26
26
|
// same).
|
|
27
27
|
import { Index, Show, createMemo, splitProps, untrack, } from 'solid-js';
|
|
28
|
-
import { SINGLE_COLUMN, chunkIntoRows, expandRowViewability, firstItemOfRow, lastItemOfRow, removeClippedSubviewsOrDefault, rowKeyExtractor, } from '@symbiote-native/components';
|
|
28
|
+
import { SINGLE_COLUMN, arrayLikeLength, chunkIntoRows, expandRowViewability, firstItemOfRow, lastItemOfRow, removeClippedSubviewsOrDefault, rowKeyExtractor, } from '@symbiote-native/components';
|
|
29
29
|
import { Platform, dlog, resolveClassName, } from '@symbiote-native/engine';
|
|
30
|
+
import { renderItemContent } from '../virtualized-list/item-content.jsx';
|
|
30
31
|
import { VirtualizedList, } from '../virtualized-list';
|
|
31
32
|
// What FlatList itself consumes or re-shapes. Everything NOT listed here is passthrough and rides
|
|
32
33
|
// down onto the inner list untouched — `ref` among it, which is why no delegate handle is needed.
|
|
@@ -35,6 +36,7 @@ const OWN_PROPS = [
|
|
|
35
36
|
'numColumns',
|
|
36
37
|
'columnWrapperStyle',
|
|
37
38
|
'renderItem',
|
|
39
|
+
'ListItemComponent',
|
|
38
40
|
'keyExtractor',
|
|
39
41
|
'ItemSeparatorComponent',
|
|
40
42
|
'onViewableItemsChanged',
|
|
@@ -56,7 +58,7 @@ export function FlatList(props) {
|
|
|
56
58
|
const columns = columnCount();
|
|
57
59
|
// Logged from inside the memo, not from the body: a Solid body runs ONCE, so a line emitted
|
|
58
60
|
// there would report the mount-time shape forever and be actively misleading on a data change.
|
|
59
|
-
dlog(`Solid FlatList over ${props.data
|
|
61
|
+
dlog(`Solid FlatList over ${arrayLikeLength(props.data)} items, ${columns} column(s)`);
|
|
60
62
|
return columns > SINGLE_COLUMN ? chunkIntoRows(props.data, columns) : [];
|
|
61
63
|
});
|
|
62
64
|
// A bare string is a registered class name and has to be resolved here — the row view's style
|
|
@@ -74,7 +76,7 @@ export function FlatList(props) {
|
|
|
74
76
|
// Called ONCE and untracked, the shape VirtualizedList's own cell build uses: a
|
|
75
77
|
// tracked call would put every signal renderItem reads into this column's insert
|
|
76
78
|
// effect and rebuild the subtree instead of updating its leaf.
|
|
77
|
-
const content = untrack(() => props
|
|
79
|
+
const content = untrack(() => renderItemContent(props, () => ({
|
|
78
80
|
item: item(),
|
|
79
81
|
index: info().item.startIndex + column,
|
|
80
82
|
// The row IS the virtualized cell, so every column in it shares the row's
|
|
@@ -108,13 +110,9 @@ export function FlatList(props) {
|
|
|
108
110
|
pair.onViewableItemsChanged(expandRowViewability(rowInfo, props.keyExtractor));
|
|
109
111
|
},
|
|
110
112
|
})));
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
// soundly for an arbitrary ItemT. <Show> memoizes on the flip alone, so a numColumns change
|
|
115
|
-
// rebuilds (RN documents changing it live as unsupported — "change the key prop" — which is the
|
|
116
|
-
// same fresh start) while every other prop change re-props the live list.
|
|
117
|
-
return (<Show when={isMultiColumn()} fallback={<VirtualizedList {...passthrough} removeClippedSubviews={removeClippedSubviews()} 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}/>}>
|
|
113
|
+
// Два списка с разными типами элементов, `ItemT` и `IRow<ItemT>`, одним элементом их не выразить
|
|
114
|
+
// `<Show>` пересобирает список только при смене `numColumns`, остальные props обновляют живой
|
|
115
|
+
return (<Show when={isMultiColumn()} fallback={<VirtualizedList {...passthrough} removeClippedSubviews={removeClippedSubviews()} data={props.data} getItem={(_source, index) => props.data[index]} getItemCount={() => arrayLikeLength(props.data)} renderItem={props.renderItem} ListItemComponent={props.ListItemComponent} keyExtractor={props.keyExtractor} ItemSeparatorComponent={props.ItemSeparatorComponent} onViewableItemsChanged={props.onViewableItemsChanged} viewabilityConfigCallbackPairs={props.viewabilityConfigCallbackPairs}/>}>
|
|
118
116
|
<VirtualizedList {...passthrough} removeClippedSubviews={removeClippedSubviews()} data={rows()} getItem={(_source, index) => rows()[index]} getItemCount={() => rows().length} renderItem={renderRow} keyExtractor={(row) => rowKeyExtractor(row, props.keyExtractor)} ItemSeparatorComponent={rowSeparatorComponent()} onViewableItemsChanged={rowViewability()} viewabilityConfigCallbackPairs={rowViewabilityPairs()}/>
|
|
119
117
|
</Show>);
|
|
120
118
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { JSX } from '../jsx-runtime';
|
|
2
2
|
import type { IAccessibilityProps, IAriaProps } from '@symbiote-native/components';
|
|
3
|
-
import type { IClassNameValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
4
|
-
export
|
|
3
|
+
import type { IClassNameValue, IColorValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
4
|
+
export type IInputAccessoryViewProps = IAccessibilityProps & IAriaProps & {
|
|
5
5
|
nativeID?: string;
|
|
6
|
-
backgroundColor?:
|
|
6
|
+
backgroundColor?: IColorValue;
|
|
7
7
|
style?: IStyleProp<IViewStyle>;
|
|
8
8
|
class?: IClassNameValue;
|
|
9
9
|
children?: JSX.Element;
|
|
10
|
-
}
|
|
10
|
+
};
|
|
@@ -2,7 +2,7 @@ import type { JSX } from '../jsx-runtime';
|
|
|
2
2
|
import { type IAccessibilityProps, type IAriaProps, type IKeyboardAvoidingBehavior } from '@symbiote-native/components';
|
|
3
3
|
import { type IClassNameValue, type IStyleProp, type ISymbioteEvent, type IViewStyle } from '@symbiote-native/engine';
|
|
4
4
|
export type { IKeyboardAvoidingBehavior } from '@symbiote-native/components';
|
|
5
|
-
export
|
|
5
|
+
export type IKeyboardAvoidingViewProps = IAccessibilityProps & IAriaProps & {
|
|
6
6
|
behavior?: IKeyboardAvoidingBehavior;
|
|
7
7
|
enabled?: boolean;
|
|
8
8
|
keyboardVerticalOffset?: number;
|
|
@@ -11,5 +11,5 @@ export interface IKeyboardAvoidingViewProps extends IAccessibilityProps, IAriaPr
|
|
|
11
11
|
class?: IClassNameValue;
|
|
12
12
|
onLayout?: (event: ISymbioteEvent) => void;
|
|
13
13
|
children?: JSX.Element;
|
|
14
|
-
}
|
|
14
|
+
};
|
|
15
15
|
export declare function KeyboardAvoidingView(props: IKeyboardAvoidingViewProps): JSX.Element;
|
|
@@ -1,34 +1,10 @@
|
|
|
1
|
-
// KeyboardAvoidingView
|
|
2
|
-
//
|
|
3
|
-
// readLayoutFrame / computeInset) and the behavior → style/structure decision
|
|
4
|
-
// (resolveKeyboardAvoidingLayout) are framework-agnostic and live in @symbiote-native/components,
|
|
5
|
-
// shared verbatim with React, Vue, Svelte and Angular. Solid supplies only the reactivity: one
|
|
6
|
-
// signal for the inset, three plain variables for the measured frame and the cross-fade setting, a
|
|
7
|
-
// subscribe-at-setup / onCleanup pair over the Keyboard module, and the element assembly around its
|
|
8
|
-
// own children.
|
|
9
|
-
//
|
|
10
|
-
// render-keyboard-avoiding-view.ts deliberately returns a layout DESCRIPTION rather than a
|
|
11
|
-
// Descriptor tree — KAV wraps a live user subtree, which is the case the render-fn boundary rules
|
|
12
|
-
// OUT of core (.claude/rules/component-render-fn-boundary.md). So descriptorToSolid has nothing to
|
|
13
|
-
// do here, exactly as in View and Pressable; the two structural shapes ('nested' vs 'wrapper') are
|
|
14
|
-
// written directly as the two branches at the bottom.
|
|
15
|
-
//
|
|
16
|
-
// NO whenCommitted anywhere: KAV issues no imperative native call. It only reads `onLayout`, and the
|
|
17
|
-
// engine raises the native layout flag itself the moment a listener rides the prop bag — an event
|
|
18
|
-
// cannot arrive before the view exists, so there is no pre-commit window to retry across.
|
|
19
|
-
//
|
|
20
|
-
// NOTHING here destructures `props`. Solid props are getters and a component body runs ONCE; every
|
|
21
|
-
// read below sits inside an accessor, a memo, or an event handler — including
|
|
22
|
-
// `keyboardVerticalOffset`, which is read at EVENT time so a changed offset applies to the next
|
|
23
|
-
// keyboard event without re-subscribing (React re-runs its useEffect for that; here the closure
|
|
24
|
-
// already sees the live value).
|
|
1
|
+
// KeyboardAvoidingView, the Solid lifecycle half over the shared `createKeyboardAvoidingModel`
|
|
2
|
+
// Nothing destructures `props`: they are getters, read at event time or inside an accessor
|
|
25
3
|
import { createMemo, createSignal, onCleanup, splitProps } from 'solid-js';
|
|
26
|
-
import {
|
|
27
|
-
import { Keyboard, Platform,
|
|
4
|
+
import { createKeyboardAvoidingModel, keyboardAvoidingEventNamesFor, readPrefersCrossFadeTransitions, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from '@symbiote-native/components';
|
|
5
|
+
import { Keyboard, Platform, } from '@symbiote-native/engine';
|
|
28
6
|
import { withStableKeys } from '../utils/stable-keys.js';
|
|
29
|
-
// Read
|
|
30
|
-
// testID, class) forwards onto the wrapper View, which folds them once through
|
|
31
|
-
// resolveAccessibilityProps and widens the vanished-key case with withStableKeys.
|
|
7
|
+
// Read here, the rest forwards onto the wrapper which folds aria/role and accessibility* once
|
|
32
8
|
const HANDLED_PROPS = [
|
|
33
9
|
'behavior',
|
|
34
10
|
'enabled',
|
|
@@ -38,106 +14,55 @@ const HANDLED_PROPS = [
|
|
|
38
14
|
'onLayout',
|
|
39
15
|
'children',
|
|
40
16
|
];
|
|
41
|
-
|
|
42
|
-
|
|
17
|
+
// The model with its inset signal and keyboard subscriptions, kept apart so the component reads as
|
|
18
|
+
// the element assembly
|
|
19
|
+
function createKeyboardModel(props) {
|
|
43
20
|
const [inset, setInset] = createSignal(0);
|
|
44
|
-
//
|
|
45
|
-
// NEXT keyboard event's math, which is what re-runs the layout memo. Same shape as React's
|
|
46
|
-
// frameRef/initialHeightRef, Vue's `let frame` and Svelte's non-$state pair.
|
|
47
|
-
let frame;
|
|
48
|
-
let initialHeight;
|
|
49
|
-
// A device accessibility setting, not state: it cannot change mid-session, and nothing should
|
|
50
|
-
// repaint when the promise lands. So a plain `let` rather than a signal — the same reason `frame`
|
|
51
|
-
// above is one. It is read at EVENT time, by which point the microtask has long resolved; a
|
|
52
|
-
// keyboard event that somehow beat it just sees the `false` default, which is the ordinary math.
|
|
53
|
-
//
|
|
54
|
-
// Through the shared reader, not AccessibilityInfo directly: the engine's getter REJECTS on a
|
|
55
|
-
// native error (RN parity), and nothing awaits this call, so a rejection here would surface as an
|
|
56
|
-
// unhandled promise. readPrefersCrossFadeTransitions owns that fallback once for all five adapters
|
|
57
|
-
// and answers false, which is also what a missing module and an Android host answer.
|
|
21
|
+
// A device setting, not state: nothing should repaint when the promise lands
|
|
58
22
|
let prefersCrossFadeTransitions = false;
|
|
59
23
|
void readPrefersCrossFadeTransitions().then(enabled => {
|
|
60
24
|
prefersCrossFadeTransitions = enabled;
|
|
61
25
|
});
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// the keyboard. Core gates the correction on 'height'; the other modes ignore the value.
|
|
73
|
-
previousInset,
|
|
74
|
-
prefersCrossFadeTransitions,
|
|
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);
|
|
79
|
-
dlog(`KeyboardAvoidingView show -> inset ${next}`);
|
|
80
|
-
setInset(next);
|
|
81
|
-
};
|
|
82
|
-
const onHide = () => {
|
|
83
|
-
dlog('KeyboardAvoidingView hide -> inset 0');
|
|
84
|
-
setInset(0);
|
|
85
|
-
};
|
|
86
|
-
// TWO listeners, never three, and their names come from the host (RN KeyboardAvoidingView.js):
|
|
87
|
-
// iOS takes the WILL pair so the view rides up with the keyboard animation instead of snapping
|
|
88
|
-
// into place after it; Android has no will-notifications and keeps the DID pair. The change-frame
|
|
89
|
-
// notification is deliberately absent — RN's own comment says an undocked, split or floating iOS
|
|
90
|
-
// keyboard emits it BEFORE the hide, so listening to it applies a frame captured mid-dismissal.
|
|
91
|
-
//
|
|
92
|
-
// Subscribed in the setup body, which runs once per mount — Solid's counterpart to React's
|
|
93
|
-
// mount-effect and Vue's onMounted. onCleanup runs on dispose (render.ts's teardown disposes the
|
|
94
|
-
// whole root), so an unmount removes exactly the listeners this mount added; leaking them would
|
|
95
|
-
// leave a dead closure calling setInset on a disposed signal on every keystroke.
|
|
26
|
+
const model = createKeyboardAvoidingModel({
|
|
27
|
+
options: () => ({
|
|
28
|
+
behavior: props.behavior,
|
|
29
|
+
enabled: props.enabled !== false,
|
|
30
|
+
keyboardVerticalOffset: props.keyboardVerticalOffset ?? DEFAULT_VERTICAL_OFFSET,
|
|
31
|
+
}),
|
|
32
|
+
setInset,
|
|
33
|
+
prefersCrossFade: () => prefersCrossFadeTransitions,
|
|
34
|
+
});
|
|
35
|
+
// Subscribed in the setup body, which runs once per mount, and removed when the root is disposed
|
|
96
36
|
const events = keyboardAvoidingEventNamesFor(Platform.OS);
|
|
97
37
|
const subscriptions = [
|
|
98
|
-
Keyboard.addListener(events.show,
|
|
99
|
-
Keyboard.addListener(events.hide,
|
|
38
|
+
Keyboard.addListener(events.show, model.keyboardShown),
|
|
39
|
+
Keyboard.addListener(events.hide, model.keyboardHidden),
|
|
100
40
|
];
|
|
101
41
|
onCleanup(() => {
|
|
102
42
|
for (const subscription of subscriptions)
|
|
103
43
|
subscription.remove();
|
|
104
44
|
});
|
|
45
|
+
return { inset, model };
|
|
46
|
+
}
|
|
47
|
+
export function KeyboardAvoidingView(props) {
|
|
48
|
+
const [local, rest] = splitProps(props, HANDLED_PROPS);
|
|
49
|
+
const { inset, model } = createKeyboardModel(props);
|
|
105
50
|
const handleLayout = (event) => {
|
|
106
|
-
|
|
107
|
-
if (measured !== undefined) {
|
|
108
|
-
frame = measured;
|
|
109
|
-
if (initialHeight === undefined)
|
|
110
|
-
initialHeight = measured.height;
|
|
111
|
-
}
|
|
51
|
+
model.laidOut(event.nativeEvent.layout);
|
|
112
52
|
local.onLayout?.(event);
|
|
113
53
|
};
|
|
114
54
|
const layout = createMemo(() => resolveKeyboardAvoidingLayout({
|
|
115
55
|
behavior: local.behavior,
|
|
116
|
-
//
|
|
117
|
-
// is not read at all on that branch, so a disabled KAV does not even subscribe to the signal;
|
|
118
|
-
// re-enabling re-runs this memo through the `local.enabled` getter and picks the value up.
|
|
56
|
+
// `inset()` is not read when disabled, so a disabled view does not subscribe to the signal
|
|
119
57
|
effectiveInset: local.enabled === false ? 0 : inset(),
|
|
120
|
-
initialHeight,
|
|
58
|
+
initialHeight: model.initialHeight(),
|
|
121
59
|
style: local.style,
|
|
122
60
|
contentContainerStyle: local.contentContainerStyle,
|
|
123
61
|
}));
|
|
124
|
-
//
|
|
125
|
-
// the
|
|
126
|
-
// the branch below runs inside the wrapper's `insert` render effect. `layout()` returns a fresh
|
|
127
|
-
// object on every keyboard event, so anything that lets it reach that effect tears the wrapped
|
|
128
|
-
// subtree down and rebuilds it on every show/hide: typically the form the user is typing in, which
|
|
129
|
-
// loses focus mid-keystroke. createMemo's default `===` equality stops the churn at the BOOLEAN —
|
|
130
|
-
// it only notifies when the structure actually flips, i.e. when `behavior` moves in or out of
|
|
131
|
-
// 'position'.
|
|
132
|
-
//
|
|
133
|
-
// babel-preset-solid's `wrapConditionals` (on by default) would memoize the condition of the
|
|
134
|
-
// ternary below on its own, so the inline form happens to be safe too. Naming the memo makes that
|
|
135
|
-
// guarantee local instead of a compiler flag nobody reads, and it survives the refactor that
|
|
136
|
-
// actually breaks it — measured: moving the same branch into a plain `renderContent()` helper (the
|
|
137
|
-
// shape Pressable uses) defeats wrapConditionals and rebuilds the subtree on every keyboard event.
|
|
62
|
+
// Named so the children survive a keystroke: `layout()` is a fresh object per keyboard event,
|
|
63
|
+
// and this boolean only notifies when the structure flips, `insert` would rebuild the subtree
|
|
138
64
|
const isNested = createMemo(() => layout().kind === 'nested');
|
|
139
|
-
// The inset reaches the host as a
|
|
140
|
-
// so nothing is rebuilt.
|
|
65
|
+
// The inset reaches the host as a prop, `spread` diffs it on the same element
|
|
141
66
|
const innerStyle = () => {
|
|
142
67
|
const resolved = layout();
|
|
143
68
|
return resolved.kind === 'nested' ? resolved.innerStyle : undefined;
|
|
@@ -145,16 +70,11 @@ export function KeyboardAvoidingView(props) {
|
|
|
145
70
|
const wrapperProps = createMemo(() => ({
|
|
146
71
|
...rest,
|
|
147
72
|
style: layout().wrapperStyle,
|
|
148
|
-
// Last, so
|
|
73
|
+
// Last, so the model always measures, the caller's handler is invoked from inside
|
|
149
74
|
onLayout: handleLayout,
|
|
150
75
|
}));
|
|
151
|
-
//
|
|
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).
|
|
76
|
+
// `spread` has no removal pass, so a key that stops being emitted would keep its last value
|
|
155
77
|
const stableWrapperProps = withStableKeys(wrapperProps);
|
|
156
|
-
// 'nested' ('position') pushes the children in an inner View by `bottom: inset`; the wrapper modes
|
|
157
|
-
// ('padding' / 'height' / no behavior) adjust the single wrapper directly.
|
|
158
78
|
return (<view {...stableWrapperProps()}>
|
|
159
79
|
{isNested() ? (<view style={innerStyle()}>{local.children}</view>) : (local.children)}
|
|
160
80
|
</view>);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { JSX } from '../jsx-runtime';
|
|
2
2
|
import { type IAccessibilityProps, type IAriaProps, type IModalAnimationType, type IModalOrientation, type IModalPresentationStyle } from '@symbiote-native/components';
|
|
3
|
-
import { type IClassNameValue, type IStyleProp, type ISymbioteEvent, type IViewStyle } from '@symbiote-native/engine';
|
|
3
|
+
import { type IClassNameValue, type IColorValue, type IStyleProp, type ISymbioteEvent, type IViewStyle } from '@symbiote-native/engine';
|
|
4
4
|
export type { IModalAnimationType, IModalPresentationStyle, IModalOrientation, IModalOrientationChangeEvent, } from '@symbiote-native/components';
|
|
5
|
-
export
|
|
5
|
+
export type IModalProps = IAccessibilityProps & IAriaProps & {
|
|
6
6
|
visible?: boolean;
|
|
7
7
|
transparent?: boolean;
|
|
8
|
-
backdropColor?:
|
|
8
|
+
backdropColor?: IColorValue;
|
|
9
9
|
animationType?: IModalAnimationType;
|
|
10
10
|
presentationStyle?: IModalPresentationStyle;
|
|
11
11
|
supportedOrientations?: ReadonlyArray<IModalOrientation>;
|
|
@@ -20,5 +20,5 @@ export interface IModalProps extends IAccessibilityProps, IAriaProps {
|
|
|
20
20
|
style?: IStyleProp<IViewStyle>;
|
|
21
21
|
class?: IClassNameValue;
|
|
22
22
|
children?: JSX.Element;
|
|
23
|
-
}
|
|
23
|
+
};
|
|
24
24
|
export declare function Modal(props: IModalProps): JSX.Element;
|
|
@@ -34,9 +34,10 @@
|
|
|
34
34
|
// SEED, which is a mount-time value by definition (React's useReducer lazy init and Svelte's
|
|
35
35
|
// $state(...) initializer read it exactly once too).
|
|
36
36
|
import { createEffect, createSignal, Show, splitProps } from 'solid-js';
|
|
37
|
-
import { createDescriptorShapeGuard, createInitialModalState, isModalVisible, modalReducer, modalVisibilityAction, renderModal, resolveAccessibilityProps, shouldRenderModal, } from '@symbiote-native/components';
|
|
37
|
+
import { createDescriptorShapeGuard, createInitialModalState, isModalVisible, modalReducer, modalVisibilityAction, renderModal, resolveAccessibilityProps, shouldRenderModal, warnAboutModalProps, } from '@symbiote-native/components';
|
|
38
38
|
import { dlog, Platform, } from '@symbiote-native/engine';
|
|
39
39
|
import { withStableKeys } from '../utils/stable-keys.js';
|
|
40
|
+
import { VirtualizedListScopeResetter } from './virtualized-list/nested-scope.js';
|
|
40
41
|
// Read by Modal itself; everything else (the four DirectEvents, testID/nativeID, every
|
|
41
42
|
// accessibility* and aria-* field) forwards onto the host node through `passthrough`, exactly as
|
|
42
43
|
// React's `...passthrough` rest does.
|
|
@@ -58,6 +59,8 @@ const HANDLED_PROPS = [
|
|
|
58
59
|
];
|
|
59
60
|
const shape = createDescriptorShapeGuard('Modal');
|
|
60
61
|
export function Modal(props) {
|
|
62
|
+
// RN checks on mount and on every update, a dev build only
|
|
63
|
+
createEffect(() => warnAboutModalProps(props));
|
|
61
64
|
const [local, rest] = splitProps(props, HANDLED_PROPS);
|
|
62
65
|
const isVisible = () => isModalVisible(local.visible);
|
|
63
66
|
// The keep-alive seed is a mount-time value by contract (createInitialModalState: a modal that
|
|
@@ -83,11 +86,7 @@ export function Modal(props) {
|
|
|
83
86
|
if (!shouldRender())
|
|
84
87
|
dlog('Modal hidden -> no node committed');
|
|
85
88
|
});
|
|
86
|
-
//
|
|
87
|
-
// at creation, which would paint a descriptor (and emit renderModal's "committing" dlog) for a
|
|
88
|
-
// modal that is hidden and commits nothing. Read from inside the two `spread` render effects
|
|
89
|
-
// instead, it never runs at all while `Show` is closed; the cost is that a change recomputes this
|
|
90
|
-
// pure object literal twice, once per bag.
|
|
89
|
+
// An accessor, not a memo: a memo runs eagerly and would build a descriptor for a hidden modal
|
|
91
90
|
const descriptor = () => renderModal({
|
|
92
91
|
visible: local.visible,
|
|
93
92
|
transparent: local.transparent,
|
|
@@ -113,10 +112,7 @@ export function Modal(props) {
|
|
|
113
112
|
throw shape.error('the container child disappeared');
|
|
114
113
|
return shape.asElement(first);
|
|
115
114
|
};
|
|
116
|
-
//
|
|
117
|
-
// two branches emit DIFFERENT key sets — and Solid's `spread` walks only the CURRENT keys with no
|
|
118
|
-
// removal pass, so a vanished key would keep its last value on the native view forever
|
|
119
|
-
// (.claude/rules/solid-descriptor-bridge.md §1).
|
|
115
|
+
// `resolveAccessibilityProps` emits different key sets per branch and `spread` never removes one
|
|
120
116
|
const hostBag = withStableKeys(() => descriptor().props);
|
|
121
117
|
const containerBag = withStableKeys(() => ({
|
|
122
118
|
...container().props,
|
|
@@ -124,7 +120,11 @@ export function Modal(props) {
|
|
|
124
120
|
}));
|
|
125
121
|
return (<Show when={shouldRender()}>
|
|
126
122
|
<modal {...hostBag()}>
|
|
127
|
-
<view {...containerBag()}>
|
|
123
|
+
<view {...containerBag()}>
|
|
124
|
+
<VirtualizedListScopeResetter>
|
|
125
|
+
{local.children}
|
|
126
|
+
</VirtualizedListScopeResetter>
|
|
127
|
+
</view>
|
|
128
128
|
</modal>
|
|
129
129
|
</Show>);
|
|
130
130
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { JSX } from '../jsx-runtime';
|
|
2
2
|
import type { IAccessibilityProps, IAriaProps } from '@symbiote-native/components';
|
|
3
|
-
import type { IClassNameValue } from '@symbiote-native/engine';
|
|
4
|
-
export
|
|
3
|
+
import type { IClassNameValue, IColorValue } from '@symbiote-native/engine';
|
|
4
|
+
export type IRefreshControlProps = IAccessibilityProps & IAriaProps & {
|
|
5
5
|
refreshing: boolean;
|
|
6
6
|
id?: string;
|
|
7
7
|
onRefresh?: () => void | Promise<void>;
|
|
8
|
-
tintColor?:
|
|
8
|
+
tintColor?: IColorValue;
|
|
9
9
|
title?: string;
|
|
10
|
-
titleColor?:
|
|
10
|
+
titleColor?: IColorValue;
|
|
11
11
|
progressViewOffset?: number;
|
|
12
|
-
colors?: readonly
|
|
13
|
-
progressBackgroundColor?:
|
|
12
|
+
colors?: readonly IColorValue[];
|
|
13
|
+
progressBackgroundColor?: IColorValue;
|
|
14
14
|
size?: 'default' | 'large';
|
|
15
15
|
enabled?: boolean;
|
|
16
16
|
class?: IClassNameValue;
|
|
17
17
|
children?: JSX.Element;
|
|
18
|
-
}
|
|
18
|
+
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Ref } from 'solid-js';
|
|
2
2
|
import type { JSX } from '../jsx-runtime';
|
|
3
3
|
import type { ISymbioteEvent } from '@symbiote-native/engine';
|
|
4
|
-
import type { IAccessibilityProps, IAriaProps } from '@symbiote-native/components';
|
|
5
|
-
import type { IClassNameValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
4
|
+
import type { IAccessibilityProps, IAriaProps, IInnerViewRef, IScrollViewNativeOnlyProps } from '@symbiote-native/components';
|
|
5
|
+
import type { IClassNameValue, IColorValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
6
6
|
import type { IHostInstance } from '../host-instance';
|
|
7
7
|
export type { IScrollViewHandle } from '@symbiote-native/components';
|
|
8
8
|
type IScrollHandler = (event: ISymbioteEvent) => void;
|
|
9
|
-
export
|
|
9
|
+
export type IScrollViewProps = IAccessibilityProps & IAriaProps & IScrollViewNativeOnlyProps & {
|
|
10
10
|
style?: IStyleProp<IViewStyle>;
|
|
11
11
|
class?: IClassNameValue;
|
|
12
12
|
contentContainerStyle?: IStyleProp<IViewStyle> | string;
|
|
@@ -36,8 +36,11 @@ export interface IScrollViewProps extends IAccessibilityProps, IAriaProps {
|
|
|
36
36
|
snapToStart?: boolean;
|
|
37
37
|
snapToEnd?: boolean;
|
|
38
38
|
disableIntervalMomentum?: boolean;
|
|
39
|
+
experimental_endDraggingSensitivityMultiplier?: number;
|
|
39
40
|
stickyHeaderIndices?: number[];
|
|
40
41
|
invertStickyHeaders?: boolean;
|
|
42
|
+
stickyHeaderHiddenOnScroll?: boolean;
|
|
43
|
+
innerViewRef?: IInnerViewRef;
|
|
41
44
|
keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
|
|
42
45
|
keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
|
|
43
46
|
maintainVisibleContentPosition?: {
|
|
@@ -66,7 +69,7 @@ export interface IScrollViewProps extends IAccessibilityProps, IAriaProps {
|
|
|
66
69
|
overScrollMode?: 'auto' | 'always' | 'never';
|
|
67
70
|
fadingEdgeLength?: number;
|
|
68
71
|
persistentScrollbar?: boolean;
|
|
69
|
-
endFillColor?:
|
|
72
|
+
endFillColor?: IColorValue;
|
|
70
73
|
onLayout?: IScrollHandler;
|
|
71
74
|
onScroll?: IScrollHandler;
|
|
72
75
|
onScrollBeginDrag?: IScrollHandler;
|
|
@@ -76,4 +79,4 @@ export interface IScrollViewProps extends IAccessibilityProps, IAriaProps {
|
|
|
76
79
|
onScrollToTop?: IScrollHandler;
|
|
77
80
|
ref?: Ref<IHostInstance>;
|
|
78
81
|
children?: JSX.Element;
|
|
79
|
-
}
|
|
82
|
+
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { Ref } from 'solid-js';
|
|
1
|
+
import type { JSX, Ref } from 'solid-js';
|
|
2
2
|
import type { ITextInputProps as ITextInputBaseProps } from '@symbiote-native/components';
|
|
3
3
|
import type { IClassNameValue } from '@symbiote-native/engine';
|
|
4
4
|
import type { IHostInstance } from '../host-instance';
|
|
5
5
|
export type ITextInputProps = ITextInputBaseProps & {
|
|
6
6
|
class?: IClassNameValue;
|
|
7
7
|
ref?: Ref<IHostInstance>;
|
|
8
|
+
children?: JSX.Element;
|
|
8
9
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Ref } from 'solid-js';
|
|
2
2
|
import type { JSX } from '../jsx-runtime';
|
|
3
|
-
import type { IAccessibilityProps, IAriaProps } from '@symbiote-native/components';
|
|
3
|
+
import type { IAccessibilityProps, IAriaProps, ITextNativeOnlyProps } from '@symbiote-native/components';
|
|
4
4
|
import type { IClassNameValue, IStyleProp, ISymbioteEvent, ITextStyle } from '@symbiote-native/engine';
|
|
5
5
|
import type { IHostInstance } from '../host-instance';
|
|
6
|
-
export
|
|
6
|
+
export type ITextProps = IAccessibilityProps & IAriaProps & ITextNativeOnlyProps & {
|
|
7
7
|
style?: IStyleProp<ITextStyle>;
|
|
8
8
|
class?: IClassNameValue;
|
|
9
9
|
onPress?: (event: ISymbioteEvent) => void;
|
|
@@ -23,4 +23,4 @@ export interface ITextProps extends IAccessibilityProps, IAriaProps {
|
|
|
23
23
|
selectionColor?: string;
|
|
24
24
|
ref?: Ref<IHostInstance>;
|
|
25
25
|
children?: JSX.Element;
|
|
26
|
-
}
|
|
26
|
+
};
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import type { JSX } from '../jsx-runtime';
|
|
2
2
|
import type { IPressTimingProps } from '@symbiote-native/components';
|
|
3
|
-
import type { IClassNameValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
3
|
+
import type { IClassNameValue, IColorValue, IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
4
4
|
import type { IPressableProps } from './pressable-props';
|
|
5
5
|
export type ITouchableBaseProps = Omit<IPressableProps, 'style' | 'class' | 'children'> & IPressTimingProps & {
|
|
6
6
|
style?: IStyleProp<IViewStyle>;
|
|
7
7
|
class?: IClassNameValue;
|
|
8
8
|
children?: JSX.Element;
|
|
9
9
|
};
|
|
10
|
-
export
|
|
10
|
+
export type ITouchableOpacityProps = ITouchableBaseProps & {
|
|
11
11
|
activeOpacity?: number;
|
|
12
|
-
}
|
|
13
|
-
export
|
|
12
|
+
};
|
|
13
|
+
export type ITouchableHighlightProps = ITouchableBaseProps & {
|
|
14
14
|
activeOpacity?: number;
|
|
15
|
-
underlayColor?:
|
|
15
|
+
underlayColor?: IColorValue;
|
|
16
16
|
touchSoundDisabled?: boolean;
|
|
17
17
|
testOnly_pressed?: boolean;
|
|
18
18
|
onShowUnderlay?: () => void;
|
|
19
19
|
onHideUnderlay?: () => void;
|
|
20
|
-
}
|
|
20
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { IVirtualizedListProps, IVirtualizedListComponent, IVirtualizedListCellInfo, IVirtualizedListRenderItem, IVirtualizedListHandle, ICellLayout, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, } from './shared';
|
|
2
|
-
export declare const VirtualizedList: import("./
|
|
1
|
+
export type { IVirtualizedListProps, IVirtualizedListComponent, ICellRendererComponent, ICellRendererProps, IVirtualizedListCellInfo, IVirtualizedListRenderItem, IVirtualizedListHandle, ICellLayout, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, } from './shared';
|
|
2
|
+
export declare const VirtualizedList: import("./virtualized-list-props").IVirtualizedListComponent;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { IVirtualizedListProps, IVirtualizedListComponent, IVirtualizedListCellInfo, IVirtualizedListRenderItem, IVirtualizedListHandle, ICellLayout, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, } from './shared';
|
|
2
|
-
export declare const VirtualizedList: import("./
|
|
1
|
+
export type { IVirtualizedListProps, IVirtualizedListComponent, ICellRendererComponent, ICellRendererProps, IVirtualizedListCellInfo, IVirtualizedListRenderItem, IVirtualizedListHandle, ICellLayout, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, } from './shared';
|
|
2
|
+
export declare const VirtualizedList: import("./virtualized-list-props").IVirtualizedListComponent;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Accessor } from 'solid-js';
|
|
2
|
+
import type { JSX } from '../../jsx-runtime';
|
|
3
|
+
import type { IVirtualizedListCellInfo, IVirtualizedListProps } from './virtualized-list-props';
|
|
4
|
+
type IItemRenderers<ItemT> = Pick<IVirtualizedListProps<ItemT>, 'renderItem' | 'ListItemComponent'>;
|
|
5
|
+
export declare function renderItemContent<ItemT>(renderers: IItemRenderers<ItemT>, info: Accessor<IVirtualizedListCellInfo<ItemT>>): JSX.Element;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// One cell's content: `ListItemComponent` or `renderItem`, RN's `_renderElement`
|
|
2
|
+
import { drawItem } from '@symbiote-native/components';
|
|
3
|
+
// The component takes the info as plain props, reactive getters over the accessor
|
|
4
|
+
export function renderItemContent(renderers, info) {
|
|
5
|
+
return drawItem({
|
|
6
|
+
renderItem: renderers.renderItem,
|
|
7
|
+
component: renderers.ListItemComponent,
|
|
8
|
+
}, info, (Component, given) => (<Component item={given().item} index={given().index} separators={given().separators}/>));
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Accessor } from 'solid-js';
|
|
2
|
+
import type { IStyleProp, IViewStyle } from '@symbiote-native/engine';
|
|
3
|
+
import type { JSX } from '../../jsx-runtime';
|
|
4
|
+
export type IListBodyDeps = {
|
|
5
|
+
header: JSX.Element;
|
|
6
|
+
footer: JSX.Element;
|
|
7
|
+
empty: JSX.Element;
|
|
8
|
+
headerStyle: () => IStyleProp<IViewStyle> | undefined;
|
|
9
|
+
footerStyle: () => IStyleProp<IViewStyle> | undefined;
|
|
10
|
+
emptyStyle: () => IStyleProp<IViewStyle> | undefined;
|
|
11
|
+
isEmpty: () => boolean;
|
|
12
|
+
rowKeys: Accessor<string[]>;
|
|
13
|
+
renderRow: (rowKey: string) => JSX.Element;
|
|
14
|
+
};
|
|
15
|
+
export declare function listBody(deps: IListBodyDeps): JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The children of the content node: header, the keyed rows (or the empty slot), footer
|
|
2
|
+
import { For, Show } from 'solid-js';
|
|
3
|
+
// Every reactive read sits inside a `<Show>` or `<For>` prop getter, so this function has no
|
|
4
|
+
// dependencies and the content view's `insert` effect runs exactly once, a condition moved into a
|
|
5
|
+
// plain helper called from here would rebuild the whole list on the next scroll
|
|
6
|
+
export function listBody(deps) {
|
|
7
|
+
return [
|
|
8
|
+
<Show when={deps.header !== undefined}>
|
|
9
|
+
<view style={deps.headerStyle()}>{deps.header}</view>
|
|
10
|
+
</Show>,
|
|
11
|
+
<Show when={deps.isEmpty()} fallback={<For each={deps.rowKeys()}>{deps.renderRow}</For>}>
|
|
12
|
+
<Show when={deps.empty !== undefined}>
|
|
13
|
+
<view style={deps.emptyStyle()}>{deps.empty}</view>
|
|
14
|
+
</Show>
|
|
15
|
+
</Show>,
|
|
16
|
+
<Show when={deps.footer !== undefined}>
|
|
17
|
+
<view style={deps.footerStyle()}>{deps.footer}</view>
|
|
18
|
+
</Show>,
|
|
19
|
+
];
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type IListNesting } from '@symbiote-native/components';
|
|
2
|
+
import { createListWindow } from './list-window';
|
|
3
|
+
import { type IScrollHandlers } from './scroll-handlers';
|
|
4
|
+
import { createStickyState } from './sticky-state';
|
|
5
|
+
import { type IListDriver } from './use-list-driver';
|
|
6
|
+
import type { IVirtualizedListProps } from './virtualized-list-props';
|
|
7
|
+
export type IListParts<ItemT> = {
|
|
8
|
+
driver: IListDriver<ItemT>;
|
|
9
|
+
nesting: IListNesting<ItemT>;
|
|
10
|
+
sticky: ReturnType<typeof createStickyState>;
|
|
11
|
+
listWindow: ReturnType<typeof createListWindow>;
|
|
12
|
+
handlers: IScrollHandlers;
|
|
13
|
+
};
|
|
14
|
+
export declare function createListParts<ItemT>(props: IVirtualizedListProps<ItemT>): IListParts<ItemT>;
|