@symbiote-native/components 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -9
- package/build/accessibility-props.d.ts +1 -1
- package/build/accessibility-props.js +2 -2
- package/build/behaviors/activity-indicator/shared.js +41 -70
- package/build/behaviors/button.d.ts +11 -0
- package/build/behaviors/button.js +101 -89
- package/build/behaviors/image-background.d.ts +1 -0
- package/build/behaviors/image-background.js +97 -81
- package/build/behaviors/image.d.ts +0 -1
- package/build/behaviors/image.js +24 -105
- package/build/behaviors/input-accessory-view.d.ts +0 -1
- package/build/behaviors/input-accessory-view.js +47 -53
- package/build/behaviors/pressable.d.ts +1 -1
- package/build/behaviors/pressable.js +68 -97
- package/build/behaviors/refresh-control.js +15 -2
- package/build/behaviors/scroll-view/index.android.js +25 -37
- package/build/behaviors/scroll-view/index.d.ts +1 -0
- package/build/behaviors/scroll-view/index.js +3 -0
- package/build/behaviors/scroll-view/responder.d.ts +4 -0
- package/build/behaviors/scroll-view/responder.js +202 -0
- package/build/behaviors/scroll-view/shared.d.ts +1 -3
- package/build/behaviors/scroll-view/shared.js +95 -95
- package/build/behaviors/scroll-view/sticky.d.ts +1 -0
- package/build/behaviors/scroll-view/sticky.js +62 -49
- package/build/behaviors/switch.js +43 -86
- package/build/behaviors/text-input.js +219 -107
- package/build/behaviors/touchable-highlight.js +74 -61
- package/build/behaviors/touchable-native-feedback.js +43 -122
- package/build/behaviors/touchable-opacity.js +71 -59
- package/build/behaviors/touchable-without-feedback.js +35 -100
- package/build/component-names/index.android.js +7 -11
- package/build/component-names/index.ios.js +0 -7
- package/build/component-names/shared.d.ts +1 -1
- package/build/index.d.ts +15 -21
- package/build/index.js +21 -28
- package/build/resolve-intrinsic.js +3 -9
- package/build/scroll-view-commands.d.ts +1 -9
- package/build/scroll-view-commands.js +13 -74
- package/build/state/flat-list.d.ts +2 -2
- package/build/state/flat-list.js +10 -2
- package/build/state/pressable.d.ts +6 -1
- package/build/state/pressable.js +63 -28
- package/build/state/section-list.d.ts +2 -0
- package/build/state/section-list.js +14 -7
- package/build/state/text-input.d.ts +7 -40
- package/build/state/text-input.js +17 -186
- package/build/state/touchable.d.ts +1 -0
- package/build/state/touchable.js +11 -8
- package/build/state/virtualized-list-reducer.d.ts +2 -2
- package/build/state/virtualized-list.d.ts +6 -6
- package/build/state/virtualized-list.js +71 -37
- package/build/text-props.d.ts +0 -8
- package/build/text-props.js +14 -25
- package/build/view/render-button.d.ts +1 -29
- package/build/view/render-button.js +44 -81
- package/build/view/render-image/index.d.ts +14 -1
- package/build/view/render-image/index.js +22 -147
- package/build/view/render-input-accessory-view.d.ts +1 -5
- package/build/view/render-input-accessory-view.js +26 -48
- package/build/view/render-keyboard-avoiding-view.d.ts +7 -1
- package/build/view/render-keyboard-avoiding-view.js +40 -1
- package/build/view/render-modal.d.ts +1 -1
- package/build/view/render-modal.js +15 -5
- package/build/view/render-pressable/index.d.ts +1 -0
- package/build/view/render-pressable/index.js +4 -0
- package/build/view/render-scroll-view.d.ts +0 -4
- package/build/view/render-scroll-view.js +3 -49
- package/build/view/render-switch.d.ts +0 -14
- package/build/view/render-switch.js +4 -42
- package/build/view/render-touchable-highlight.d.ts +1 -0
- package/build/view/render-touchable-native-feedback.d.ts +0 -1
- package/build/view/render-touchable-native-feedback.js +15 -9
- package/host-primitives.cjs +49 -203
- package/host-primitives.d.cts +0 -1
- package/package.json +3 -7
- package/build/fold-host-bag.d.ts +0 -15
- package/build/fold-host-bag.js +0 -99
- package/build/view/render-text-input.d.ts +0 -11
- package/build/view/render-text-input.js +0 -39
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type IViewableItemsChangedInfo, type IViewToken } from './virtualized-list';
|
|
2
2
|
export declare const SINGLE_COLUMN = 1;
|
|
3
3
|
export interface IRow<ItemT> {
|
|
4
4
|
items: ItemT[];
|
|
5
5
|
startIndex: number;
|
|
6
6
|
}
|
|
7
7
|
export declare function chunkIntoRows<ItemT>(data: readonly ItemT[], columns: number): IRow<ItemT>[];
|
|
8
|
-
export declare function rowKeyExtractor<ItemT>(row: IRow<ItemT
|
|
8
|
+
export declare function rowKeyExtractor<ItemT>(row: IRow<ItemT>, keyExtractor?: (item: ItemT, index: number) => string): string;
|
|
9
9
|
export declare function expandRowToken<ItemT>(token: IViewToken<IRow<ItemT>>, keyExtractor?: (item: ItemT, index: number) => string): IViewToken<ItemT>[];
|
|
10
10
|
export declare function expandRowViewability<ItemT>(info: IViewableItemsChangedInfo<IRow<ItemT>>, keyExtractor?: (item: ItemT, index: number) => string): IViewableItemsChangedInfo<ItemT>;
|
|
11
11
|
export declare function lastItemOfRow<ItemT>(row: IRow<ItemT> | undefined): ItemT | undefined;
|
package/build/state/flat-list.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// back to per-item tokens, and the row separator unwraps to the flanking items. All of
|
|
5
5
|
// this is pure transform — every adapter (React, Vue) reuses it; the adapter supplies
|
|
6
6
|
// only the element creation (createElement / h) for a row and the ref wiring.
|
|
7
|
+
import { defaultKeyExtractor, } from './virtualized-list.js';
|
|
7
8
|
export const SINGLE_COLUMN = 1;
|
|
8
9
|
export function chunkIntoRows(data, columns) {
|
|
9
10
|
const rows = [];
|
|
@@ -12,8 +13,15 @@ export function chunkIntoRows(data, columns) {
|
|
|
12
13
|
}
|
|
13
14
|
return rows;
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
// RN's own multi-column row key (`FlatList.js`'s `_keyExtractor`): each item's own key, joined
|
|
17
|
+
// with `:`, never a synthetic position string — an app's `keyExtractor` (or the object's own
|
|
18
|
+
// `key`/`id`) is what keeps a row's identity stable across inserts/removes, and a `row-${index}`
|
|
19
|
+
// key defeats that as surely as no key at all.
|
|
20
|
+
export function rowKeyExtractor(row, keyExtractor) {
|
|
21
|
+
const resolve = keyExtractor ?? defaultKeyExtractor;
|
|
22
|
+
return row.items
|
|
23
|
+
.map((item, column) => resolve(item, row.startIndex + column))
|
|
24
|
+
.join(':');
|
|
17
25
|
}
|
|
18
26
|
// Expand a row's viewable token to one token per item in that row, all sharing the row's
|
|
19
27
|
// isViewable flag, so the caller sees item-level visibility rather than row-level.
|
|
@@ -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 LONG_PRESS_DEACTIVATION_DISTANCE = 10;
|
|
3
4
|
export declare const DEFAULT_MIN_PRESS_DURATION_MS = 130;
|
|
4
5
|
export declare const DEFAULT_PRESS_RECT_OFFSETS: {
|
|
5
6
|
top: number;
|
|
@@ -52,7 +53,6 @@ export declare function readPoint(event: ISymbioteEvent): {
|
|
|
52
53
|
y: number;
|
|
53
54
|
} | undefined;
|
|
54
55
|
export declare function computeRegion(width: number, height: number, pageX: number, pageY: number): IResponderRegion | undefined;
|
|
55
|
-
export declare function rippleProps(config: IPressableAndroidRippleConfig): Record<string, IRippleBackground> | undefined;
|
|
56
56
|
export interface IPressRuntime {
|
|
57
57
|
longPressCancel: (() => void) | undefined;
|
|
58
58
|
longPressFired: boolean;
|
|
@@ -62,6 +62,10 @@ export interface IPressRuntime {
|
|
|
62
62
|
x: number;
|
|
63
63
|
y: number;
|
|
64
64
|
} | undefined;
|
|
65
|
+
activatePosition: {
|
|
66
|
+
x: number;
|
|
67
|
+
y: number;
|
|
68
|
+
} | undefined;
|
|
65
69
|
driftedOut: boolean;
|
|
66
70
|
region: IResponderRegion | undefined;
|
|
67
71
|
active: boolean;
|
|
@@ -89,6 +93,7 @@ export interface IPressMachineConfig {
|
|
|
89
93
|
minPressDuration?: number;
|
|
90
94
|
hitSlop?: IRectOffset;
|
|
91
95
|
pressRetentionOffset?: IRectOffset;
|
|
96
|
+
android_disableSound?: boolean;
|
|
92
97
|
}
|
|
93
98
|
export interface IPressHandlers {
|
|
94
99
|
handlePressIn: IPressHandler;
|
package/build/state/pressable.js
CHANGED
|
@@ -9,8 +9,15 @@
|
|
|
9
9
|
// re-render, so each framework owns its reactive primitive) and the raw frame-measure (the host
|
|
10
10
|
// node plus its measure call). The rest (the timers, the geometry, the suppression flags,
|
|
11
11
|
// the decision of when each callback fires) is here, shared by every adapter.
|
|
12
|
-
import { dlog, Platform } from '@symbiote-native/engine';
|
|
12
|
+
import { dlog, Platform, SoundManager, } from '@symbiote-native/engine';
|
|
13
13
|
export const DEFAULT_DELAY_LONG_PRESS_MS = 500;
|
|
14
|
+
// Pressability.js's DEFAULT_LONG_PRESS_DEACTIVATION_DISTANCE. A SEPARATE, smaller radius than
|
|
15
|
+
// `pressRetentionOffset`/`hitSlop`: any move past it cancels a pending long press even while the
|
|
16
|
+
// finger is still well inside the retention rect — real presses jitter a few px, long-press must
|
|
17
|
+
// not fire mid-scroll. Vendor exposes it only as a rare static override
|
|
18
|
+
// (`Pressability.setLongPressDeactivationDistance`, used by e.g. gesture libraries), never as a
|
|
19
|
+
// per-component prop, so it stays a plain constant here rather than a config field.
|
|
20
|
+
export const LONG_PRESS_DEACTIVATION_DISTANCE = 10;
|
|
14
21
|
// Pressability's default active-visual floor for a plain Pressable. Touchable* overrides this to 0.
|
|
15
22
|
export const DEFAULT_MIN_PRESS_DURATION_MS = 130;
|
|
16
23
|
// RN's default extra slop kept around a press once it is active, before a drift fires pressOut.
|
|
@@ -72,22 +79,6 @@ export function computeRegion(width, height, pageX, pageY) {
|
|
|
72
79
|
bottom: pageY + height,
|
|
73
80
|
};
|
|
74
81
|
}
|
|
75
|
-
// Build the Android native-feedback prop the inner View carries from the ripple config. RN runs
|
|
76
|
-
// the color through processColor → a native int; we have no native bridge in JS, so we keep the
|
|
77
|
-
// string and let Android resolve it (a null color is the documented "no tint"). Inert on iOS.
|
|
78
|
-
export function rippleProps(config) {
|
|
79
|
-
if (Platform.OS !== 'android')
|
|
80
|
-
return undefined;
|
|
81
|
-
const background = {
|
|
82
|
-
type: 'RippleAndroid',
|
|
83
|
-
color: config.color ?? null,
|
|
84
|
-
borderless: config.borderless === true,
|
|
85
|
-
rippleRadius: config.radius,
|
|
86
|
-
};
|
|
87
|
-
return config.foreground === true
|
|
88
|
-
? { nativeForegroundAndroid: background }
|
|
89
|
-
: { nativeBackgroundAndroid: background };
|
|
90
|
-
}
|
|
91
82
|
export function createPressRuntime() {
|
|
92
83
|
return {
|
|
93
84
|
longPressCancel: undefined,
|
|
@@ -95,6 +86,7 @@ export function createPressRuntime() {
|
|
|
95
86
|
pressDelayCancel: undefined,
|
|
96
87
|
pressOutCancel: undefined,
|
|
97
88
|
pressOrigin: undefined,
|
|
89
|
+
activatePosition: undefined,
|
|
98
90
|
driftedOut: false,
|
|
99
91
|
region: undefined,
|
|
100
92
|
active: false,
|
|
@@ -154,7 +146,7 @@ function measureRegion(runtime, measureFn) {
|
|
|
154
146
|
// (the React adapter's useMemo body) lives here, shared by every adapter. The adapter rebuilds
|
|
155
147
|
// these per render (the closures capture the live config) while the runtime persists across them.
|
|
156
148
|
export function createPressHandlers(config, runtime, host) {
|
|
157
|
-
const { onPress, onPressIn, onPressOut, onPressMove, onLongPress, delayLongPress, unstable_pressDelay, minPressDuration = DEFAULT_MIN_PRESS_DURATION_MS, } = config;
|
|
149
|
+
const { onPress, onPressIn, onPressOut, onPressMove, onLongPress, delayLongPress, unstable_pressDelay, minPressDuration = DEFAULT_MIN_PRESS_DURATION_MS, android_disableSound, } = config;
|
|
158
150
|
// Per-edge offsets for the measured-rect retention test (RN's hitSlop + pressRectOffset).
|
|
159
151
|
// pressRetentionOffset defaults to RN's DEFAULT_PRESS_RECT_OFFSETS when unset; hitSlop to zero.
|
|
160
152
|
const hitSlopRect = normalizeRect(config.hitSlop);
|
|
@@ -165,6 +157,18 @@ export function createPressHandlers(config, runtime, host) {
|
|
|
165
157
|
const fallbackThreshold = maxEdge(hitSlopRect) + maxEdge(pressRectOffset);
|
|
166
158
|
// True iff the touch still belongs to the active press: against the measured rect when we have
|
|
167
159
|
// one (the RN-faithful path), else the symmetric radius fallback.
|
|
160
|
+
//
|
|
161
|
+
// TODO(rn-parity): `Pressability.js`'s `onResponderMove` (:489-493) returns right after the
|
|
162
|
+
// app's `onPressMove`, before any drift/long-press-jitter check, whenever the region is not yet
|
|
163
|
+
// measured — vendor treats "no measurement yet" as "do nothing until one arrives." The radius
|
|
164
|
+
// fallback below diverges: it judges drift/jitter on synthetic geometry instead of waiting.
|
|
165
|
+
// Confirmed unreachable on a real device (`Tree::measure` in `SymbioteTree.cpp` is a synchronous
|
|
166
|
+
// JSI call, so the region is always populated before `handleResponderMove` can fire), so the
|
|
167
|
+
// fallback only ever fires in this codebase's own test harness, which wires
|
|
168
|
+
// `getMeasureFn: () => undefined` everywhere (`state/pressable.test.ts:84` and every touchable
|
|
169
|
+
// test built on it). A real fix needs a synchronous mock-measure harness under 11+ existing
|
|
170
|
+
// drift/retention/long-press call sites across `pressable.test.ts`, `touchable-opacity.test.ts`
|
|
171
|
+
// and `render-pressable.test.ts`, not a one-line guard — see the audit skill for the finding.
|
|
168
172
|
function isWithinRetention(point) {
|
|
169
173
|
const region = runtime.region;
|
|
170
174
|
if (region !== undefined) {
|
|
@@ -184,6 +188,24 @@ export function createPressHandlers(config, runtime, host) {
|
|
|
184
188
|
function clearPressOut() {
|
|
185
189
|
cancelRuntimeTimer(runtime, 'pressOutCancel');
|
|
186
190
|
}
|
|
191
|
+
// Pressability.js arms this ONCE, at RESPONDER_GRANT, and a drift out/back-in never re-arms it
|
|
192
|
+
// (`onResponderMove` only ever CANCELS via `_cancelLongPressDelayTimeout`, permanently, for the
|
|
193
|
+
// rest of the gesture). Splitting it out of `activate()` is what makes that possible: `activate`
|
|
194
|
+
// itself runs again on every drift-back-in, and used to re-arm a fresh full-duration timer each
|
|
195
|
+
// time, which is wrong on two counts — it can resurrect a long press the finger already
|
|
196
|
+
// disqualified, and if it never left, it's simply the same timer restarted for no reason.
|
|
197
|
+
function armLongPress(event) {
|
|
198
|
+
if (!onLongPress)
|
|
199
|
+
return;
|
|
200
|
+
runtime.longPressCancel = host.schedule(() => {
|
|
201
|
+
runtime.longPressCancel = undefined;
|
|
202
|
+
if (runtime.disposed || !runtime.active || runtime.driftedOut)
|
|
203
|
+
return;
|
|
204
|
+
runtime.longPressFired = true;
|
|
205
|
+
dlog('Pressable longPress timer fired');
|
|
206
|
+
onLongPress(event);
|
|
207
|
+
}, delayLongPress + unstable_pressDelay);
|
|
208
|
+
}
|
|
187
209
|
function activate(event) {
|
|
188
210
|
if (runtime.disposed || runtime.active || runtime.driftedOut)
|
|
189
211
|
return;
|
|
@@ -191,18 +213,11 @@ export function createPressHandlers(config, runtime, host) {
|
|
|
191
213
|
clearPressOut();
|
|
192
214
|
runtime.active = true;
|
|
193
215
|
runtime.activatedAt = host.now();
|
|
216
|
+
// Pressability.js's `_activate`: the jitter check below reads from here, re-set on every
|
|
217
|
+
// (re)activation — including a drift-back-in re-arm, matching vendor exactly.
|
|
218
|
+
runtime.activatePosition = readPoint(event);
|
|
194
219
|
dlog('Pressable pressIn');
|
|
195
220
|
host.setPressed(true);
|
|
196
|
-
if (!runtime.longPressFired && onLongPress) {
|
|
197
|
-
runtime.longPressCancel = host.schedule(() => {
|
|
198
|
-
runtime.longPressCancel = undefined;
|
|
199
|
-
if (runtime.disposed || !runtime.active || runtime.driftedOut)
|
|
200
|
-
return;
|
|
201
|
-
runtime.longPressFired = true;
|
|
202
|
-
dlog('Pressable longPress timer fired');
|
|
203
|
-
onLongPress(event);
|
|
204
|
-
}, delayLongPress);
|
|
205
|
-
}
|
|
206
221
|
onPressIn?.(event);
|
|
207
222
|
}
|
|
208
223
|
function deactivate(event) {
|
|
@@ -262,6 +277,11 @@ export function createPressHandlers(config, runtime, host) {
|
|
|
262
277
|
runtime.pressOrigin = readPoint(event);
|
|
263
278
|
runtime.driftedOut = false;
|
|
264
279
|
measureRegion(runtime, host.getMeasureFn());
|
|
280
|
+
// Armed at GRANT, unconditionally — before the `unstable_pressDelay` branch below, which only
|
|
281
|
+
// defers the PRESSED VISUAL. `delayLongPress` already has `unstable_pressDelay` baked out of
|
|
282
|
+
// its default (`configFor`), so adding it back here is what keeps the long-press threshold at
|
|
283
|
+
// a constant time-from-touch-down regardless of how long the visual is deferred.
|
|
284
|
+
armLongPress(event);
|
|
265
285
|
if (unstable_pressDelay > 0) {
|
|
266
286
|
dlog(`Pressable pressIn deferred ${unstable_pressDelay}ms`);
|
|
267
287
|
runtime.pressDelayCancel = host.schedule(() => {
|
|
@@ -303,6 +323,12 @@ export function createPressHandlers(config, runtime, host) {
|
|
|
303
323
|
dlog('Pressable press suppressed by prior longPress');
|
|
304
324
|
return;
|
|
305
325
|
}
|
|
326
|
+
// Pressability.js:754-756 — Android only, and only when onPress is actually about to fire.
|
|
327
|
+
if (onPress !== undefined &&
|
|
328
|
+
Platform.OS === 'android' &&
|
|
329
|
+
android_disableSound !== true) {
|
|
330
|
+
SoundManager.playTouchSound();
|
|
331
|
+
}
|
|
306
332
|
onPress?.(event);
|
|
307
333
|
},
|
|
308
334
|
handleResponderMove(event) {
|
|
@@ -312,6 +338,15 @@ export function createPressHandlers(config, runtime, host) {
|
|
|
312
338
|
const here = readPoint(event);
|
|
313
339
|
if (!here)
|
|
314
340
|
return;
|
|
341
|
+
// Pressability.js:502-508 — checked BEFORE the retention-region branch below, and
|
|
342
|
+
// independent of it: a long press must not fire on a jitter, even one well inside the
|
|
343
|
+
// broader retention rect that keeps the press itself alive.
|
|
344
|
+
const activatePosition = runtime.activatePosition;
|
|
345
|
+
if (activatePosition !== undefined) {
|
|
346
|
+
const distance = Math.hypot(activatePosition.x - here.x, activatePosition.y - here.y);
|
|
347
|
+
if (distance > LONG_PRESS_DEACTIVATION_DISTANCE)
|
|
348
|
+
clearLongPress();
|
|
349
|
+
}
|
|
315
350
|
if (!isWithinRetention(here)) {
|
|
316
351
|
if (!runtime.driftedOut) {
|
|
317
352
|
dlog('Pressable drifted past retention region — deactivating');
|
|
@@ -2,6 +2,8 @@ import type { IScrollRoutingHandle } from './scroll-routing-handle';
|
|
|
2
2
|
export interface ISection<ItemT> {
|
|
3
3
|
title: string;
|
|
4
4
|
data: readonly ItemT[];
|
|
5
|
+
key?: string;
|
|
6
|
+
keyExtractor?(item: ItemT, index: number): string;
|
|
5
7
|
}
|
|
6
8
|
export type ISectionEntry<ItemT> = {
|
|
7
9
|
kind: 'header';
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// stream, so headers, items, and footers are windowed by the same machinery. All of this
|
|
5
5
|
// is pure transform - every adapter reuses it; the adapter supplies only the per-entry
|
|
6
6
|
// element creation (renderSectionHeader / renderItem / etc.) and the ref wiring.
|
|
7
|
+
import { defaultKeyExtractor } from './virtualized-list.js';
|
|
7
8
|
// Flatten sections into entries AND record where each section header lands in the flat
|
|
8
9
|
// stream, so scrollToLocation can map (sectionIndex, itemIndex) -> flat index without
|
|
9
10
|
// re-deriving the layout. withSeparators inserts a section separator between adjacent
|
|
@@ -30,16 +31,22 @@ export function flattenSections(sections, withSeparators) {
|
|
|
30
31
|
export function unwrapEntryItem(entry) {
|
|
31
32
|
return entry !== undefined && entry.kind === 'item' ? entry.item : undefined;
|
|
32
33
|
}
|
|
34
|
+
// RN's own section-relative key (`VirtualizedSectionList.js`'s `_subExtractor`): the section's
|
|
35
|
+
// own `key` when given, else its position — never the flat entry index, which is `entry`'s own
|
|
36
|
+
// but is not the SECTION's identity.
|
|
37
|
+
function sectionKeyPart(section, sectionIndex) {
|
|
38
|
+
return section.key ?? String(sectionIndex);
|
|
39
|
+
}
|
|
33
40
|
export function sectionEntryKey(entry, index, keyExtractor) {
|
|
34
|
-
if (entry.kind === 'header')
|
|
35
|
-
return `section-${entry.sectionIndex}`;
|
|
36
|
-
if (entry.kind === 'footer')
|
|
37
|
-
return `section-${entry.sectionIndex}:footer`;
|
|
38
41
|
if (entry.kind === 'section-separator')
|
|
39
42
|
return `section-${entry.sectionIndex}:separator`;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
const sectionKey = sectionKeyPart(entry.section, entry.sectionIndex);
|
|
44
|
+
if (entry.kind === 'header')
|
|
45
|
+
return `${sectionKey}:header`;
|
|
46
|
+
if (entry.kind === 'footer')
|
|
47
|
+
return `${sectionKey}:footer`;
|
|
48
|
+
const resolve = entry.section.keyExtractor ?? keyExtractor ?? defaultKeyExtractor;
|
|
49
|
+
return `${sectionKey}:${resolve(entry.item, entry.itemIndex)}`;
|
|
43
50
|
}
|
|
44
51
|
// RN's itemIndex is offset by 1 so itemIndex 0 targets the header and itemIndex >= 1
|
|
45
52
|
// targets that section's items. Returns undefined when the section is out of range.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { IMeasureOnSuccess, IMeasureInWindowOnSuccess, IMeasureLayoutOnSuccess, ISymbioteNode,
|
|
1
|
+
import type { IMeasureOnSuccess, IMeasureInWindowOnSuccess, IMeasureLayoutOnSuccess, ISymbioteNode, ISymbioteEvent, ITextStyle } from '@symbiote-native/engine';
|
|
2
2
|
import type { IAccessibilityProps, IAriaProps } from '../accessibility-props';
|
|
3
|
+
import type { IRectOffset } from './pressable';
|
|
3
4
|
export type IInputMode = 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
|
|
4
5
|
export type IEnterKeyHint = 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send';
|
|
5
6
|
export type ISubmitBehavior = 'submit' | 'blurAndSubmit' | 'newline';
|
|
@@ -10,49 +11,10 @@ export type ITextInputSelection = {
|
|
|
10
11
|
export type ITextInputEventHandler = (event: ISymbioteEvent) => void;
|
|
11
12
|
export declare const INITIAL_EVENT_COUNT = 0;
|
|
12
13
|
export declare const SELECTION_NONE = -1;
|
|
13
|
-
export declare function keyboardTypeForInputMode(inputMode: string, os: IPlatformOSType): string | undefined;
|
|
14
|
-
export declare function mapAutoComplete(map: Record<string, string>, token: string): string | undefined;
|
|
15
|
-
export declare function foldAutoComplete(token: string | undefined): {
|
|
16
|
-
autoComplete: string | undefined;
|
|
17
|
-
textContentType: string | undefined;
|
|
18
|
-
};
|
|
19
|
-
export declare function foldSubmitBehavior(submitBehavior: string | undefined, blurOnSubmit: boolean | undefined, multiline: boolean): string;
|
|
20
14
|
export declare function foldText(value: string | undefined, defaultValue: string | undefined): string | undefined;
|
|
21
15
|
export declare function textFromChange(event: ISymbioteEvent): string | undefined;
|
|
22
16
|
export declare function eventCountFromChange(event: ISymbioteEvent): number | undefined;
|
|
23
17
|
export declare function shouldCommandText(lastNativeText: string | undefined, value: string | undefined): value is string;
|
|
24
|
-
export type ITextInputFoldInput = {
|
|
25
|
-
inputMode?: string;
|
|
26
|
-
keyboardType?: string;
|
|
27
|
-
enterKeyHint?: string;
|
|
28
|
-
returnKeyType?: string;
|
|
29
|
-
readOnly?: boolean;
|
|
30
|
-
editable?: boolean;
|
|
31
|
-
submitBehavior?: string;
|
|
32
|
-
blurOnSubmit?: boolean;
|
|
33
|
-
multiline: boolean;
|
|
34
|
-
cursorColor?: string;
|
|
35
|
-
selectionColor?: string;
|
|
36
|
-
selectionHandleColor?: string;
|
|
37
|
-
autoComplete?: string;
|
|
38
|
-
textContentType?: string;
|
|
39
|
-
showSoftInputOnFocus?: boolean;
|
|
40
|
-
underlineColorAndroid?: string;
|
|
41
|
-
};
|
|
42
|
-
export type IFoldedTextInputProps = {
|
|
43
|
-
keyboardType: string | undefined;
|
|
44
|
-
returnKeyType: string | undefined;
|
|
45
|
-
editable: boolean | undefined;
|
|
46
|
-
submitBehavior: string;
|
|
47
|
-
selectionColor: string | undefined;
|
|
48
|
-
cursorColor: string | undefined;
|
|
49
|
-
selectionHandleColor: string | undefined;
|
|
50
|
-
underlineColorAndroid: string;
|
|
51
|
-
autoComplete: string | undefined;
|
|
52
|
-
textContentType: string | undefined;
|
|
53
|
-
showSoftInputOnFocus: boolean | undefined;
|
|
54
|
-
};
|
|
55
|
-
export declare function resolveTextInputProps(input: ITextInputFoldInput): IFoldedTextInputProps;
|
|
56
18
|
export type ITextInputChangeEvent = ISymbioteEvent & {
|
|
57
19
|
text: string;
|
|
58
20
|
};
|
|
@@ -89,7 +51,12 @@ export type ITextInputProps = IAccessibilityProps & IAriaProps & {
|
|
|
89
51
|
underlineColorAndroid?: string;
|
|
90
52
|
inputAccessoryViewID?: string;
|
|
91
53
|
style?: ITextStyle;
|
|
54
|
+
hitSlop?: IRectOffset;
|
|
55
|
+
onPress?: ITextInputEventHandler;
|
|
56
|
+
onPressIn?: ITextInputEventHandler;
|
|
57
|
+
onPressOut?: ITextInputEventHandler;
|
|
92
58
|
onValueChange?: (event: ITextInputChangeEvent) => void;
|
|
59
|
+
onChangeText?: (event: ITextInputChangeEvent) => void;
|
|
93
60
|
onFocus?: ITextInputEventHandler;
|
|
94
61
|
onBlur?: ITextInputEventHandler;
|
|
95
62
|
onEndEditing?: ITextInputEventHandler;
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
// latest count, while `lastNativeText` is bookkeeping the controlled-write effect mutates
|
|
12
12
|
// without a render. So the logic layer is the pure folds/maps + the controlled-write predicate;
|
|
13
13
|
// each adapter holds the two pieces in ITS own primitives (React useState/useRef, Vue ref/let).
|
|
14
|
-
import { Platform } from '@symbiote-native/engine';
|
|
15
14
|
// The acknowledged count starts at 0, native has reported nothing yet, so the first
|
|
16
15
|
// controlled write echoes 0 and lands on eventLag 0.
|
|
17
16
|
export const INITIAL_EVENT_COUNT = 0;
|
|
@@ -19,156 +18,20 @@ export const INITIAL_EVENT_COUNT = 0;
|
|
|
19
18
|
// leave the caret where it is rather than move it (used on a controlled write with no
|
|
20
19
|
// explicit selection).
|
|
21
20
|
export const SELECTION_NONE = -1;
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// RN's inputMode -> keyboardType map (TextInput.js:815). `search` is platform-split (iOS
|
|
26
|
-
// 'web-search', else 'default'); we use the Android/default branch since the folded prop is
|
|
27
|
-
// forwarded verbatim and the safe default avoids an unknown keyboardType on Android.
|
|
28
|
-
const inputModeToKeyboardType = {
|
|
29
|
-
decimal: 'decimal-pad',
|
|
30
|
-
email: 'email-address',
|
|
31
|
-
none: 'default',
|
|
32
|
-
numeric: 'number-pad',
|
|
33
|
-
tel: 'phone-pad',
|
|
34
|
-
text: 'default',
|
|
35
|
-
url: 'url',
|
|
36
|
-
};
|
|
37
|
-
// `search` is the ONE inputMode token RN resolves per platform (TextInput.js:815-825):
|
|
38
|
-
// search: Platform.OS === 'ios' ? 'web-search' : 'default'
|
|
39
|
-
// iOS has a dedicated search keyboard whose return key is a magnifier; every other host falls back
|
|
40
|
-
// to the default one. It is deliberately absent from the map above, so there is one source for it
|
|
41
|
-
// rather than a map entry and an override that can drift apart.
|
|
21
|
+
// THE FOUR W3C->NATIVE LOOKUP TABLES AND THE FOLDS OVER THEM STOOD HERE, and they are the engine's
|
|
22
|
+
// now: `foldTextInputAliases` in `SymbioteFabricProps.cpp`, with
|
|
23
|
+
// `core/engine/cpp/tests/js/text-input-payload.itest.ts` as their contract.
|
|
42
24
|
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
const enterKeyHintToReturnKeyType = {
|
|
54
|
-
done: 'done',
|
|
55
|
-
enter: 'default',
|
|
56
|
-
go: 'go',
|
|
57
|
-
next: 'next',
|
|
58
|
-
previous: 'previous',
|
|
59
|
-
search: 'search',
|
|
60
|
-
send: 'send',
|
|
61
|
-
};
|
|
62
|
-
// RN's W3C autocomplete -> Android `autoComplete` map (TextInput.js:828). A token with no
|
|
63
|
-
// native equivalent passes through unchanged (RN's `?? autoComplete`).
|
|
64
|
-
const autoCompleteWebToAndroid = {
|
|
65
|
-
'additional-name': 'name-middle',
|
|
66
|
-
'address-line1': 'postal-address-region',
|
|
67
|
-
'address-line2': 'postal-address-locality',
|
|
68
|
-
bday: 'birthdate-full',
|
|
69
|
-
'bday-day': 'birthdate-day',
|
|
70
|
-
'bday-month': 'birthdate-month',
|
|
71
|
-
'bday-year': 'birthdate-year',
|
|
72
|
-
'cc-csc': 'cc-csc',
|
|
73
|
-
'cc-exp': 'cc-exp',
|
|
74
|
-
'cc-exp-month': 'cc-exp-month',
|
|
75
|
-
'cc-exp-year': 'cc-exp-year',
|
|
76
|
-
'cc-number': 'cc-number',
|
|
77
|
-
country: 'postal-address-country',
|
|
78
|
-
'current-password': 'password',
|
|
79
|
-
email: 'email',
|
|
80
|
-
'family-name': 'name-family',
|
|
81
|
-
'given-name': 'name-given',
|
|
82
|
-
'honorific-prefix': 'name-prefix',
|
|
83
|
-
'honorific-suffix': 'name-suffix',
|
|
84
|
-
name: 'name',
|
|
85
|
-
'new-password': 'password-new',
|
|
86
|
-
off: 'off',
|
|
87
|
-
'one-time-code': 'sms-otp',
|
|
88
|
-
'postal-code': 'postal-code',
|
|
89
|
-
sex: 'gender',
|
|
90
|
-
'street-address': 'street-address',
|
|
91
|
-
tel: 'tel',
|
|
92
|
-
'tel-country-code': 'tel-country-code',
|
|
93
|
-
'tel-national': 'tel-national',
|
|
94
|
-
username: 'username',
|
|
95
|
-
};
|
|
96
|
-
// RN's W3C autocomplete -> iOS `textContentType` map (TextInput.js:862). A token absent here
|
|
97
|
-
// leaves textContentType undefined on iOS (RN's `autoComplete in map` guard).
|
|
98
|
-
const autoCompleteWebToTextContentType = {
|
|
99
|
-
'additional-name': 'middleName',
|
|
100
|
-
'address-line1': 'streetAddressLine1',
|
|
101
|
-
'address-line2': 'streetAddressLine2',
|
|
102
|
-
bday: 'birthdate',
|
|
103
|
-
'bday-day': 'birthdateDay',
|
|
104
|
-
'bday-month': 'birthdateMonth',
|
|
105
|
-
'bday-year': 'birthdateYear',
|
|
106
|
-
'cc-additional-name': 'creditCardMiddleName',
|
|
107
|
-
'cc-csc': 'creditCardSecurityCode',
|
|
108
|
-
'cc-exp': 'creditCardExpiration',
|
|
109
|
-
'cc-exp-month': 'creditCardExpirationMonth',
|
|
110
|
-
'cc-exp-year': 'creditCardExpirationYear',
|
|
111
|
-
'cc-family-name': 'creditCardFamilyName',
|
|
112
|
-
'cc-given-name': 'creditCardGivenName',
|
|
113
|
-
'cc-name': 'creditCardName',
|
|
114
|
-
'cc-number': 'creditCardNumber',
|
|
115
|
-
'cc-type': 'creditCardType',
|
|
116
|
-
country: 'countryName',
|
|
117
|
-
'current-password': 'password',
|
|
118
|
-
email: 'emailAddress',
|
|
119
|
-
'family-name': 'familyName',
|
|
120
|
-
'given-name': 'givenName',
|
|
121
|
-
'honorific-prefix': 'namePrefix',
|
|
122
|
-
'honorific-suffix': 'nameSuffix',
|
|
123
|
-
name: 'name',
|
|
124
|
-
'new-password': 'newPassword',
|
|
125
|
-
nickname: 'nickname',
|
|
126
|
-
off: 'none',
|
|
127
|
-
'one-time-code': 'oneTimeCode',
|
|
128
|
-
organization: 'organizationName',
|
|
129
|
-
'organization-title': 'jobTitle',
|
|
130
|
-
'postal-code': 'postalCode',
|
|
131
|
-
'street-address': 'fullStreetAddress',
|
|
132
|
-
tel: 'telephoneNumber',
|
|
133
|
-
url: 'URL',
|
|
134
|
-
username: 'username',
|
|
135
|
-
};
|
|
136
|
-
// Safe lookup into a W3C->native / alias->native map (no `as`): own-property guard, undefined
|
|
137
|
-
// if the token has no native equivalent. The caller decides the per-platform fallback. Named
|
|
138
|
-
// for its first user (autoComplete), but it is the one generic safe lookup every map shares.
|
|
139
|
-
export function mapAutoComplete(map, token) {
|
|
140
|
-
return Object.prototype.hasOwnProperty.call(map, token)
|
|
141
|
-
? map[token]
|
|
142
|
-
: undefined;
|
|
143
|
-
}
|
|
144
|
-
// RN folds W3C `autoComplete` per platform (TextInput.js:938): Android takes the mapped
|
|
145
|
-
// `autoComplete` token (falling back to the raw token), iOS takes the mapped `textContentType`
|
|
146
|
-
// (only when the token is in the map, else leaves it untouched). Symbiote is Metro-built per
|
|
147
|
-
// platform but folds platform-agnostically, resolving BOTH native props from the one token:
|
|
148
|
-
// the iOS-only `textContentType` is inert on Android and the Android `autoComplete` token is
|
|
149
|
-
// inert on iOS, so emitting both is safe: same shape as the dual-keyed events.
|
|
150
|
-
export function foldAutoComplete(token) {
|
|
151
|
-
if (token === undefined)
|
|
152
|
-
return { autoComplete: undefined, textContentType: undefined };
|
|
153
|
-
return {
|
|
154
|
-
autoComplete: mapAutoComplete(autoCompleteWebToAndroid, token) ?? token,
|
|
155
|
-
textContentType: mapAutoComplete(autoCompleteWebToTextContentType, token),
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
// RN's submitBehavior reconciliation (TextInput.js:559). Explicit submitBehavior wins (with
|
|
159
|
-
// single-line 'newline' coerced to 'blurAndSubmit'); else it is derived from the legacy
|
|
160
|
-
// blurOnSubmit per multiline. Input/output are plain strings: the result is forwarded to the
|
|
161
|
-
// native prop verbatim, so no ISubmitBehavior union is needed at the seam.
|
|
162
|
-
export function foldSubmitBehavior(submitBehavior, blurOnSubmit, multiline) {
|
|
163
|
-
if (submitBehavior !== undefined) {
|
|
164
|
-
if (!multiline && submitBehavior === 'newline')
|
|
165
|
-
return 'blurAndSubmit';
|
|
166
|
-
return submitBehavior;
|
|
167
|
-
}
|
|
168
|
-
if (multiline)
|
|
169
|
-
return blurOnSubmit === true ? 'blurAndSubmit' : 'newline';
|
|
170
|
-
return blurOnSubmit !== false ? 'blurAndSubmit' : 'submit';
|
|
171
|
-
}
|
|
25
|
+
// Deleted in the same pass as `rippleProps` next door, for the same reason. Nothing called them
|
|
26
|
+
// after the port — only this file's own tests and the barrel — and a rule that lives in two places
|
|
27
|
+
// has two behaviours the day one of them is edited. An exported twin kept alive by its own test is
|
|
28
|
+
// the shape the "no mirrors" rule exists to catch.
|
|
29
|
+
//
|
|
30
|
+
// Gone with them: `inputModeToKeyboardType`, `enterKeyHintToReturnKeyType`,
|
|
31
|
+
// `autoCompleteWebToAndroid`, `autoCompleteWebToTextContentType`, `keyboardTypeForInputMode`,
|
|
32
|
+
// `mapAutoComplete`, `foldAutoComplete`, `foldSubmitBehavior`, `resolveTextInputProps` and its two
|
|
33
|
+
// types. What STAYS below is the machine — `foldText`, the change-event readers, and the
|
|
34
|
+
// controlled-write decision — which runs at gesture rate and calls back into app code.
|
|
172
35
|
// RN's fold: value wins, else defaultValue, else leave undefined (uncontrolled).
|
|
173
36
|
export function foldText(value, defaultValue) {
|
|
174
37
|
if (typeof value === 'string')
|
|
@@ -194,44 +57,9 @@ export function eventCountFromChange(event) {
|
|
|
194
57
|
export function shouldCommandText(lastNativeText, value) {
|
|
195
58
|
return typeof value === 'string' && lastNativeText !== value;
|
|
196
59
|
}
|
|
197
|
-
// The whole per-platform-agnostic prop fold in one place (TextInput.js:928-946), shared by every
|
|
198
|
-
// adapter: inputMode wins over keyboardType, enterKeyHint over returnKeyType, readOnly over
|
|
199
|
-
// editable (inverted), the cursor/selection-handle colors default from selectionColor, the W3C
|
|
200
|
-
// autoComplete token folds to the per-platform native prop (an explicit textContentType still
|
|
201
|
-
// wins), inputMode forces softInput visibility, and underlineColorAndroid defaults to
|
|
202
|
-
// 'transparent' to hide the Material EditText bar.
|
|
203
|
-
export function resolveTextInputProps(input) {
|
|
204
|
-
const folded = foldAutoComplete(input.autoComplete);
|
|
205
|
-
return {
|
|
206
|
-
keyboardType: input.inputMode !== undefined
|
|
207
|
-
? keyboardTypeForInputMode(input.inputMode, Platform.OS)
|
|
208
|
-
: input.keyboardType,
|
|
209
|
-
returnKeyType: input.enterKeyHint !== undefined
|
|
210
|
-
? mapAutoComplete(enterKeyHintToReturnKeyType, input.enterKeyHint)
|
|
211
|
-
: input.returnKeyType,
|
|
212
|
-
editable: input.readOnly !== undefined ? !input.readOnly : input.editable,
|
|
213
|
-
submitBehavior: foldSubmitBehavior(input.submitBehavior, input.blurOnSubmit, input.multiline),
|
|
214
|
-
selectionColor: input.selectionColor,
|
|
215
|
-
cursorColor: input.cursorColor !== undefined
|
|
216
|
-
? input.cursorColor
|
|
217
|
-
: input.selectionColor,
|
|
218
|
-
selectionHandleColor: input.selectionHandleColor !== undefined
|
|
219
|
-
? input.selectionHandleColor
|
|
220
|
-
: input.selectionColor,
|
|
221
|
-
underlineColorAndroid: input.underlineColorAndroid !== undefined
|
|
222
|
-
? input.underlineColorAndroid
|
|
223
|
-
: 'transparent',
|
|
224
|
-
autoComplete: folded.autoComplete,
|
|
225
|
-
textContentType: input.textContentType !== undefined
|
|
226
|
-
? input.textContentType
|
|
227
|
-
: folded.textContentType,
|
|
228
|
-
showSoftInputOnFocus: input.inputMode !== undefined
|
|
229
|
-
? input.inputMode !== 'none'
|
|
230
|
-
: input.showSoftInputOnFocus,
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
60
|
const TEXT_INPUT_CALLBACKS = {
|
|
234
61
|
onValueChange: true,
|
|
62
|
+
onChangeText: true,
|
|
235
63
|
onFocus: true,
|
|
236
64
|
onBlur: true,
|
|
237
65
|
onEndEditing: true,
|
|
@@ -239,5 +67,8 @@ const TEXT_INPUT_CALLBACKS = {
|
|
|
239
67
|
onKeyPress: true,
|
|
240
68
|
onSelectionChange: true,
|
|
241
69
|
onContentSizeChange: true,
|
|
70
|
+
onPress: true,
|
|
71
|
+
onPressIn: true,
|
|
72
|
+
onPressOut: true,
|
|
242
73
|
};
|
|
243
74
|
export const TEXT_INPUT_CALLBACK_NAMES = Object.keys(TEXT_INPUT_CALLBACKS);
|
|
@@ -50,6 +50,7 @@ export declare function createHighlightUnderlayRuntime(): IHighlightUnderlayRunt
|
|
|
50
50
|
export interface IHighlightUnderlayConfig {
|
|
51
51
|
delayPressOut: number;
|
|
52
52
|
hasPressHandler: boolean;
|
|
53
|
+
testOnlyPressed: boolean;
|
|
53
54
|
schedule: (callback: () => void, ms: number) => () => void;
|
|
54
55
|
}
|
|
55
56
|
export interface IHighlightUnderlayCallbacks {
|
package/build/state/touchable.js
CHANGED
|
@@ -18,14 +18,15 @@ export const DEFAULT_ACTIVE_OPACITY = 0.2;
|
|
|
18
18
|
// finger drifted outside and came back (RESPONDER_INACTIVE_PRESS_OUT -> RESPONDER_ACTIVE_PRESS_IN,
|
|
19
19
|
// driven by onResponderMove).
|
|
20
20
|
//
|
|
21
|
-
// Our engine
|
|
22
|
-
// on
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
21
|
+
// Our engine's `state/pressable.ts` DOES reproduce that reactivation — `handleResponderMove`
|
|
22
|
+
// re-`activate()`s on a drift-back-in — and `touchable-opacity.ts`'s own `activate` callback picks
|
|
23
|
+
// between the two constants below by checking `event.type === 'responderMove'`, our engine's own
|
|
24
|
+
// dispatch-name equivalent (`core/engine/src/events/index.ts`'s `bubble`/`callOwnListener` both set
|
|
25
|
+
// `type: listenerName`). A prior version of this comment claimed "no drift-back-in re-activation at
|
|
26
|
+
// all" — that was true only until the drift/retention mechanism was added to `state/pressable.ts`
|
|
27
|
+
// and was never re-checked against it; every reactivation was snapping to the active opacity
|
|
28
|
+
// instantly instead of easing over 150ms until this was caught.
|
|
26
29
|
export const OPACITY_ACTIVE_GRANT_DURATION_MS = 0;
|
|
27
|
-
// The re-activation branch. Unreachable today — kept so the constant exists when the press
|
|
28
|
-
// machine grows drift-back-in, and because it is the value RN uses there.
|
|
29
30
|
export const OPACITY_ACTIVE_DURATION_MS = 150;
|
|
30
31
|
export const OPACITY_INACTIVE_DURATION_MS = 250;
|
|
31
32
|
export const RESTING_OPACITY = 1;
|
|
@@ -128,7 +129,7 @@ export function createHighlightUnderlayRuntime() {
|
|
|
128
129
|
return { hideTimerCancel: undefined };
|
|
129
130
|
}
|
|
130
131
|
export function createHighlightUnderlayHandlers(config, runtime, callbacks) {
|
|
131
|
-
const { delayPressOut, hasPressHandler, schedule } = config;
|
|
132
|
+
const { delayPressOut, hasPressHandler, testOnlyPressed, schedule } = config;
|
|
132
133
|
const { setShown, onShowUnderlay, onHideUnderlay } = callbacks;
|
|
133
134
|
function clearHideTimer() {
|
|
134
135
|
if (runtime.hideTimerCancel !== undefined) {
|
|
@@ -144,6 +145,8 @@ export function createHighlightUnderlayHandlers(config, runtime, callbacks) {
|
|
|
144
145
|
}
|
|
145
146
|
function hide() {
|
|
146
147
|
clearHideTimer();
|
|
148
|
+
if (testOnlyPressed)
|
|
149
|
+
return;
|
|
147
150
|
if (!hasPressHandler)
|
|
148
151
|
return;
|
|
149
152
|
setShown(false);
|
|
@@ -57,8 +57,8 @@ export interface IListReducerInputs<ItemT> {
|
|
|
57
57
|
initialNumToRender: number;
|
|
58
58
|
maxToRenderPerBatch: number;
|
|
59
59
|
updateCellsBatchingPeriod: number;
|
|
60
|
-
onEndReachedThreshold: number;
|
|
61
|
-
onStartReachedThreshold: number;
|
|
60
|
+
onEndReachedThreshold: number | undefined;
|
|
61
|
+
onStartReachedThreshold: number | undefined;
|
|
62
62
|
onEndReachedActive: boolean;
|
|
63
63
|
onStartReachedActive: boolean;
|
|
64
64
|
viewabilityPairs: IViewabilityConfigCallbackPair<ItemT>[];
|