@symbiote-native/components 0.4.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -8
- package/build/accessibility-props.d.ts +11 -0
- package/build/accessibility-props.js +30 -117
- package/build/behaviors/image.d.ts +3 -0
- package/build/behaviors/image.js +123 -0
- package/build/behaviors/input-accessory-view.d.ts +3 -0
- package/build/behaviors/input-accessory-view.js +70 -0
- package/build/behaviors/pressable.d.ts +2 -0
- package/build/behaviors/pressable.js +310 -0
- package/build/behaviors/switch.d.ts +2 -0
- package/build/behaviors/switch.js +182 -0
- package/build/behaviors/text-input.d.ts +14 -0
- package/build/behaviors/text-input.js +291 -0
- package/build/bootstrap/index.js +1 -1
- package/build/component-names/index.android.js +23 -1
- package/build/component-names/index.ios.js +9 -1
- package/build/component-names/shared.d.ts +1 -1
- package/build/component-names/shared.js +40 -1
- package/build/descriptor.d.ts +8 -0
- package/build/descriptor.js +27 -0
- package/build/fold-host-bag.d.ts +15 -0
- package/build/fold-host-bag.js +99 -0
- package/build/index.d.ts +25 -12
- package/build/index.js +18 -6
- package/build/resolve-intrinsic.d.ts +7 -0
- package/build/resolve-intrinsic.js +49 -0
- package/build/state/pressable.d.ts +9 -0
- package/build/state/pressable.js +133 -37
- package/build/state/sticky-header-reducer.js +28 -5
- package/build/state/switch.js +1 -1
- package/build/state/text-input.d.ts +7 -1
- package/build/state/text-input.js +46 -8
- package/build/state/touchable.d.ts +30 -1
- package/build/state/touchable.js +94 -5
- package/build/state/virtualized-list-diagnostics.d.ts +31 -0
- package/build/state/virtualized-list-diagnostics.js +33 -0
- package/build/state/virtualized-list-reducer.d.ts +14 -0
- package/build/state/virtualized-list-reducer.js +179 -45
- package/build/state/virtualized-list.d.ts +5 -2
- package/build/state/virtualized-list.js +143 -34
- package/build/state-style.d.ts +15 -0
- package/build/state-style.js +47 -0
- package/build/text-props.d.ts +9 -0
- package/build/text-props.js +25 -0
- package/build/view/render-activity-indicator.js +37 -3
- package/build/view/render-image/index.d.ts +2 -0
- package/build/view/render-image/index.js +42 -5
- package/build/view/render-input-accessory-view.d.ts +2 -0
- package/build/view/render-input-accessory-view.js +41 -6
- package/build/view/render-keyboard-avoiding-view.d.ts +14 -2
- package/build/view/render-keyboard-avoiding-view.js +52 -6
- package/build/view/render-modal.js +4 -2
- package/build/view/render-pressable/index.js +3 -1
- package/build/view/render-scroll-sticky.js +1 -1
- package/build/view/render-scroll-view.js +9 -3
- package/build/view/render-switch.js +11 -2
- package/build/view/render-text-input.js +6 -2
- package/build/view/render-touchable-highlight.d.ts +11 -1
- package/build/view/render-touchable-highlight.js +11 -10
- package/build/view/render-touchable-native-feedback.js +5 -1
- package/host-primitives.cjs +380 -0
- package/host-primitives.d.cts +35 -0
- package/lowering-fixtures.cjs +259 -0
- package/lowering-fixtures.d.cts +17 -0
- package/package.json +42 -5
- package/specialize-state-style.cjs +219 -0
- package/specialize-state-style.d.cts +15 -0
package/build/index.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
export { el, txt } from './descriptor';
|
|
2
2
|
export type { IDescriptor, IDescriptorType, IDescriptorProps, IDescriptorChild, } from './descriptor';
|
|
3
|
+
export { createDescriptorShapeGuard } from './descriptor';
|
|
4
|
+
export type { IDescriptorShapeGuard } from './descriptor';
|
|
3
5
|
export { resolveAccessibilityProps } from './accessibility-props';
|
|
6
|
+
export { resolveTextProps } from './text-props';
|
|
7
|
+
export type { IEllipsizeMode, ITextDefaultableProps } from './text-props';
|
|
4
8
|
export type { IAccessibilityProps, IAriaProps, IAccessibilityRole, IRole, IAccessibilityStateValue, IAccessibilityValue, IAccessibilityActionInfo, } from './accessibility-props';
|
|
5
9
|
export { descriptorFor, COMPONENT_DESCRIPTORS } from './component-names';
|
|
6
10
|
export { buildDescriptors, makeDescriptorFor } from './component-names/shared';
|
|
7
|
-
export type { ISymbioteIntrinsic, IComponentDescriptor } from './component-names/shared';
|
|
11
|
+
export type { ISymbioteIntrinsic, IComponentDescriptor, } from './component-names/shared';
|
|
8
12
|
export { renderActivityIndicator } from './view/render-activity-indicator';
|
|
9
13
|
export type { IActivityIndicatorProps, IActivityIndicatorViewProps, IActivityIndicatorPlatform, IActivityIndicatorSize, } from './view/render-activity-indicator';
|
|
10
14
|
export { renderSwitch } from './view/render-switch';
|
|
@@ -17,12 +21,12 @@ export type { IScrollIntrinsics, IContentSize, IScrollForwarding, IScrollForward
|
|
|
17
21
|
export { buildScrollViewHandle, splitLayoutProps, attachStickyScroll, isSymbioteEvent, forwardScrollEvent, } from './scroll-view-commands';
|
|
18
22
|
export type { IScrollViewHandle } from './scroll-view-commands';
|
|
19
23
|
export { computeStickyInterpolation, nextStickyHeaderY, stickyDebounceMs, readLayoutNumber, STICKY_HEADER_Z_INDEX, } from './view/render-scroll-sticky';
|
|
20
|
-
export type { IStickyHeaderProps, IStickyInterpolationParams } from './view/render-scroll-sticky';
|
|
24
|
+
export type { IStickyHeaderProps, IStickyInterpolationParams, } from './view/render-scroll-sticky';
|
|
21
25
|
export { reduceSticky, createInitialStickyState, stickyEffectSignature, } from './state/sticky-header-reducer';
|
|
22
26
|
export type { IStickyHeaderState, IStickyReducerInputs, IStickyAction, IStickyEffect, IStickyReduceResult, } from './state/sticky-header-reducer';
|
|
23
27
|
export type { IImageSource, IImageSourceProp, IResizeMode, IImageProps, IImageViewProps, } from './view/render-image';
|
|
24
28
|
export { renderImage } from './view/render-image';
|
|
25
|
-
export type { IImageStatics, IImageSize, IImageCacheStatus } from '@symbiote-native/engine';
|
|
29
|
+
export type { IImageStatics, IImageSize, IImageCacheStatus, } from '@symbiote-native/engine';
|
|
26
30
|
export { imageStatics, setImageSourceResolver } from '@symbiote-native/engine';
|
|
27
31
|
export { renderImageBackground } from './view/render-image-background';
|
|
28
32
|
export type { IImageBackgroundViewProps } from './view/render-image-background';
|
|
@@ -30,29 +34,38 @@ export { renderInputAccessoryView } from './view/render-input-accessory-view';
|
|
|
30
34
|
export type { IInputAccessoryViewViewProps } from './view/render-input-accessory-view';
|
|
31
35
|
export { renderModal } from './view/render-modal';
|
|
32
36
|
export type { IModalViewProps, IModalAnimationType, IModalPresentationStyle, IModalOrientation, IModalOrientationChangeEvent, } from './view/render-modal';
|
|
33
|
-
export { modalReducer, createInitialModalState, shouldRenderModal } from './state/modal';
|
|
37
|
+
export { modalReducer, createInitialModalState, shouldRenderModal, } from './state/modal';
|
|
34
38
|
export type { IModalState, IModalAction } from './state/modal';
|
|
35
|
-
export { computeInset, readKeyboardFrame, readLayoutFrame, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from './view/render-keyboard-avoiding-view';
|
|
36
|
-
export type { IKeyboardAvoidingBehavior, IMeasuredFrame, IKeyboardFrame, IKeyboardAvoidingLayout, IResolveKeyboardAvoidingLayoutParams, } from './view/render-keyboard-avoiding-view';
|
|
37
|
-
export { createPressHandlers, createPressRuntime, normalizeRect, maxEdge, isTouchWithinRegion, readPoint, computeRegion, rippleProps, DEFAULT_DELAY_LONG_PRESS_MS, DEFAULT_PRESS_RECT_OFFSETS, } from './state/pressable';
|
|
39
|
+
export { computeInset, keyboardAvoidingEventNamesFor, readPrefersCrossFadeTransitions, readKeyboardFrame, readLayoutFrame, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from './view/render-keyboard-avoiding-view';
|
|
40
|
+
export type { IComputeInsetOptions, IKeyboardAvoidingBehavior, IKeyboardAvoidingEventNames, IMeasuredFrame, IKeyboardFrame, IKeyboardAvoidingLayout, IResolveKeyboardAvoidingLayoutParams, } from './view/render-keyboard-avoiding-view';
|
|
41
|
+
export { createPressHandlers, createPressRuntime, disposePressRuntime, normalizeRect, maxEdge, isTouchWithinRegion, readPoint, computeRegion, rippleProps, DEFAULT_DELAY_LONG_PRESS_MS, DEFAULT_MIN_PRESS_DURATION_MS, DEFAULT_PRESS_RECT_OFFSETS, } from './state/pressable';
|
|
38
42
|
export type { IPressState, IPressHandler, IPressHandlers, IPressHost, IPressRuntime, IPressMachineConfig, IFrameCallback, IRectOffset, IEdgeInsets, IResponderRegion, IPressableAndroidRippleConfig, IRippleBackground as IPressableRippleBackground, } from './state/pressable';
|
|
39
43
|
export { buildPressableListeners, resolveDisabledAccessibilityState, noteHoverNoop, shouldSuppressPress, shouldClaimResponder, isTerminationAllowed, } from './view/render-pressable';
|
|
40
|
-
export { computePressOutWait, createTouchableFeedbackRuntime, createTouchableFeedbackHandlers, DEFAULT_ACTIVE_OPACITY, OPACITY_ACTIVE_DURATION_MS, OPACITY_INACTIVE_DURATION_MS, RESTING_OPACITY, DEFAULT_HIGHLIGHT_CHILD_OPACITY, DEFAULT_UNDERLAY_COLOR,
|
|
41
|
-
export type { IPressTimingProps, ITouchableHandler, ITouchableFeedbackRuntime, ITouchableFeedbackConfig, ITouchableFeedbackCallbacks, ITouchableFeedbackHandlers, } from './state/touchable';
|
|
42
|
-
export {
|
|
44
|
+
export { computePressOutWait, createTouchableFeedbackRuntime, createTouchableFeedbackHandlers, DEFAULT_ACTIVE_OPACITY, OPACITY_ACTIVE_GRANT_DURATION_MS, OPACITY_ACTIVE_DURATION_MS, OPACITY_INACTIVE_DURATION_MS, RESTING_OPACITY, DEFAULT_HIGHLIGHT_CHILD_OPACITY, DEFAULT_UNDERLAY_COLOR, TOUCHABLE_MIN_PRESS_DURATION_MS, restingOpacityFromStyle, hasTouchablePressHandler, createHighlightUnderlayRuntime, createHighlightUnderlayHandlers, } from './state/touchable';
|
|
45
|
+
export type { IPressTimingProps, ITouchableHandler, ITouchableFeedbackRuntime, ITouchableFeedbackConfig, ITouchableFeedbackCallbacks, ITouchableFeedbackHandlers, ITouchablePressHandlerProps, IHighlightUnderlayRuntime, IHighlightUnderlayConfig, IHighlightUnderlayCallbacks, IHighlightUnderlayHandlers, } from './state/touchable';
|
|
46
|
+
export { resolveHighlightExtraStyles } from './view/render-touchable-highlight';
|
|
47
|
+
export type { ITouchableHighlightExtraStyles, ITouchableHighlightUnderlayView, } from './view/render-touchable-highlight';
|
|
43
48
|
export { backgroundProps, canUseNativeForeground, selectableBackground, selectableBackgroundBorderless, rippleBackground, } from './view/render-touchable-native-feedback';
|
|
44
49
|
export type { INativeFeedbackBackground, IThemeAttrBackground, IRippleBackground, } from './view/render-touchable-native-feedback';
|
|
45
50
|
export { BUTTON_ACCESSIBILITY_ROLE, buttonTextStyle, resolveButtonTextStyle, } from './view/render-button';
|
|
46
51
|
export type { IButtonProps } from './view/render-button';
|
|
47
52
|
export { resolveTextInputProps, foldText, foldAutoComplete, foldSubmitBehavior, mapAutoComplete, textFromChange, eventCountFromChange, shouldCommandText, INITIAL_EVENT_COUNT, SELECTION_NONE, } from './state/text-input';
|
|
48
53
|
export type { ITextInputProps, ITextInputHandle, ITextInputSelection, ITextInputEventHandler, ITextInputFoldInput, IFoldedTextInputProps, IInputMode, IEnterKeyHint, ISubmitBehavior, } from './state/text-input';
|
|
54
|
+
export { keyboardTypeForInputMode } from './state/text-input';
|
|
49
55
|
export { renderTextInput } from './view/render-text-input';
|
|
50
56
|
export type { ITextInputViewProps } from './view/render-text-input';
|
|
51
|
-
export { DEFAULT_WINDOW_SIZE, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_END_REACHED_THRESHOLD, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_VIEW_AREA_COVERAGE_PERCENT_THRESHOLD, DEFAULT_START_REACHED_THRESHOLD, FIRST_INDEX, EMPTY_OFFSET, NO_INDEX, NO_CONTENT_LENGTH_SENT, INVERTED_Y_STYLE, INVERTED_X_STYLE, readScrollOffset, readLayoutLength, buildOffsets, computeWindow, throttleWindow, visiblePercent, isCellViewable, offsetForIndex, averageMeasuredLength, highestMeasuredIndex, computeEndReached, computeStartReached, buildViewabilityPairs, computeViewableSet, diffViewable, maxMinimumViewTime, buildListPlan, computeMvcpAdjustment, resolveItemKey, indexOfItem, offsetForEnd, isSeparatorGapInRange, decideEdgeReached, resolveStickySectionHeaders, wrapFixedLayout, resolveAverageLength, } from './state/virtualized-list';
|
|
57
|
+
export { DEFAULT_WINDOW_SIZE, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_END_REACHED_THRESHOLD, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_VIEW_AREA_COVERAGE_PERCENT_THRESHOLD, DEFAULT_START_REACHED_THRESHOLD, FIRST_INDEX, EMPTY_OFFSET, NO_INDEX, NO_CONTENT_LENGTH_SENT, INVERTED_Y_STYLE, INVERTED_X_STYLE, readScrollOffset, readLayoutLength, readLayoutOffset, buildOffsets, computeWindow, throttleWindow, visiblePercent, isCellViewable, offsetForIndex, averageMeasuredLength, averageMeasuredStride, highestMeasuredIndex, computeEndReached, computeStartReached, buildViewabilityPairs, computeViewableSet, diffViewable, maxMinimumViewTime, buildListPlan, computeMvcpAdjustment, resolveItemKey, indexOfItem, offsetForEnd, isSeparatorGapInRange, decideEdgeReached, resolveStickySectionHeaders, wrapFixedLayout, resolveAverageLength, } from './state/virtualized-list';
|
|
52
58
|
export type { IMvcpAction, IMvcpAdjustmentParams, IMvcpAdjustmentResult, ICellLayout, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, IVirtualizedListHandle, IViewableSetParams, IListCellPlan, IListPlan, IListPlanParams, } from './state/virtualized-list';
|
|
53
59
|
export { reduceList, createInitialListState, listEffectSignature, } from './state/virtualized-list-reducer';
|
|
54
60
|
export type { IListState, IListMetrics, IListReducerInputs, IListAction, IListEffect, IListReduceResult, } from './state/virtualized-list-reducer';
|
|
61
|
+
export { subscribeListDiagnostics } from './state/virtualized-list-diagnostics';
|
|
62
|
+
export type { IListDiagnosticFrame, IListDiagnosticMove, } from './state/virtualized-list-diagnostics';
|
|
55
63
|
export { SINGLE_COLUMN, chunkIntoRows, rowKeyExtractor, expandRowToken, expandRowViewability, lastItemOfRow, firstItemOfRow, } from './state/flat-list';
|
|
56
64
|
export type { IRow } from './state/flat-list';
|
|
57
65
|
export { flattenSections, unwrapEntryItem, sectionEntryKey, scrollLocationToFlatIndex, } from './state/section-list';
|
|
58
|
-
export type { ISection, ISectionEntry, IVirtualizedSectionListHandle } from './state/section-list';
|
|
66
|
+
export type { ISection, ISectionEntry, IVirtualizedSectionListHandle, } from './state/section-list';
|
|
67
|
+
export { registerPressableBehavior, PRESSABLE_TAG, } from './behaviors/pressable';
|
|
68
|
+
export { foldImagePayload, IMAGE_TAG, registerImageBehavior, } from './behaviors/image';
|
|
69
|
+
export { foldInputAccessoryViewPayload, INPUT_ACCESSORY_VIEW_TAG, registerInputAccessoryViewBehavior, } from './behaviors/input-accessory-view';
|
|
70
|
+
export { registerTextInputBehavior, buildTextInputHandle, TEXT_INPUT_TAG, TEXT_INPUT_MULTILINE_TAG, } from './behaviors/text-input';
|
|
71
|
+
export { registerSwitchBehavior, SWITCH_TAG } from './behaviors/switch';
|
package/build/index.js
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
// wires state→render with ITS lifecycle (React hooks / Vue reactivity) and maps the
|
|
4
4
|
// Descriptor onto its own element.
|
|
5
5
|
export { el, txt } from './descriptor.js';
|
|
6
|
+
// The shape-stability guard a fine-grained adapter's Descriptor bridge builds on. Internal to
|
|
7
|
+
// adapter bridges (Svelte's and Solid's), NOT part of an app's surface — so it stays out of the
|
|
8
|
+
// adapter barrels, unlike the passthrough names in tests/adapter-barrel-parity.test.ts.
|
|
9
|
+
export { createDescriptorShapeGuard } from './descriptor.js';
|
|
6
10
|
// Accessibility folding: the web-alias (aria-*/role) → canonical accessibility* transform
|
|
7
11
|
// and its types. Framework-agnostic, so React, Vue, and the next adapter all fold
|
|
8
12
|
// identically; moved here from @symbiote-native/react. RefreshControl/SafeAreaView/ScrollView consume it.
|
|
9
13
|
export { resolveAccessibilityProps } from './accessibility-props.js';
|
|
14
|
+
export { resolveTextProps } from './text-props.js';
|
|
10
15
|
// Intrinsic (`symbiote-*`) → Fabric view-name resolution. Shared by every adapter so the
|
|
11
16
|
// names CANNOT drift between them (one engine, one Fabric). The name tables are
|
|
12
17
|
// Metro-split (.ios/.android, filename selects, no Platform.OS read); the base
|
|
@@ -37,19 +42,19 @@ export { renderImageBackground } from './view/render-image-background.js';
|
|
|
37
42
|
export { renderInputAccessoryView } from './view/render-input-accessory-view.js';
|
|
38
43
|
// Modal: full 3-layer split (state machine gates the iOS keep-alive frame).
|
|
39
44
|
export { renderModal } from './view/render-modal.js';
|
|
40
|
-
export { modalReducer, createInitialModalState, shouldRenderModal } from './state/modal.js';
|
|
45
|
+
export { modalReducer, createInitialModalState, shouldRenderModal, } from './state/modal.js';
|
|
41
46
|
// KeyboardAvoidingView: pure inset/behavior math; the adapter owns the Keyboard subscription.
|
|
42
|
-
export { computeInset, readKeyboardFrame, readLayoutFrame, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from './view/render-keyboard-avoiding-view.js';
|
|
47
|
+
export { computeInset, keyboardAvoidingEventNamesFor, readPrefersCrossFadeTransitions, readKeyboardFrame, readLayoutFrame, resolveKeyboardAvoidingLayout, DEFAULT_VERTICAL_OFFSET, } from './view/render-keyboard-avoiding-view.js';
|
|
43
48
|
// Pressable: the press state machine (logic) + render decisions. The pure lifecycle (timers,
|
|
44
49
|
// geometry, drift test, suppression flags) lives in state/pressable; the responder-listener +
|
|
45
50
|
// accessibilityState + ripple render decisions in view/render-pressable. Every adapter supplies
|
|
46
51
|
// only its reactive `pressed` cell + the View's measure handle (see the 3-layer split).
|
|
47
|
-
export { createPressHandlers, createPressRuntime, normalizeRect, maxEdge, isTouchWithinRegion, readPoint, computeRegion, rippleProps, DEFAULT_DELAY_LONG_PRESS_MS, DEFAULT_PRESS_RECT_OFFSETS, } from './state/pressable.js';
|
|
52
|
+
export { createPressHandlers, createPressRuntime, disposePressRuntime, normalizeRect, maxEdge, isTouchWithinRegion, readPoint, computeRegion, rippleProps, DEFAULT_DELAY_LONG_PRESS_MS, DEFAULT_MIN_PRESS_DURATION_MS, DEFAULT_PRESS_RECT_OFFSETS, } from './state/pressable.js';
|
|
48
53
|
export { buildPressableListeners, resolveDisabledAccessibilityState, noteHoverNoop, shouldSuppressPress, shouldClaimResponder, isTerminationAllowed, } from './view/render-pressable/index.js';
|
|
49
54
|
// Touchable*: shared press-timing constants + the deactivation-floor math (the Animated feedback
|
|
50
55
|
// itself stays per-adapter), and TouchableNativeFeedback's pure static factories + background map.
|
|
51
|
-
export { computePressOutWait, createTouchableFeedbackRuntime, createTouchableFeedbackHandlers, DEFAULT_ACTIVE_OPACITY, OPACITY_ACTIVE_DURATION_MS, OPACITY_INACTIVE_DURATION_MS, RESTING_OPACITY, DEFAULT_HIGHLIGHT_CHILD_OPACITY, DEFAULT_UNDERLAY_COLOR,
|
|
52
|
-
export {
|
|
56
|
+
export { computePressOutWait, createTouchableFeedbackRuntime, createTouchableFeedbackHandlers, DEFAULT_ACTIVE_OPACITY, OPACITY_ACTIVE_GRANT_DURATION_MS, OPACITY_ACTIVE_DURATION_MS, OPACITY_INACTIVE_DURATION_MS, RESTING_OPACITY, DEFAULT_HIGHLIGHT_CHILD_OPACITY, DEFAULT_UNDERLAY_COLOR, TOUCHABLE_MIN_PRESS_DURATION_MS, restingOpacityFromStyle, hasTouchablePressHandler, createHighlightUnderlayRuntime, createHighlightUnderlayHandlers, } from './state/touchable.js';
|
|
57
|
+
export { resolveHighlightExtraStyles } from './view/render-touchable-highlight.js';
|
|
53
58
|
export { backgroundProps, canUseNativeForeground, selectableBackground, selectableBackgroundBorderless, rippleBackground, } from './view/render-touchable-native-feedback.js';
|
|
54
59
|
// Button: shared base text style, role constant, and color fold (the adapter composes its own
|
|
55
60
|
// TouchableOpacity + Text).
|
|
@@ -60,15 +65,22 @@ export { BUTTON_ACCESSIBILITY_ROLE, buttonTextStyle, resolveButtonTextStyle, } f
|
|
|
60
65
|
// resolved native props. Both shared verbatim across React and Vue; the adapter owns only the
|
|
61
66
|
// hooks/reactivity + the imperative handle.
|
|
62
67
|
export { resolveTextInputProps, foldText, foldAutoComplete, foldSubmitBehavior, mapAutoComplete, textFromChange, eventCountFromChange, shouldCommandText, INITIAL_EVENT_COUNT, SELECTION_NONE, } from './state/text-input.js';
|
|
68
|
+
export { keyboardTypeForInputMode } from './state/text-input.js';
|
|
63
69
|
export { renderTextInput } from './view/render-text-input.js';
|
|
64
70
|
// VirtualizedList family: the framework-agnostic windowing engine + data shapes. Lists
|
|
65
71
|
// have NO view/render-*.ts (the cell content is the framework's own children, so there is
|
|
66
72
|
// no Descriptor render fn) — the shared layer for lists is this STATE/logic, reused
|
|
67
73
|
// verbatim by React and Vue.
|
|
68
|
-
export { DEFAULT_WINDOW_SIZE, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_END_REACHED_THRESHOLD, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_VIEW_AREA_COVERAGE_PERCENT_THRESHOLD, DEFAULT_START_REACHED_THRESHOLD, FIRST_INDEX, EMPTY_OFFSET, NO_INDEX, NO_CONTENT_LENGTH_SENT, INVERTED_Y_STYLE, INVERTED_X_STYLE, readScrollOffset, readLayoutLength, buildOffsets, computeWindow, throttleWindow, visiblePercent, isCellViewable, offsetForIndex, averageMeasuredLength, highestMeasuredIndex, computeEndReached, computeStartReached, buildViewabilityPairs, computeViewableSet, diffViewable, maxMinimumViewTime, buildListPlan, computeMvcpAdjustment, resolveItemKey, indexOfItem, offsetForEnd, isSeparatorGapInRange, decideEdgeReached, resolveStickySectionHeaders, wrapFixedLayout, resolveAverageLength, } from './state/virtualized-list.js';
|
|
74
|
+
export { DEFAULT_WINDOW_SIZE, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_END_REACHED_THRESHOLD, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_VIEW_AREA_COVERAGE_PERCENT_THRESHOLD, DEFAULT_START_REACHED_THRESHOLD, FIRST_INDEX, EMPTY_OFFSET, NO_INDEX, NO_CONTENT_LENGTH_SENT, INVERTED_Y_STYLE, INVERTED_X_STYLE, readScrollOffset, readLayoutLength, readLayoutOffset, buildOffsets, computeWindow, throttleWindow, visiblePercent, isCellViewable, offsetForIndex, averageMeasuredLength, averageMeasuredStride, highestMeasuredIndex, computeEndReached, computeStartReached, buildViewabilityPairs, computeViewableSet, diffViewable, maxMinimumViewTime, buildListPlan, computeMvcpAdjustment, resolveItemKey, indexOfItem, offsetForEnd, isSeparatorGapInRange, decideEdgeReached, resolveStickySectionHeaders, wrapFixedLayout, resolveAverageLength, } from './state/virtualized-list.js';
|
|
69
75
|
// The orchestration reducer: the one pure state machine every list adapter drives, folding the
|
|
70
76
|
// after-commit effect skeleton (window recompute -> edge -> viewability -> initial-scroll -> MVCP)
|
|
71
77
|
// out of each adapter's reactive glue. The adapter maps events to actions and runs the effects.
|
|
72
78
|
export { reduceList, createInitialListState, listEffectSignature, } from './state/virtualized-list-reducer.js';
|
|
79
|
+
export { subscribeListDiagnostics } from './state/virtualized-list-diagnostics.js';
|
|
73
80
|
export { SINGLE_COLUMN, chunkIntoRows, rowKeyExtractor, expandRowToken, expandRowViewability, lastItemOfRow, firstItemOfRow, } from './state/flat-list.js';
|
|
74
81
|
export { flattenSections, unwrapEntryItem, sectionEntryKey, scrollLocationToFlatIndex, } from './state/section-list.js';
|
|
82
|
+
export { registerPressableBehavior, PRESSABLE_TAG, } from './behaviors/pressable.js';
|
|
83
|
+
export { foldImagePayload, IMAGE_TAG, registerImageBehavior, } from './behaviors/image.js';
|
|
84
|
+
export { foldInputAccessoryViewPayload, INPUT_ACCESSORY_VIEW_TAG, registerInputAccessoryViewBehavior, } from './behaviors/input-accessory-view.js';
|
|
85
|
+
export { registerTextInputBehavior, buildTextInputHandle, TEXT_INPUT_TAG, TEXT_INPUT_MULTILINE_TAG, } from './behaviors/text-input.js';
|
|
86
|
+
export { registerSwitchBehavior, SWITCH_TAG } from './behaviors/switch.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a primitive tag to the intrinsic it should actually commit, given its creation props.
|
|
3
|
+
*
|
|
4
|
+
* A tag with no `intrinsicWhen` — every primitive but `TextInput` today — passes through by
|
|
5
|
+
* identity, so a renderer may call this unconditionally without paying a lookup per prop.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveIntrinsicTag(tag: string, props: Readonly<Record<string, unknown>> | undefined): string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// The RUNTIME half of `HOST_PRIMITIVES.intrinsicWhen` — choosing WHICH native view a primitive
|
|
2
|
+
// commits, from the props it was created with.
|
|
3
|
+
//
|
|
4
|
+
// Why this has to exist at runtime at all. `intrinsicWhen` lets one primitive map to two native
|
|
5
|
+
// views (`TextInput` -> `RCTSinglelineTextInputView` / `RCTMultilineTextInputView`), and until now
|
|
6
|
+
// only the lowering transforms read it — so the choice was made at COMPILE time, from the source
|
|
7
|
+
// text. That is why `dynamicIntrinsicChoice` had to be a refusal category: a transform seeing
|
|
8
|
+
// `multiline={isLong}` cannot know the value, so it refused to lower the element at all.
|
|
9
|
+
//
|
|
10
|
+
// A public primitive TAG has no transform in front of it on three of the five adapters, so the
|
|
11
|
+
// choice has to be made where the value is actually known — here, at element creation. That is a
|
|
12
|
+
// capability the compiler never had, and it is why the refusal category disappears WITH the
|
|
13
|
+
// transform rather than migrating into it.
|
|
14
|
+
//
|
|
15
|
+
// WHAT THIS DELIBERATELY DOES NOT DO: react to the prop CHANGING later. A node's native view is
|
|
16
|
+
// fixed at `createNode` and no prop write moves a node between view types, so flipping `multiline`
|
|
17
|
+
// on a mounted input needs the node re-created — exactly as a browser re-initialises on an
|
|
18
|
+
// `<input type>` change. Re-creation is the renderer's business, not this function's; until a
|
|
19
|
+
// renderer implements it, a runtime flip keeps the view it was created with.
|
|
20
|
+
import { HOST_PRIMITIVES } from '../host-primitives.cjs';
|
|
21
|
+
const CHOICE_BY_BASE_TAG = new Map(Object.values(HOST_PRIMITIVES).flatMap((primitive) => primitive.intrinsicWhen === undefined
|
|
22
|
+
? []
|
|
23
|
+
: [
|
|
24
|
+
[
|
|
25
|
+
primitive.intrinsic,
|
|
26
|
+
{
|
|
27
|
+
prop: primitive.intrinsicWhen.prop,
|
|
28
|
+
alternate: primitive.intrinsicWhen.intrinsic,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
]));
|
|
32
|
+
/**
|
|
33
|
+
* Resolve a primitive tag to the intrinsic it should actually commit, given its creation props.
|
|
34
|
+
*
|
|
35
|
+
* A tag with no `intrinsicWhen` — every primitive but `TextInput` today — passes through by
|
|
36
|
+
* identity, so a renderer may call this unconditionally without paying a lookup per prop.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveIntrinsicTag(tag, props) {
|
|
39
|
+
const choice = CHOICE_BY_BASE_TAG.get(tag);
|
|
40
|
+
if (choice === undefined || props === undefined)
|
|
41
|
+
return tag;
|
|
42
|
+
// Explicit rather than truthy, and the string form is not decoration: a template-driven adapter
|
|
43
|
+
// can deliver `multiline="true"` as an ATTRIBUTE STRING (Angular's `multiline="true"` vs
|
|
44
|
+
// `[multiline]="true"`), and plain truthiness would then read `multiline="false"` — also a
|
|
45
|
+
// non-empty string — as multiline. An absent prop keeps the base tag.
|
|
46
|
+
return props[choice.prop] === true || props[choice.prop] === 'true'
|
|
47
|
+
? choice.alternate
|
|
48
|
+
: tag;
|
|
49
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ISymbioteEvent } from '@symbiote-native/engine';
|
|
2
2
|
export declare const DEFAULT_DELAY_LONG_PRESS_MS = 500;
|
|
3
|
+
export declare const DEFAULT_MIN_PRESS_DURATION_MS = 130;
|
|
3
4
|
export declare const DEFAULT_PRESS_RECT_OFFSETS: {
|
|
4
5
|
top: number;
|
|
5
6
|
left: number;
|
|
@@ -56,19 +57,26 @@ export interface IPressRuntime {
|
|
|
56
57
|
longPressCancel: (() => void) | undefined;
|
|
57
58
|
longPressFired: boolean;
|
|
58
59
|
pressDelayCancel: (() => void) | undefined;
|
|
60
|
+
pressOutCancel: (() => void) | undefined;
|
|
59
61
|
pressOrigin: {
|
|
60
62
|
x: number;
|
|
61
63
|
y: number;
|
|
62
64
|
} | undefined;
|
|
63
65
|
driftedOut: boolean;
|
|
64
66
|
region: IResponderRegion | undefined;
|
|
67
|
+
active: boolean;
|
|
68
|
+
activatedAt: number | undefined;
|
|
69
|
+
delayElapsed: boolean;
|
|
70
|
+
disposed: boolean;
|
|
65
71
|
}
|
|
66
72
|
export declare function createPressRuntime(): IPressRuntime;
|
|
73
|
+
export declare function disposePressRuntime(runtime: IPressRuntime): void;
|
|
67
74
|
export type IFrameCallback = (x: number, y: number, width: number, height: number, pageX: number, pageY: number) => void;
|
|
68
75
|
export interface IPressHost {
|
|
69
76
|
setPressed: (pressed: boolean) => void;
|
|
70
77
|
getMeasureFn: () => ((callback: IFrameCallback) => void) | undefined;
|
|
71
78
|
schedule: (callback: () => void, ms: number) => () => void;
|
|
79
|
+
now: () => number;
|
|
72
80
|
}
|
|
73
81
|
export interface IPressMachineConfig {
|
|
74
82
|
onPress?: IPressHandler;
|
|
@@ -78,6 +86,7 @@ export interface IPressMachineConfig {
|
|
|
78
86
|
onLongPress?: IPressHandler;
|
|
79
87
|
delayLongPress: number;
|
|
80
88
|
unstable_pressDelay: number;
|
|
89
|
+
minPressDuration?: number;
|
|
81
90
|
hitSlop?: IRectOffset;
|
|
82
91
|
pressRetentionOffset?: IRectOffset;
|
|
83
92
|
}
|
package/build/state/pressable.js
CHANGED
|
@@ -11,9 +11,16 @@
|
|
|
11
11
|
// the decision of when each callback fires) is here, shared by every adapter.
|
|
12
12
|
import { dlog, Platform } from '@symbiote-native/engine';
|
|
13
13
|
export const DEFAULT_DELAY_LONG_PRESS_MS = 500;
|
|
14
|
+
// Pressability's default active-visual floor for a plain Pressable. Touchable* overrides this to 0.
|
|
15
|
+
export const DEFAULT_MIN_PRESS_DURATION_MS = 130;
|
|
14
16
|
// RN's default extra slop kept around a press once it is active, before a drift fires pressOut.
|
|
15
17
|
// The PressRect extension (Pressability.js DEFAULT_PRESS_RECT_OFFSETS). Per-edge, deeper bottom.
|
|
16
|
-
export const DEFAULT_PRESS_RECT_OFFSETS = {
|
|
18
|
+
export const DEFAULT_PRESS_RECT_OFFSETS = {
|
|
19
|
+
top: 20,
|
|
20
|
+
left: 20,
|
|
21
|
+
bottom: 30,
|
|
22
|
+
right: 20,
|
|
23
|
+
};
|
|
17
24
|
// ---- geometry (pure) --------------------------------------------------------------------------
|
|
18
25
|
// A rect offset is a per-edge object (not the scalar shorthand). Reads the asymmetric form
|
|
19
26
|
// without a cast.
|
|
@@ -58,7 +65,12 @@ export function readPoint(event) {
|
|
|
58
65
|
export function computeRegion(width, height, pageX, pageY) {
|
|
59
66
|
if (!width && !height && !pageX && !pageY)
|
|
60
67
|
return undefined;
|
|
61
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
left: pageX,
|
|
70
|
+
top: pageY,
|
|
71
|
+
right: pageX + width,
|
|
72
|
+
bottom: pageY + height,
|
|
73
|
+
};
|
|
62
74
|
}
|
|
63
75
|
// Build the Android native-feedback prop the inner View carries from the ripple config. RN runs
|
|
64
76
|
// the color through processColor → a native int; we have no native bridge in JS, so we keep the
|
|
@@ -81,11 +93,40 @@ export function createPressRuntime() {
|
|
|
81
93
|
longPressCancel: undefined,
|
|
82
94
|
longPressFired: false,
|
|
83
95
|
pressDelayCancel: undefined,
|
|
96
|
+
pressOutCancel: undefined,
|
|
84
97
|
pressOrigin: undefined,
|
|
85
98
|
driftedOut: false,
|
|
86
99
|
region: undefined,
|
|
100
|
+
active: false,
|
|
101
|
+
activatedAt: undefined,
|
|
102
|
+
delayElapsed: false,
|
|
103
|
+
disposed: false,
|
|
87
104
|
};
|
|
88
105
|
}
|
|
106
|
+
function cancelRuntimeTimer(runtime, key) {
|
|
107
|
+
const cancel = runtime[key];
|
|
108
|
+
if (cancel === undefined)
|
|
109
|
+
return;
|
|
110
|
+
cancel();
|
|
111
|
+
runtime[key] = undefined;
|
|
112
|
+
}
|
|
113
|
+
// RN Pressability.reset(): cancel every timer and make any already-queued callback inert. The
|
|
114
|
+
// adapter invokes this exactly once from its own unmount/destroy hook.
|
|
115
|
+
export function disposePressRuntime(runtime) {
|
|
116
|
+
if (runtime.disposed)
|
|
117
|
+
return;
|
|
118
|
+
runtime.disposed = true;
|
|
119
|
+
cancelRuntimeTimer(runtime, 'longPressCancel');
|
|
120
|
+
cancelRuntimeTimer(runtime, 'pressDelayCancel');
|
|
121
|
+
cancelRuntimeTimer(runtime, 'pressOutCancel');
|
|
122
|
+
runtime.longPressFired = false;
|
|
123
|
+
runtime.pressOrigin = undefined;
|
|
124
|
+
runtime.driftedOut = false;
|
|
125
|
+
runtime.region = undefined;
|
|
126
|
+
runtime.active = false;
|
|
127
|
+
runtime.activatedAt = undefined;
|
|
128
|
+
runtime.delayElapsed = false;
|
|
129
|
+
}
|
|
89
130
|
// Measure the responder's on-screen frame and cache it as the retention region for the life of
|
|
90
131
|
// the press (RN measures on responder grant, _measureResponderRegion). When measure is
|
|
91
132
|
// unavailable (no node yet, an uncommitted node, or a host slot without a measure method,
|
|
@@ -113,7 +154,7 @@ function measureRegion(runtime, measureFn) {
|
|
|
113
154
|
// (the React adapter's useMemo body) lives here, shared by every adapter. The adapter rebuilds
|
|
114
155
|
// these per render (the closures capture the live config) while the runtime persists across them.
|
|
115
156
|
export function createPressHandlers(config, runtime, host) {
|
|
116
|
-
const { onPress, onPressIn, onPressOut, onPressMove, onLongPress, delayLongPress, unstable_pressDelay, } = config;
|
|
157
|
+
const { onPress, onPressIn, onPressOut, onPressMove, onLongPress, delayLongPress, unstable_pressDelay, minPressDuration = DEFAULT_MIN_PRESS_DURATION_MS, } = config;
|
|
117
158
|
// Per-edge offsets for the measured-rect retention test (RN's hitSlop + pressRectOffset).
|
|
118
159
|
// pressRetentionOffset defaults to RN's DEFAULT_PRESS_RECT_OFFSETS when unset; hitSlop to zero.
|
|
119
160
|
const hitSlopRect = normalizeRect(config.hitSlop);
|
|
@@ -132,23 +173,31 @@ export function createPressHandlers(config, runtime, host) {
|
|
|
132
173
|
const origin = runtime.pressOrigin;
|
|
133
174
|
if (origin === undefined)
|
|
134
175
|
return true;
|
|
135
|
-
return Math.hypot(point.x - origin.x, point.y - origin.y) <= fallbackThreshold;
|
|
176
|
+
return (Math.hypot(point.x - origin.x, point.y - origin.y) <= fallbackThreshold);
|
|
136
177
|
}
|
|
137
178
|
function clearLongPress() {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
179
|
+
cancelRuntimeTimer(runtime, 'longPressCancel');
|
|
180
|
+
}
|
|
181
|
+
function clearPressDelay() {
|
|
182
|
+
cancelRuntimeTimer(runtime, 'pressDelayCancel');
|
|
183
|
+
}
|
|
184
|
+
function clearPressOut() {
|
|
185
|
+
cancelRuntimeTimer(runtime, 'pressOutCancel');
|
|
142
186
|
}
|
|
143
|
-
// The real activation: flip pressed-state on, arm the long-press timer, fire onPressIn. Split
|
|
144
|
-
// out so unstable_pressDelay can defer it behind a timer while an early release flushes it.
|
|
145
187
|
function activate(event) {
|
|
188
|
+
if (runtime.disposed || runtime.active || runtime.driftedOut)
|
|
189
|
+
return;
|
|
190
|
+
// A new grant or drift-back-in supersedes a delayed out from the previous active interval.
|
|
191
|
+
clearPressOut();
|
|
192
|
+
runtime.active = true;
|
|
193
|
+
runtime.activatedAt = host.now();
|
|
146
194
|
dlog('Pressable pressIn');
|
|
147
195
|
host.setPressed(true);
|
|
148
|
-
runtime.longPressFired
|
|
149
|
-
if (onLongPress) {
|
|
196
|
+
if (!runtime.longPressFired && onLongPress) {
|
|
150
197
|
runtime.longPressCancel = host.schedule(() => {
|
|
151
198
|
runtime.longPressCancel = undefined;
|
|
199
|
+
if (runtime.disposed || !runtime.active || runtime.driftedOut)
|
|
200
|
+
return;
|
|
152
201
|
runtime.longPressFired = true;
|
|
153
202
|
dlog('Pressable longPress timer fired');
|
|
154
203
|
onLongPress(event);
|
|
@@ -156,51 +205,99 @@ export function createPressHandlers(config, runtime, host) {
|
|
|
156
205
|
}
|
|
157
206
|
onPressIn?.(event);
|
|
158
207
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
208
|
+
function deactivate(event) {
|
|
209
|
+
if (runtime.disposed || !runtime.active)
|
|
210
|
+
return;
|
|
211
|
+
runtime.active = false;
|
|
212
|
+
clearLongPress();
|
|
213
|
+
const activatedAt = runtime.activatedAt ?? host.now();
|
|
214
|
+
runtime.activatedAt = undefined;
|
|
215
|
+
const heldFor = Math.max(0, host.now() - activatedAt);
|
|
216
|
+
const wait = Math.max(minPressDuration - heldFor, 0);
|
|
217
|
+
const finish = () => {
|
|
218
|
+
runtime.pressOutCancel = undefined;
|
|
219
|
+
if (runtime.disposed)
|
|
220
|
+
return;
|
|
221
|
+
host.setPressed(false);
|
|
222
|
+
onPressOut?.(event);
|
|
223
|
+
};
|
|
224
|
+
if (wait > 0) {
|
|
225
|
+
dlog(`Pressable pressOut deferred ${wait}ms`);
|
|
226
|
+
runtime.pressOutCancel = host.schedule(finish, wait);
|
|
166
227
|
}
|
|
228
|
+
else {
|
|
229
|
+
finish();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// An honest release arrives as press then pressOut. If it beat delayPressIn, activate now so the
|
|
233
|
+
// tap still flashes and calls onPressIn; a cancelled/out-of-bounds gesture never calls this path.
|
|
234
|
+
function completePressDelay(event) {
|
|
235
|
+
if (runtime.pressDelayCancel !== undefined)
|
|
236
|
+
clearPressDelay();
|
|
237
|
+
if (!runtime.delayElapsed)
|
|
238
|
+
runtime.delayElapsed = true;
|
|
239
|
+
if (!runtime.driftedOut)
|
|
240
|
+
activate(event);
|
|
241
|
+
}
|
|
242
|
+
function resetGesture() {
|
|
243
|
+
runtime.pressOrigin = undefined;
|
|
244
|
+
runtime.region = undefined;
|
|
245
|
+
runtime.driftedOut = false;
|
|
246
|
+
runtime.delayElapsed = false;
|
|
247
|
+
runtime.longPressFired = false;
|
|
167
248
|
}
|
|
168
249
|
return {
|
|
169
250
|
handlePressIn(event) {
|
|
251
|
+
if (runtime.disposed)
|
|
252
|
+
return;
|
|
253
|
+
// A new grant cancels a delayed out from the prior gesture, matching Pressability's
|
|
254
|
+
// onResponderGrant -> _cancelPressOutDelayTimeout.
|
|
255
|
+
clearPressDelay();
|
|
256
|
+
clearLongPress();
|
|
257
|
+
clearPressOut();
|
|
258
|
+
runtime.active = false;
|
|
259
|
+
runtime.activatedAt = undefined;
|
|
260
|
+
runtime.longPressFired = false;
|
|
261
|
+
runtime.delayElapsed = false;
|
|
170
262
|
runtime.pressOrigin = readPoint(event);
|
|
171
263
|
runtime.driftedOut = false;
|
|
172
|
-
// Measure now so the move stream tests against the real frame, not a press-start radius.
|
|
173
264
|
measureRegion(runtime, host.getMeasureFn());
|
|
174
265
|
if (unstable_pressDelay > 0) {
|
|
175
266
|
dlog(`Pressable pressIn deferred ${unstable_pressDelay}ms`);
|
|
176
267
|
runtime.pressDelayCancel = host.schedule(() => {
|
|
177
268
|
runtime.pressDelayCancel = undefined;
|
|
178
|
-
|
|
269
|
+
if (runtime.disposed)
|
|
270
|
+
return;
|
|
271
|
+
runtime.delayElapsed = true;
|
|
272
|
+
if (!runtime.driftedOut)
|
|
273
|
+
activate(event);
|
|
179
274
|
}, unstable_pressDelay);
|
|
180
275
|
return;
|
|
181
276
|
}
|
|
277
|
+
runtime.delayElapsed = true;
|
|
182
278
|
activate(event);
|
|
183
279
|
},
|
|
184
280
|
handlePressOut(event) {
|
|
281
|
+
if (runtime.disposed)
|
|
282
|
+
return;
|
|
185
283
|
dlog('Pressable pressOut');
|
|
186
|
-
|
|
284
|
+
// A cancellation before delayPressIn must stay inert. An honest release already flushed the
|
|
285
|
+
// delay in handlePress, because the engine emits press immediately before pressOut.
|
|
286
|
+
clearPressDelay();
|
|
187
287
|
clearLongPress();
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
host.setPressed(false);
|
|
191
|
-
onPressOut?.(event);
|
|
288
|
+
deactivate(event);
|
|
289
|
+
resetGesture();
|
|
192
290
|
},
|
|
193
291
|
handlePress(event) {
|
|
292
|
+
if (runtime.disposed)
|
|
293
|
+
return;
|
|
194
294
|
dlog('Pressable press');
|
|
195
|
-
|
|
295
|
+
completePressDelay(event);
|
|
196
296
|
clearLongPress();
|
|
197
|
-
// A drift past the retention region cancels the tap (RN); reset and suppress.
|
|
198
297
|
if (runtime.driftedOut) {
|
|
199
|
-
runtime.driftedOut = false;
|
|
200
298
|
dlog('Pressable press suppressed by drift past retention region');
|
|
201
299
|
return;
|
|
202
300
|
}
|
|
203
|
-
// A fired long press cancels the tap: reset the flag and suppress onPress.
|
|
204
301
|
if (runtime.longPressFired) {
|
|
205
302
|
runtime.longPressFired = false;
|
|
206
303
|
dlog('Pressable press suppressed by prior longPress');
|
|
@@ -208,11 +305,9 @@ export function createPressHandlers(config, runtime, host) {
|
|
|
208
305
|
}
|
|
209
306
|
onPress?.(event);
|
|
210
307
|
},
|
|
211
|
-
// Responder-move stream of the Pressable's own View. onPressMove fires on every move while
|
|
212
|
-
// the press is live (RN). Then the retention test: a move outside the measured region
|
|
213
|
-
// (expanded by hitSlop+pressRectOffset) fires an early pressOut and marks the tap suppressed;
|
|
214
|
-
// a move back inside re-activates.
|
|
215
308
|
handleResponderMove(event) {
|
|
309
|
+
if (runtime.disposed)
|
|
310
|
+
return;
|
|
216
311
|
onPressMove?.(event);
|
|
217
312
|
const here = readPoint(event);
|
|
218
313
|
if (!here)
|
|
@@ -222,14 +317,15 @@ export function createPressHandlers(config, runtime, host) {
|
|
|
222
317
|
dlog('Pressable drifted past retention region — deactivating');
|
|
223
318
|
runtime.driftedOut = true;
|
|
224
319
|
clearLongPress();
|
|
225
|
-
|
|
226
|
-
|
|
320
|
+
// Before delayPressIn there was no activation, so there is no onPressOut to emit.
|
|
321
|
+
deactivate(event);
|
|
227
322
|
}
|
|
228
323
|
}
|
|
229
324
|
else if (runtime.driftedOut) {
|
|
230
325
|
dlog('Pressable returned inside retention region — reactivating');
|
|
231
326
|
runtime.driftedOut = false;
|
|
232
|
-
|
|
327
|
+
if (runtime.delayElapsed)
|
|
328
|
+
activate(event);
|
|
233
329
|
}
|
|
234
330
|
},
|
|
235
331
|
};
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// ./view/render-scroll-sticky; this module composes them into the ordered transition every adapter
|
|
16
16
|
// shares. Ported from ScrollViewStickyHeader.js's effect.
|
|
17
17
|
import { dlog } from '@symbiote-native/engine';
|
|
18
|
-
import { computeStickyInterpolation, stickyDebounceMs } from '../view/render-scroll-sticky.js';
|
|
18
|
+
import { computeStickyInterpolation, stickyDebounceMs, } from '../view/render-scroll-sticky.js';
|
|
19
19
|
// The un-measured identity interpolation (RN: a fresh AnimatedInterpolation before the header has
|
|
20
20
|
// measured its own y/height). Kept as the reset the initial state and every rebuild start from.
|
|
21
21
|
const IDENTITY_INPUT_RANGE = [-1, 0];
|
|
@@ -68,7 +68,9 @@ function headerTag(state) {
|
|
|
68
68
|
}
|
|
69
69
|
export function reduceSticky(state, action, inputs) {
|
|
70
70
|
dlog(`STICKY[reducer ${headerTag(state)}] action=${action.kind}` +
|
|
71
|
-
(action.kind === 'layout'
|
|
71
|
+
(action.kind === 'layout'
|
|
72
|
+
? ` y=${action.y} height=${action.height}`
|
|
73
|
+
: '') +
|
|
72
74
|
(action.kind === 'animated-tick' || action.kind === 'debounce-fired'
|
|
73
75
|
? ` value=${action.value}`
|
|
74
76
|
: '') +
|
|
@@ -88,13 +90,19 @@ export function reduceSticky(state, action, inputs) {
|
|
|
88
90
|
// native-driven rebuild (fresh AnimatedProps/AnimatedStyle graph, fresh native connect) commit a
|
|
89
91
|
// fresh prop identity on every redundant layout, which can itself provoke another relayout pass —
|
|
90
92
|
// an unbounded same-tick rebuild ping-pong that trips Svelte's effect_update_depth_exceeded guard.
|
|
91
|
-
const alreadyAtThisGeometry = state.measured &&
|
|
93
|
+
const alreadyAtThisGeometry = state.measured &&
|
|
94
|
+
state.layoutY === action.y &&
|
|
95
|
+
state.layoutHeight === action.height;
|
|
92
96
|
state.layoutY = action.y;
|
|
93
97
|
state.layoutHeight = action.height;
|
|
94
98
|
state.measured = true;
|
|
95
99
|
const effects = [];
|
|
96
100
|
if (inputs.index !== undefined) {
|
|
97
|
-
effects.push({
|
|
101
|
+
effects.push({
|
|
102
|
+
kind: 'record-header-y',
|
|
103
|
+
index: inputs.index,
|
|
104
|
+
y: action.y,
|
|
105
|
+
});
|
|
98
106
|
}
|
|
99
107
|
if (alreadyAtThisGeometry && state.rangesEmitted) {
|
|
100
108
|
dlog(`STICKY[reducer ${headerTag(state)}] layout: redundant geometry, skipped rebuild`);
|
|
@@ -161,12 +169,27 @@ export function reduceSticky(state, action, inputs) {
|
|
|
161
169
|
return {
|
|
162
170
|
state,
|
|
163
171
|
effects: [
|
|
164
|
-
{
|
|
172
|
+
{
|
|
173
|
+
kind: 'schedule-debounce',
|
|
174
|
+
delay: stickyDebounceMs(inputs.os),
|
|
175
|
+
value: action.value,
|
|
176
|
+
},
|
|
165
177
|
],
|
|
166
178
|
changed: false,
|
|
167
179
|
};
|
|
168
180
|
}
|
|
169
181
|
case 'debounce-fired': {
|
|
182
|
+
// Already sitting at this value: emit nothing. Same bail-out the 'layout' case above spells
|
|
183
|
+
// out, for the same reason - React's own header gets it free from setTranslateY (an unchanged
|
|
184
|
+
// primitive bails out of the re-render), a reducer has to say it. Device-confirmed 2026-08-18:
|
|
185
|
+
// without it a re-arriving settled value emits apply-passthrough, the adapter force-renders,
|
|
186
|
+
// the passthrough prop gets a fresh identity, the animated graph reconnects and re-emits into
|
|
187
|
+
// another tick -> another debounce -> another passthrough. One header survives it; a screen of
|
|
188
|
+
// 200 trips React's "Maximum update depth exceeded" and takes the app down.
|
|
189
|
+
if (state.translateY === action.value) {
|
|
190
|
+
dlog(`STICKY[reducer ${headerTag(state)}] debounce-fired: already at translateY=${action.value}, no-op`);
|
|
191
|
+
return { state, effects: [], changed: false };
|
|
192
|
+
}
|
|
170
193
|
// The debounce completed: commit the settled translateY. Once a NON-zero value commits, re-arm
|
|
171
194
|
// the swallow gate so the next interpolation rebuild's spurious 0 is dropped (RN).
|
|
172
195
|
dlog(`STICKY[reducer ${headerTag(state)}] debounce-fired: committing translateY=${action.value}`);
|
package/build/state/switch.js
CHANGED
|
@@ -27,5 +27,5 @@ export function valueFromChange(event) {
|
|
|
27
27
|
// retained tree never diverges and nothing re-commits. The imperative command is the only
|
|
28
28
|
// correction path. No report yet (null) or agreement → no command.
|
|
29
29
|
export function shouldSnapBack(state, fabricValue) {
|
|
30
|
-
return state.lastNativeReport !== null && state.lastNativeReport !== fabricValue;
|
|
30
|
+
return (state.lastNativeReport !== null && state.lastNativeReport !== fabricValue);
|
|
31
31
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ISymbioteEvent, ITextStyle } from '@symbiote-native/engine';
|
|
1
|
+
import type { IMeasureOnSuccess, IMeasureInWindowOnSuccess, IMeasureLayoutOnSuccess, ISymbioteNode, IPlatformOSType, ISymbioteEvent, ITextStyle } from '@symbiote-native/engine';
|
|
2
2
|
import type { IAccessibilityProps, IAriaProps } from '../accessibility-props';
|
|
3
3
|
export type IInputMode = 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
|
|
4
4
|
export type IEnterKeyHint = 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send';
|
|
@@ -10,6 +10,7 @@ export type ITextInputSelection = {
|
|
|
10
10
|
export type ITextInputEventHandler = (event: ISymbioteEvent) => void;
|
|
11
11
|
export declare const INITIAL_EVENT_COUNT = 0;
|
|
12
12
|
export declare const SELECTION_NONE = -1;
|
|
13
|
+
export declare function keyboardTypeForInputMode(inputMode: string, os: IPlatformOSType): string | undefined;
|
|
13
14
|
export declare function mapAutoComplete(map: Record<string, string>, token: string): string | undefined;
|
|
14
15
|
export declare function foldAutoComplete(token: string | undefined): {
|
|
15
16
|
autoComplete: string | undefined;
|
|
@@ -94,10 +95,15 @@ export type ITextInputProps = IAccessibilityProps & IAriaProps & {
|
|
|
94
95
|
onSelectionChange?: ITextInputEventHandler;
|
|
95
96
|
onContentSizeChange?: ITextInputEventHandler;
|
|
96
97
|
};
|
|
98
|
+
export declare const TEXT_INPUT_CALLBACK_NAMES: readonly string[];
|
|
97
99
|
export type ITextInputHandle = {
|
|
98
100
|
focus(): void;
|
|
99
101
|
blur(): void;
|
|
100
102
|
clear(): void;
|
|
101
103
|
isFocused(): boolean;
|
|
102
104
|
setSelection(start: number, end: number): void;
|
|
105
|
+
measure(callback: IMeasureOnSuccess): void;
|
|
106
|
+
measureInWindow(callback: IMeasureInWindowOnSuccess): void;
|
|
107
|
+
measureLayout(relativeToNativeNode: ISymbioteNode | number, onSuccess: IMeasureLayoutOnSuccess, onFail?: () => void): void;
|
|
108
|
+
setNativeProps(nativeProps: Record<string, unknown>): void;
|
|
103
109
|
};
|