@symbiote-native/react 1.0.0 → 2.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.
Files changed (100) hide show
  1. package/README.md +12 -11
  2. package/build/components/activity-indicator-props.d.ts +4 -0
  3. package/build/components/{button.d.ts → button-props.d.ts} +0 -2
  4. package/build/components/button-props.js +1 -0
  5. package/build/components/flat-list/index.js +2 -2
  6. package/build/components/image/image-props.d.ts +4 -0
  7. package/build/components/image/image-props.js +1 -0
  8. package/build/components/image-background-props.d.ts +9 -0
  9. package/build/components/image-background-props.js +1 -0
  10. package/build/components/input-accessory-view-props.d.ts +10 -0
  11. package/build/components/input-accessory-view-props.js +1 -0
  12. package/build/components/keyboard-avoiding-view/index.js +4 -4
  13. package/build/components/modal/index.js +3 -3
  14. package/build/components/pressable/{index.d.ts → pressable-props.d.ts} +4 -5
  15. package/build/components/pressable/pressable-props.js +1 -0
  16. package/build/components/{refresh-control/index.d.ts → refresh-control-props.d.ts} +3 -3
  17. package/build/components/refresh-control-props.js +1 -0
  18. package/build/components/safe-area-view-props.d.ts +11 -0
  19. package/build/components/safe-area-view-props.js +1 -0
  20. package/build/components/scroll-view/{shared.d.ts → scroll-view-props.d.ts} +4 -26
  21. package/build/components/scroll-view/scroll-view-props.js +1 -0
  22. package/build/components/switch/switch-props.d.ts +4 -0
  23. package/build/components/switch/switch-props.js +1 -0
  24. package/build/components/text-input/text-input-props.d.ts +4 -0
  25. package/build/components/text-input/text-input-props.js +1 -0
  26. package/build/components/touchable/touchable-props.d.ts +17 -0
  27. package/build/components/touchable/touchable-props.js +1 -0
  28. package/build/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +8 -0
  29. package/build/components/touchable-native-feedback/touchable-native-feedback-props.js +1 -0
  30. package/build/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +6 -0
  31. package/build/components/touchable-without-feedback/touchable-without-feedback-props.js +1 -0
  32. package/build/components/virtualized-list/index.js +83 -38
  33. package/build/components.d.ts +1 -2
  34. package/build/components.js +5 -19
  35. package/build/create-portal/index.js +1 -1
  36. package/build/create-tunnel/index.js +1 -1
  37. package/build/descriptor-to-react/index.js +1 -1
  38. package/build/host-config.js +22 -4
  39. package/build/index.d.ts +23 -28
  40. package/build/index.js +14 -14
  41. package/build/jsx-dev-runtime.d.ts +3 -0
  42. package/build/jsx-dev-runtime.js +10 -0
  43. package/build/jsx-namespace.type-check.d.ts +1 -0
  44. package/build/jsx-namespace.type-check.js +7 -0
  45. package/build/jsx-runtime.d.ts +62 -0
  46. package/build/jsx-runtime.js +1 -0
  47. package/build/modules/animated/index.d.ts +0 -1
  48. package/build/modules/animated/index.js +16 -22
  49. package/build/modules/image/index.d.ts +3 -0
  50. package/build/modules/image/index.js +14 -0
  51. package/build/register.d.ts +1 -0
  52. package/build/register.js +15 -0
  53. package/metro-css-parser.cjs +2 -1
  54. package/package.json +12 -8
  55. package/build/components/activity-indicator/index.android.d.ts +0 -3
  56. package/build/components/activity-indicator/index.android.js +0 -17
  57. package/build/components/activity-indicator/index.d.ts +0 -1
  58. package/build/components/activity-indicator/index.ios.d.ts +0 -3
  59. package/build/components/activity-indicator/index.ios.js +0 -16
  60. package/build/components/activity-indicator/index.js +0 -4
  61. package/build/components/activity-indicator/shared.d.ts +0 -6
  62. package/build/components/activity-indicator/shared.js +0 -22
  63. package/build/components/button.js +0 -42
  64. package/build/components/image/index.d.ts +0 -9
  65. package/build/components/image/index.js +0 -28
  66. package/build/components/image-background/index.d.ts +0 -10
  67. package/build/components/image-background/index.js +0 -46
  68. package/build/components/input-accessory-view/index.d.ts +0 -11
  69. package/build/components/input-accessory-view/index.js +0 -19
  70. package/build/components/pressable/index.js +0 -112
  71. package/build/components/refresh-control/index.js +0 -21
  72. package/build/components/safe-area-view/index.d.ts +0 -12
  73. package/build/components/safe-area-view/index.js +0 -18
  74. package/build/components/scroll-view/index.android.d.ts +0 -3
  75. package/build/components/scroll-view/index.android.js +0 -61
  76. package/build/components/scroll-view/index.d.ts +0 -1
  77. package/build/components/scroll-view/index.ios.d.ts +0 -3
  78. package/build/components/scroll-view/index.ios.js +0 -29
  79. package/build/components/scroll-view/index.js +0 -4
  80. package/build/components/scroll-view/shared.js +0 -240
  81. package/build/components/scroll-view/sticky-header.d.ts +0 -18
  82. package/build/components/scroll-view/sticky-header.js +0 -227
  83. package/build/components/switch/index.android.d.ts +0 -3
  84. package/build/components/switch/index.android.js +0 -18
  85. package/build/components/switch/index.d.ts +0 -1
  86. package/build/components/switch/index.ios.d.ts +0 -3
  87. package/build/components/switch/index.ios.js +0 -16
  88. package/build/components/switch/index.js +0 -6
  89. package/build/components/switch/shared.d.ts +0 -9
  90. package/build/components/switch/shared.js +0 -60
  91. package/build/components/text-input/index.d.ts +0 -50
  92. package/build/components/text-input/index.js +0 -180
  93. package/build/components/touchable/index.d.ts +0 -22
  94. package/build/components/touchable/index.js +0 -165
  95. package/build/components/touchable-native-feedback/index.d.ts +0 -18
  96. package/build/components/touchable-native-feedback/index.js +0 -32
  97. package/build/jsx.d.ts +0 -18
  98. package/build/state-style.d.ts +0 -1
  99. package/build/state-style.js +0 -13
  100. /package/build/{jsx.js → components/activity-indicator-props.js} +0 -0
@@ -1,50 +0,0 @@
1
- import { type ISymbioteEvent } from '@symbiote-native/engine';
2
- import type { ITextInputProps as ITextInputBaseProps, ITextInputHandle } from '@symbiote-native/components';
3
- export type { ITextInputHandle } from '@symbiote-native/components';
4
- export type ITextInputProps = ITextInputBaseProps & {
5
- className?: string;
6
- };
7
- export declare const TextInput: import("react").ForwardRefExoticComponent<import("@symbiote-native/components").IAccessibilityProps & import("@symbiote-native/components").IAriaProps & {
8
- value?: string;
9
- defaultValue?: string;
10
- placeholder?: string;
11
- placeholderTextColor?: string;
12
- editable?: boolean;
13
- keyboardType?: string;
14
- secureTextEntry?: boolean;
15
- maxLength?: number;
16
- multiline?: boolean;
17
- selection?: import("@symbiote-native/components").ITextInputSelection;
18
- autoCapitalize?: "none" | "sentences" | "words" | "characters";
19
- autoCorrect?: boolean;
20
- autoComplete?: string;
21
- textContentType?: string;
22
- autoFocus?: boolean;
23
- showSoftInputOnFocus?: boolean;
24
- returnKeyType?: string;
25
- selectTextOnFocus?: boolean;
26
- scrollEnabled?: boolean;
27
- numberOfLines?: number;
28
- textAlign?: "left" | "center" | "right";
29
- blurOnSubmit?: boolean;
30
- inputMode?: import("@symbiote-native/components").IInputMode;
31
- enterKeyHint?: import("@symbiote-native/components").IEnterKeyHint;
32
- readOnly?: boolean;
33
- submitBehavior?: import("@symbiote-native/components").ISubmitBehavior;
34
- cursorColor?: string;
35
- selectionColor?: string;
36
- selectionHandleColor?: string;
37
- underlineColorAndroid?: string;
38
- inputAccessoryViewID?: string;
39
- style?: import("@symbiote-native/engine").ITextStyle;
40
- onValueChange?: (text: string, event: ISymbioteEvent) => void;
41
- onFocus?: import("@symbiote-native/components").ITextInputEventHandler;
42
- onBlur?: import("@symbiote-native/components").ITextInputEventHandler;
43
- onEndEditing?: import("@symbiote-native/components").ITextInputEventHandler;
44
- onSubmitEditing?: import("@symbiote-native/components").ITextInputEventHandler;
45
- onKeyPress?: import("@symbiote-native/components").ITextInputEventHandler;
46
- onSelectionChange?: import("@symbiote-native/components").ITextInputEventHandler;
47
- onContentSizeChange?: import("@symbiote-native/components").ITextInputEventHandler;
48
- } & {
49
- className?: string;
50
- } & import("react").RefAttributes<ITextInputHandle>>;
@@ -1,180 +0,0 @@
1
- // TextInput: the React lifecycle half. The folds/maps (value->text, the W3C/alias resolution)
2
- // and the controlled-write predicate live in @symbiote-native/components/state; the render (intrinsic
3
- // + native-prop mapping) in @symbiote-native/components/view; both shared verbatim with the Vue
4
- // adapter. Here React supplies only the lifecycle: useState for the acknowledged event count
5
- // (it must re-render so the imperative handle echoes the latest), a ref for the host node and
6
- // the last text native holds, the useLayoutEffect controlled-write, the mount autoFocus, and
7
- // useImperativeHandle for focus/blur/clear/isFocused/setSelection.
8
- //
9
- // The controlled handshake hinges on commanding native back with the ACKNOWLEDGED event count
10
- // (setTextAndSelection), never a plain prop re-push. See @symbiote-native/components/state/text-input.
11
- import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState, } from 'react';
12
- import { dispatchViewCommand, dlog, } from '@symbiote-native/engine';
13
- import { blurTextInput, setInputBlurred, setInputFocused, } from '@symbiote-native/engine';
14
- import { resolveAccessibilityProps, resolveTextInputProps, renderTextInput, foldText, textFromChange, eventCountFromChange, shouldCommandText, INITIAL_EVENT_COUNT, SELECTION_NONE, } from '@symbiote-native/components';
15
- import { descriptorToReact } from '../../descriptor-to-react/index.js';
16
- export const TextInput = forwardRef((rawProps, forwardedRef) => {
17
- // TextInput is its own host element (not a View wrapper), so it folds aria/role here.
18
- const props = resolveAccessibilityProps(rawProps);
19
- // Pull out the fields the lifecycle owns (controlled value, the wrapped handlers, the folded
20
- // aliases) so they don't reach Fabric raw; everything else rides down via `passthrough`.
21
- const { value, defaultValue, multiline, selection, onValueChange, onFocus, onBlur, inputMode, enterKeyHint, readOnly, submitBehavior, blurOnSubmit, cursorColor, selectionColor, selectionHandleColor, keyboardType, returnKeyType, editable, autoComplete, textContentType, autoFocus, showSoftInputOnFocus, underlineColorAndroid, ...passthrough } = props;
22
- const isMultiline = multiline === true;
23
- const folded = resolveTextInputProps({
24
- inputMode,
25
- keyboardType,
26
- enterKeyHint,
27
- returnKeyType,
28
- readOnly,
29
- editable,
30
- submitBehavior,
31
- blurOnSubmit,
32
- multiline: isMultiline,
33
- cursorColor,
34
- selectionColor,
35
- selectionHandleColor,
36
- autoComplete,
37
- textContentType,
38
- showSoftInputOnFocus,
39
- underlineColorAndroid,
40
- });
41
- const ref = useRef(null);
42
- // JS-side focus state, mirrored from the focus/blur events for isFocused(). RN's
43
- // TextInputState holds the same; native exposes no synchronous focus getter.
44
- const focused = useRef(false);
45
- // The count native last acknowledged. We echo it back on every controlled write so native's
46
- // eventLag lands on 0 and the write applies.
47
- const [mostRecentEventCount, setMostRecentEventCount] = useState(INITIAL_EVENT_COUNT);
48
- // The last text native holds, as far as JS knows. Seeded from the mount-time value because the
49
- // `text` prop already carries that value down via createNode, so the FIRST controlled value is
50
- // not a divergence and must NOT re-command. Only later, post-keystroke divergences flow through
51
- // setTextAndSelection.
52
- const lastNativeText = useRef(foldText(value, defaultValue));
53
- const handleChange = useCallback((event) => {
54
- // Event seam: the controlled handshake hinges on the change payload carrying `text`
55
- // (+ `eventCount`). iOS and Android Fabric can key these differently, so log the actual
56
- // shape here; a missing `text` means onValueChange never fires.
57
- dlog(`TextInput change keys=[${Object.keys(event.nativeEvent).join(',')}] ` +
58
- `text=${JSON.stringify(event.nativeEvent.text)} count=${JSON.stringify(event.nativeEvent.eventCount)}`);
59
- const text = textFromChange(event);
60
- if (text !== undefined) {
61
- lastNativeText.current = text;
62
- onValueChange?.(text, event);
63
- }
64
- // Ordering matters: record the text first, then bump the acknowledged count, so the count
65
- // never runs ahead of the text it stands for.
66
- const count = eventCountFromChange(event);
67
- if (count !== undefined)
68
- setMostRecentEventCount(count);
69
- }, [onValueChange]);
70
- const text = foldText(value, defaultValue);
71
- // Controlled write: when JS-side `value` diverges from what native reported, command the new
72
- // text down with the acknowledged count. A plain prop re-push would race the user's keystrokes;
73
- // the command is the only stale-safe path. No deps; it runs after every render and the
74
- // predicate makes it a no-op unless the text diverged.
75
- useLayoutEffect(() => {
76
- const node = ref.current;
77
- if (node === null)
78
- return;
79
- if (!shouldCommandText(lastNativeText.current, value))
80
- return;
81
- const selStart = selection?.start ?? SELECTION_NONE;
82
- const selEnd = selection?.end ?? selection?.start ?? SELECTION_NONE;
83
- dlog(`TextInput setTextAndSelection count=${mostRecentEventCount} text=${JSON.stringify(value)}`);
84
- dispatchViewCommand(node, 'setTextAndSelection', [
85
- mostRecentEventCount,
86
- value,
87
- selStart,
88
- selEnd,
89
- ]);
90
- lastNativeText.current = value;
91
- });
92
- // autoFocus is driven in JS, not as a native prop: on mount, command `focus` down once (RN does
93
- // the same via TextInputState.focusInput, TextInput.js:538). Empty deps so it fires only on
94
- // mount; the native `focus` command is idempotent if already focused.
95
- useEffect(() => {
96
- if (autoFocus !== true)
97
- return;
98
- const node = ref.current;
99
- if (node === null)
100
- return;
101
- dlog('TextInput autoFocus -> focus command');
102
- dispatchViewCommand(node, 'focus', []);
103
- // eslint-disable-next-line react-hooks/exhaustive-deps
104
- }, []);
105
- const handleFocus = useCallback((event) => {
106
- focused.current = true;
107
- // Track focus app-wide so Keyboard.dismiss can blur this input without a ref.
108
- if (ref.current !== null)
109
- setInputFocused(ref.current);
110
- onFocus?.(event);
111
- }, [onFocus]);
112
- const handleBlur = useCallback((event) => {
113
- focused.current = false;
114
- if (ref.current !== null)
115
- setInputBlurred(ref.current);
116
- onBlur?.(event);
117
- }, [onBlur]);
118
- // The imperative API RN exposes on the ref. focus/blur drive native view commands; clear and
119
- // setSelection reuse setTextAndSelection (the same stale-safe path as a controlled write),
120
- // echoing the acknowledged event count so native applies them.
121
- useImperativeHandle(forwardedRef, () => ({
122
- // Forwarded from the node so a TextInput ref is not poorer than any other host ref. An
123
- // `expose`/`useImperativeHandle` that lists only the five TextInput methods CLOSES the
124
- // node off, and the lowered path loses the opposite four — two different surfaces an app
125
- // crosses between without touching its ref code. `ITextInputHandle` carries the account.
126
- measure: (callback) => ref.current?.measure(callback),
127
- measureInWindow: (callback) => ref.current?.measureInWindow(callback),
128
- measureLayout: (relativeToNativeNode, onSuccess, onFail) => ref.current?.measureLayout(relativeToNativeNode, onSuccess, onFail),
129
- setNativeProps: (nativeProps) => ref.current?.setNativeProps(nativeProps),
130
- focus: () => {
131
- const node = ref.current;
132
- if (node !== null)
133
- dispatchViewCommand(node, 'focus', []);
134
- },
135
- blur: () => {
136
- // Routes through TextInputState so the app-wide focus tracking clears too.
137
- blurTextInput(ref.current);
138
- },
139
- clear: () => {
140
- const node = ref.current;
141
- if (node === null)
142
- return;
143
- dispatchViewCommand(node, 'setTextAndSelection', [
144
- mostRecentEventCount,
145
- '',
146
- 0,
147
- 0,
148
- ]);
149
- lastNativeText.current = '';
150
- },
151
- isFocused: () => focused.current,
152
- setSelection: (start, end) => {
153
- const node = ref.current;
154
- if (node === null)
155
- return;
156
- const current = lastNativeText.current ?? '';
157
- dispatchViewCommand(node, 'setTextAndSelection', [
158
- mostRecentEventCount,
159
- current,
160
- start,
161
- end,
162
- ]);
163
- },
164
- }), [mostRecentEventCount]);
165
- return descriptorToReact(renderTextInput({
166
- multiline: isMultiline,
167
- text,
168
- mostRecentEventCount,
169
- selection,
170
- folded,
171
- passthrough: {
172
- ...passthrough,
173
- ref,
174
- onChange: handleChange,
175
- onFocus: handleFocus,
176
- onBlur: handleBlur,
177
- },
178
- }));
179
- });
180
- TextInput.displayName = 'TextInput';
@@ -1,22 +0,0 @@
1
- import { type FC, type ReactNode } from 'react';
2
- import { type IPressTimingProps } from '@symbiote-native/components';
3
- import { type IPressableProps } from '../pressable';
4
- import type { IStyleProp, IViewStyle } from '../../utils/styles';
5
- type ITouchableBaseProps = Omit<IPressableProps, 'style' | 'children'> & IPressTimingProps & {
6
- style?: IStyleProp<IViewStyle>;
7
- children?: ReactNode;
8
- };
9
- export interface ITouchableOpacityProps extends ITouchableBaseProps {
10
- activeOpacity?: number;
11
- }
12
- export declare const TouchableOpacity: FC<ITouchableOpacityProps>;
13
- export interface ITouchableHighlightProps extends ITouchableBaseProps {
14
- activeOpacity?: number;
15
- underlayColor?: string;
16
- onShowUnderlay?: () => void;
17
- onHideUnderlay?: () => void;
18
- }
19
- export declare const TouchableHighlight: FC<ITouchableHighlightProps>;
20
- export type ITouchableWithoutFeedbackProps = ITouchableBaseProps;
21
- export declare const TouchableWithoutFeedback: FC<ITouchableWithoutFeedbackProps>;
22
- export {};
@@ -1,165 +0,0 @@
1
- // The Touchable* family, all built on Pressable. Ported against RN's own sources
2
- // (.vendors/react-native/.../Components/Touchable/*.js) after the 2026-08-19 audit found ten
3
- // divergences; the shared halves live in @symbiote-native/components and React owns only the
4
- // lifecycle — refs, effects, the real timers, the Animated wiring, and the child clone:
5
- // TouchableOpacity: an Animated.Value fades toward activeOpacity on press-in and back to the
6
- // style's own resting opacity on press-out, driven from onPressIn/onPressOut.
7
- // TouchableHighlight: the underlay machine drives a `shown` flag (NOT `pressed` — RN holds the
8
- // underlay for delayPressOut past the tap); the container takes the underlay color and the
9
- // child takes the lowered opacity, RN's split.
10
- // TouchableWithoutFeedback: the same press-timing machine with the visual half left empty —
11
- // "without feedback" means no VISUAL, not no timing.
12
- import { Children, cloneElement, createElement, isValidElement, useEffect, useRef, useState, } from 'react';
13
- import { createHighlightUnderlayHandlers, createHighlightUnderlayRuntime, createTouchableFeedbackHandlers, createTouchableFeedbackRuntime, hasTouchablePressHandler, resolveHighlightExtraStyles, restingOpacityFromStyle, DEFAULT_ACTIVE_OPACITY, OPACITY_ACTIVE_GRANT_DURATION_MS, OPACITY_INACTIVE_DURATION_MS, TOUCHABLE_MIN_PRESS_DURATION_MS, } from '@symbiote-native/components';
14
- import { TouchablePressable as Pressable, } from '../pressable/index.js';
15
- import { Animated } from '../../modules/animated/index.js';
16
- // The real setTimeout the shared machines schedule on (core/components has no timer globals).
17
- // Returns a canceller so an early release flushes the timer.
18
- function scheduleTimeout(callback, ms) {
19
- const id = setTimeout(callback, ms);
20
- return () => clearTimeout(id);
21
- }
22
- export const TouchableOpacity = props => {
23
- const { activeOpacity = DEFAULT_ACTIVE_OPACITY, style,
24
- // className is pulled out here, like style, and applied to the inner Animated.View below —
25
- // left in ...rest it would land on the outer Pressable instead, which is not where `style`
26
- // (the opacity-fade node) goes.
27
- className, children, disabled, onPressIn, onPressOut, delayPressIn = 0, delayPressOut = 0,
28
- // RN's Touchables override Pressability's 130ms floor with 0 (TouchableOpacity.js:195); the
29
- // active visual is held by the fade's own duration instead.
30
- minPressDuration = TOUCHABLE_MIN_PRESS_DURATION_MS, ...rest } = props;
31
- // RN's _getChildStyleOpacityWithDefault: the fade settles at the opacity the CALLER's style
32
- // asks for, not at a hard 1 — and must START there, or the first paint jumps to fully opaque.
33
- const restingOpacity = restingOpacityFromStyle(style);
34
- // One Animated.Value per mount. The Animated.View leaf commits its current value every frame,
35
- // so timing it animates the real view.
36
- const opacity = useRef(new Animated.Value(restingOpacity)).current;
37
- // The shared press-scheduling cell (delayPressIn timer + activation clock), held across renders.
38
- const runtime = useRef(createTouchableFeedbackRuntime()).current;
39
- function setOpacityTo(toValue, duration) {
40
- // useNativeDriver: true is RN's own (TouchableOpacity.js:242). opacity is natively drivable
41
- // and the fade then survives a busy JS thread, which is the whole point of the feedback.
42
- Animated.timing(opacity, {
43
- toValue,
44
- duration,
45
- easing: Animated.Easing.inOut(Animated.Easing.quad),
46
- useNativeDriver: true,
47
- }).start();
48
- }
49
- // RN's componentDidUpdate: a changed `disabled` or a changed style opacity re-settles the view,
50
- // so a Touchable disabled mid-press does not stay stuck at its active opacity.
51
- const hasRendered = useRef(false);
52
- useEffect(() => {
53
- // RN runs this on UPDATE only. Firing at mount would animate over the value the
54
- // Animated.Value was just constructed with.
55
- if (!hasRendered.current) {
56
- hasRendered.current = true;
57
- return;
58
- }
59
- setOpacityTo(restingOpacity, OPACITY_INACTIVE_DURATION_MS);
60
- // eslint-disable-next-line react-hooks/exhaustive-deps -- setOpacityTo is re-made per render
61
- }, [disabled, restingOpacity]);
62
- // RN's componentWillUnmount: stop the fade so a teardown mid-animation leaves no driver running
63
- // against a node that is gone.
64
- useEffect(() => () => {
65
- opacity.resetAnimation();
66
- }, [opacity]);
67
- // The shared machine owns the delayPressIn/minPressDuration scheduling; the adapter supplies only
68
- // the native seam — the Animated opacity fade + the framework emit — as activate/deactivate.
69
- const { handlePressIn, handlePressOut } = createTouchableFeedbackHandlers({
70
- delayPressIn,
71
- delayPressOut,
72
- minPressDuration,
73
- schedule: scheduleTimeout,
74
- now: Date.now,
75
- }, runtime, {
76
- activate(event) {
77
- setOpacityTo(activeOpacity, OPACITY_ACTIVE_GRANT_DURATION_MS);
78
- onPressIn?.(event);
79
- },
80
- deactivate(event) {
81
- setOpacityTo(restingOpacity, OPACITY_INACTIVE_DURATION_MS);
82
- onPressOut?.(event);
83
- },
84
- });
85
- return createElement(Pressable, { ...rest, disabled, onPressIn: handlePressIn, onPressOut: handlePressOut }, createElement(Animated.View, { style: [style, { opacity }], className }, children));
86
- };
87
- // RN's render: the lowered opacity is cloned onto the CHILD while the container keeps an opaque
88
- // underlay (TouchableHighlight.js). Fold both onto the container instead and the opacity fades the
89
- // very underlay it is meant to reveal — `underlayColor: 'black'` paints grey. React is the one
90
- // adapter that can reach the child without a wrapper view perturbing the flex chain.
91
- // A non-element child (text, a fragment, nothing at all) has no style prop to compose onto, so it
92
- // passes through untouched rather than throwing the way RN's Children.only would.
93
- function withChildStyle(children, extra) {
94
- const child = Children.count(children) === 1 ? children : undefined;
95
- if (!isValidElement(child))
96
- return children;
97
- return cloneElement(child, { style: [child.props.style, extra] });
98
- }
99
- export const TouchableHighlight = props => {
100
- const { activeOpacity, underlayColor, style, children, onPress, onPressIn, onPressOut, onShowUnderlay, onHideUnderlay, delayPressOut = 0, ...rest } = props;
101
- // `shown` is NOT `pressed`: RN holds the underlay for delayPressOut past the tap so a press too
102
- // fast to see still flashes, and the pressed flag is already false by then.
103
- const [shown, setShown] = useState(false);
104
- const runtime = useRef(createHighlightUnderlayRuntime()).current;
105
- // RN's _hasPressHandler gate: no press handler, no underlay. onLongPress is only READ here —
106
- // it stays in `rest` for Pressable to fire directly.
107
- const hasPressHandler = hasTouchablePressHandler({
108
- onPress,
109
- onPressIn,
110
- onPressOut,
111
- onLongPress: rest.onLongPress,
112
- });
113
- const underlay = createHighlightUnderlayHandlers({ delayPressOut, hasPressHandler, schedule: scheduleTimeout }, runtime, { setShown, onShowUnderlay, onHideUnderlay });
114
- const extraStyles = resolveHighlightExtraStyles({
115
- shown,
116
- hasPressHandler,
117
- underlayColor,
118
- activeOpacity,
119
- });
120
- // Visual first, then the caller's callback — RN's order in _createPressabilityConfig.
121
- function handlePressIn(event) {
122
- underlay.handlePressIn(event);
123
- onPressIn?.(event);
124
- }
125
- function handlePress(event) {
126
- underlay.handlePress(event);
127
- onPress?.(event);
128
- }
129
- function handlePressOut(event) {
130
- underlay.handlePressOut(event);
131
- onPressOut?.(event);
132
- }
133
- return createElement(Pressable, {
134
- ...rest,
135
- style: extraStyles === undefined ? style : [style, extraStyles.underlay],
136
- onPress: handlePress,
137
- onPressIn: handlePressIn,
138
- onPressOut: handlePressOut,
139
- }, extraStyles === undefined
140
- ? children
141
- : withChildStyle(children, extraStyles.child));
142
- };
143
- export const TouchableWithoutFeedback = props => {
144
- const { children, onPressIn, onPressOut, delayPressIn = 0, delayPressOut = 0, minPressDuration = TOUCHABLE_MIN_PRESS_DURATION_MS, ...rest } = props;
145
- // RN's TouchableWithoutFeedback builds a FULL Pressability config with delayPressIn /
146
- // delayPressOut / minPressDuration: 0 — the same shared machine TouchableOpacity uses, with the
147
- // visual half left empty. Spreading the delay props onto Pressable instead did nothing: it does
148
- // not read them, and they leaked to the host as unknown props.
149
- const runtime = useRef(createTouchableFeedbackRuntime()).current;
150
- const { handlePressIn, handlePressOut } = createTouchableFeedbackHandlers({
151
- delayPressIn,
152
- delayPressOut,
153
- minPressDuration,
154
- schedule: scheduleTimeout,
155
- now: Date.now,
156
- }, runtime, {
157
- activate(event) {
158
- onPressIn?.(event);
159
- },
160
- deactivate(event) {
161
- onPressOut?.(event);
162
- },
163
- });
164
- return createElement(Pressable, { ...rest, onPressIn: handlePressIn, onPressOut: handlePressOut }, children);
165
- };
@@ -1,18 +0,0 @@
1
- import { type FC, type ReactNode } from 'react';
2
- import { type INativeFeedbackBackground, type IThemeAttrBackground, type IRippleBackground } from '@symbiote-native/components';
3
- import { type IPressableProps } from '../pressable';
4
- export type { INativeFeedbackBackground, IThemeAttrBackground, IRippleBackground, } from '@symbiote-native/components';
5
- type ITouchableNativeFeedbackBaseProps = Omit<IPressableProps, 'style' | 'children'> & {
6
- background?: INativeFeedbackBackground;
7
- useForeground?: boolean;
8
- children?: ReactNode;
9
- };
10
- export type ITouchableNativeFeedbackProps = ITouchableNativeFeedbackBaseProps;
11
- interface ITouchableNativeFeedbackComponent extends FC<ITouchableNativeFeedbackProps> {
12
- SelectableBackground: (rippleRadius?: number) => IThemeAttrBackground;
13
- SelectableBackgroundBorderless: (rippleRadius?: number) => IThemeAttrBackground;
14
- Ripple: (color: string, borderless: boolean, rippleRadius?: number) => IRippleBackground;
15
- canUseNativeForeground: () => boolean;
16
- }
17
- declare const TouchableNativeFeedback: ITouchableNativeFeedbackComponent;
18
- export { TouchableNativeFeedback };
@@ -1,32 +0,0 @@
1
- // TouchableNativeFeedback: Android's ripple/state-drawable touchable, built on Pressable like the
2
- // rest of the family. RN realizes its feedback by cloning the child into an RCTView carrying native
3
- // ripple props; we instead nest the child under a feedback View that carries those props, inside a
4
- // Pressable that owns the press wiring. The native props (nativeBackgroundAndroid /
5
- // nativeForegroundAndroid) are read by Android's ReactViewManager; on iOS they are inert props, so
6
- // the component still renders its child with working press wiring. The static factories +
7
- // background mapping are shared in @symbiote-native/components/view. React only attaches them onto the
8
- // component value and nests the feedback View.
9
- import { createElement } from 'react';
10
- import { dlog } from '@symbiote-native/engine';
11
- import { backgroundProps, canUseNativeForeground, rippleBackground, selectableBackground, selectableBackgroundBorderless, } from '@symbiote-native/components';
12
- import { View } from '../../components.js';
13
- import { TouchablePressable as Pressable, } from '../pressable/index.js';
14
- const TouchableNativeFeedbackImpl = props => {
15
- const { background, useForeground = false, children, ...rest } = props;
16
- // RN defaults a missing background to SelectableBackground() so the touchable always has
17
- // feedback; mirror that here.
18
- const resolved = background ?? selectableBackground();
19
- dlog(`TouchableNativeFeedback render ${resolved.type} useForeground ${useForeground}`);
20
- const nativeProps = backgroundProps(resolved, useForeground);
21
- // The native ripple props ride a dedicated feedback View nested under the Pressable; the
22
- // Pressable owns the press wiring, the feedback View owns the drawable.
23
- const feedback = createElement(View, nativeProps, children);
24
- return createElement(Pressable, rest, feedback);
25
- };
26
- const TouchableNativeFeedback = Object.assign(TouchableNativeFeedbackImpl, {
27
- SelectableBackground: selectableBackground,
28
- SelectableBackgroundBorderless: selectableBackgroundBorderless,
29
- Ripple: rippleBackground,
30
- canUseNativeForeground,
31
- });
32
- export { TouchableNativeFeedback };
package/build/jsx.d.ts DELETED
@@ -1,18 +0,0 @@
1
- import type { ISymbioteIntrinsic } from '@symbiote-native/components';
2
- import type { IViewProps, ITextProps } from './components';
3
- interface HostProps {
4
- style?: unknown;
5
- children?: import('react').ReactNode;
6
- [key: string]: unknown;
7
- }
8
- type ILooseIntrinsics = Omit<Record<ISymbioteIntrinsic, HostProps>, 'symbiote-view' | 'symbiote-text'>;
9
- declare module 'react' {
10
- namespace JSX {
11
- interface IntrinsicElements extends ILooseIntrinsics {
12
- 'symbiote-view': IViewProps;
13
- 'symbiote-text': ITextProps;
14
- }
15
- }
16
- }
17
- export type ISymbioteIntrinsicTag = keyof import('react').JSX.IntrinsicElements;
18
- export {};
@@ -1 +0,0 @@
1
- export { resolveStateStyle, type IPressStateArgument, type IResolvedStateStyle, } from '@symbiote-native/components/state-style';
@@ -1,13 +0,0 @@
1
- // Re-export only. The implementation is shared (`@symbiote-native/components`) because two adapters
2
- // had written byte-identical copies; this file exists so the code a transform EMITS keeps importing
3
- // from the adapter package the app already depends on, rather than reaching past it.
4
- //
5
- // Imported from the SUBPATH, not the barrel: every adapter barrel re-exports
6
- // `@symbiote-native/components` wholesale, so a name on that barrel silently becomes public API on
7
- // all five — which is how a transform internal nearly shipped as a supported export.
8
- //
9
- // Present on every adapter, including those with no lowering transform yet: a per-framework subpath
10
- // that four of five declare is exactly the silent gap `tests/package-subpath-parity.test.ts` exists
11
- // to catch, and the file costs a re-export. Deliberately NOT on the public barrel — the emitted
12
- // code names it, app code does not.
13
- export { resolveStateStyle, } from '@symbiote-native/components/state-style';