react-native-auto-positioned-popup 1.0.11 → 1.0.13
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 +94 -0
- package/README_zh.md +94 -0
- package/lib/AutoPositionedPopup.d.ts.map +1 -1
- package/lib/AutoPositionedPopup.js +294 -134
- package/lib/AutoPositionedPopup.js.map +1 -1
- package/lib/AutoPositionedPopupProps.d.ts +8 -0
- package/lib/AutoPositionedPopupProps.d.ts.map +1 -1
- package/package.json +10 -10
- package/src/AutoPositionedPopup.tsx +363 -216
- package/src/AutoPositionedPopupProps.ts +15 -7
|
@@ -34,8 +34,7 @@ const ListItem = memo(({ updateState, item, index, selectedItem, }) => {
|
|
|
34
34
|
rootViewsRef.current = rootViews;
|
|
35
35
|
}, [rootViews]);
|
|
36
36
|
return useMemo(() => {
|
|
37
|
-
// console.log('AutoPositionedPopup.tsx ListItem
|
|
38
|
-
// console.log('AutoPositionedPopup.tsx ListItem item=', item);
|
|
37
|
+
// console.log('AutoPositionedPopup.tsx ListItem=', {index, item, selectedItem});
|
|
39
38
|
const isSelected = item.id === (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id);
|
|
40
39
|
return (<TouchableOpacity key={item.id} style={[
|
|
41
40
|
styles.commonModalRow,
|
|
@@ -51,7 +50,7 @@ const ListItem = memo(({ updateState, item, index, selectedItem, }) => {
|
|
|
51
50
|
</TouchableOpacity>);
|
|
52
51
|
}, [updateState, item, index, selectedItem, rootViewsRef]);
|
|
53
52
|
});
|
|
54
|
-
const AutoPositionedPopupList = memo(({ tag, updateState, fetchData, keyExtractor = (item) => String(item.id), renderItem, selectedItem, localSearch, pageSize, }) => {
|
|
53
|
+
const AutoPositionedPopupList = memo(({ tag, updateState, fetchData, keyExtractor = (item) => String(item.id), renderItem, selectedItem, localSearch, pageSize, showListEmptyComponent, emptyText }) => {
|
|
55
54
|
const [state, setState] = useState({
|
|
56
55
|
selectedItem: selectedItem,
|
|
57
56
|
localData: [],
|
|
@@ -78,8 +77,8 @@ const AutoPositionedPopupList = memo(({ tag, updateState, fetchData, keyExtracto
|
|
|
78
77
|
};
|
|
79
78
|
}, []);
|
|
80
79
|
// useEffect(() => {
|
|
81
|
-
// //
|
|
82
|
-
// //
|
|
80
|
+
// // Listen to TextInput events, refresh list when received, not dependent on global searchQuery
|
|
81
|
+
// // Sync the latest searchQuery to list-specific ref for _fetchData to use
|
|
83
82
|
// ref_searchQuery.current = searchQuery;
|
|
84
83
|
// console.log('AutoPositionedPopupList useEffect searchQuery=', searchQuery);
|
|
85
84
|
// console.log('AutoPositionedPopupList useEffect state.localData=', state.localData);
|
|
@@ -108,10 +107,7 @@ const AutoPositionedPopupList = memo(({ tag, updateState, fetchData, keyExtracto
|
|
|
108
107
|
updateState(key, value);
|
|
109
108
|
};
|
|
110
109
|
const _fetchData = async ({ pageIndex, pageSize: currentPageSize, }) => {
|
|
111
|
-
console.log('AutoPositionedPopupList _fetchData
|
|
112
|
-
console.log('AutoPositionedPopupList _fetchData state.localData=', state.localData);
|
|
113
|
-
console.log('AutoPositionedPopupList _fetchData ref_searchQuery.current=', ref_searchQuery.current);
|
|
114
|
-
console.log('AutoPositionedPopupList _fetchData localSearch=', localSearch);
|
|
110
|
+
console.log('AutoPositionedPopupList _fetchData=', { pageIndex, pageSize: currentPageSize, 'state.localData': state.localData, 'ref_searchQuery.current': ref_searchQuery.current, localSearch });
|
|
115
111
|
if (localSearch && state.localData.length > 0) {
|
|
116
112
|
const result = state.localData.filter((item) => {
|
|
117
113
|
var _a;
|
|
@@ -160,7 +156,7 @@ const AutoPositionedPopupList = memo(({ tag, updateState, fetchData, keyExtracto
|
|
|
160
156
|
// Babel configuration handles the path redirection based on global.$fake
|
|
161
157
|
// No need for conditional import here
|
|
162
158
|
return (<View style={[styles.baseModalView, styles.autoPositionedPopupList]}>
|
|
163
|
-
<AdvancedFlatList style={[{ borderRadius: 0 }]} {...(ref_list && { ref: ref_list })} keyExtractor={(item, index) => keyExtractor ? keyExtractor(item) : item.id} keyboardShouldPersistTaps={'always'} fetchData={_fetchData} renderItem={renderItem ? ({ item, index }) => renderItem({ item: item, index }) : ({ item, index }) => _renderItem({ item: item, index })} showListEmptyComponent={
|
|
159
|
+
<AdvancedFlatList style={[{ borderRadius: 0 }]} {...(ref_list && { ref: ref_list })} keyExtractor={(item, index) => keyExtractor ? keyExtractor(item) : item.id} keyboardShouldPersistTaps={'always'} fetchData={_fetchData} renderItem={renderItem ? ({ item, index }) => renderItem({ item: item, index }) : ({ item, index }) => _renderItem({ item: item, index })} showListEmptyComponent={showListEmptyComponent} emptyText={emptyText}/>
|
|
164
160
|
</View>);
|
|
165
161
|
}, [tag,
|
|
166
162
|
updateState,
|
|
@@ -172,7 +168,7 @@ const AutoPositionedPopupList = memo(({ tag, updateState, fetchData, keyExtracto
|
|
|
172
168
|
searchQuery,
|
|
173
169
|
localSearch,
|
|
174
170
|
pageSize,
|
|
175
|
-
rootViewsRef,
|
|
171
|
+
rootViewsRef, showListEmptyComponent, emptyText
|
|
176
172
|
]);
|
|
177
173
|
});
|
|
178
174
|
// List layout constants
|
|
@@ -182,7 +178,7 @@ const listLayout = {
|
|
|
182
178
|
// Main AutoPositionedPopup component
|
|
183
179
|
const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
184
180
|
console.log('AutoPositionedPopup props=', props);
|
|
185
|
-
const { tag, style, AutoPositionedPopupBtnStyle, placeholder = 'Please Select', onSubmitEditing, TextInputProps = {}, inputStyle, labelStyle, popUpViewStyle = { left: '5%', width: '90%' }, fetchData = async ({ pageIndex, pageSize, searchQuery, }) => {
|
|
181
|
+
const { tag, style, AutoPositionedPopupBtnStyle, placeholder = 'Please Select', onSubmitEditing, TextInputProps = { autoFocus: true }, inputStyle, labelStyle, popUpViewStyle = { left: '5%', width: '90%' }, fetchData = async ({ pageIndex, pageSize, searchQuery, }) => {
|
|
186
182
|
const res = {
|
|
187
183
|
items: [],
|
|
188
184
|
pageIndex,
|
|
@@ -198,7 +194,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
198
194
|
console.warn('Error in fetch operation:', e);
|
|
199
195
|
}
|
|
200
196
|
return res;
|
|
201
|
-
}, renderItem, onItemSelected, localSearch = false, pageSize = 20, selectedItem, useTextInput = false, btwChildren, CustomRow = ({ children }) => <View>{children}</View>, keyExtractor = (item) => String((item === null || item === void 0 ? void 0 : item.id) || ''), AutoPositionedPopupBtnDisabled = false, forceRemoveAllRootViewOnItemSelected = false, centerDisplay = false, selectedItemBackgroundColor = 'rgba(116, 116, 128, 0.08)', textAlign = 'right', CustomPopView = undefined, CustomPopViewStyle } = props;
|
|
197
|
+
}, renderItem, onItemSelected, localSearch = false, pageSize = 20, selectedItem, useTextInput = false, btwChildren, CustomRow = ({ children }) => <View>{children}</View>, keyExtractor = (item) => String((item === null || item === void 0 ? void 0 : item.id) || ''), AutoPositionedPopupBtnDisabled = false, forceRemoveAllRootViewOnItemSelected = false, centerDisplay = false, selectedItemBackgroundColor = 'rgba(116, 116, 128, 0.08)', textAlign = 'right', CustomPopView = undefined, CustomPopViewStyle, showListEmptyComponent = true, emptyText = '' } = props;
|
|
202
198
|
// State management similar to project implementation
|
|
203
199
|
const [state, setState] = useState({
|
|
204
200
|
isFocus: false,
|
|
@@ -218,6 +214,16 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
218
214
|
const keyboardVisibleRef = useRef(false);
|
|
219
215
|
const refAutoPositionedPopup = useRef(null);
|
|
220
216
|
const ref_searchQuery = useRef('');
|
|
217
|
+
// Add ref to track previous keyboard state to avoid false triggers during parent component re-renders
|
|
218
|
+
const prevIsKeyboardFullyShownRef = useRef(false);
|
|
219
|
+
const prevPropsRef = useRef({});
|
|
220
|
+
// Add ref to prevent onFocus/onBlur loop triggers during parent component re-renders
|
|
221
|
+
const lastFocusTimeRef = useRef(0);
|
|
222
|
+
const isFocusEventProcessingRef = useRef(false);
|
|
223
|
+
// Add ref to stabilize TextInput props reference
|
|
224
|
+
// Only update when deep comparison detects real changes to avoid TextInput recreation due to reference changes during parent component redraws
|
|
225
|
+
const stableInputStyleRef = useRef(inputStyle);
|
|
226
|
+
const stableTextInputPropsRef = useRef(TextInputProps);
|
|
221
227
|
// Simple keyboard status tracking (alternative to useKeyboardStatus hook)
|
|
222
228
|
// Legacy state for compatibility
|
|
223
229
|
const [isVisible, setIsVisible] = useState(false);
|
|
@@ -239,8 +245,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
239
245
|
useEffect(() => {
|
|
240
246
|
(async () => {
|
|
241
247
|
})();
|
|
242
|
-
console.log(`AutoPositionedPopup componentDidMount tag
|
|
243
|
-
console.log('AutoPositionedPopup componentDidMount CustomPopView=', CustomPopView);
|
|
248
|
+
console.log(`AutoPositionedPopup componentDidMount=`, { tag, CustomPopView });
|
|
244
249
|
//componentWillUnmount
|
|
245
250
|
return () => {
|
|
246
251
|
console.log(`AutoPositionedPopup componentWillUnmount tag=`, tag);
|
|
@@ -271,8 +276,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
271
276
|
}, [rootViews]);
|
|
272
277
|
useEffect(() => {
|
|
273
278
|
var _a, _b;
|
|
274
|
-
console.log('AutoPositionedPopup useEffect tag=', tag);
|
|
275
|
-
console.log('AutoPositionedPopup useEffect selectedItem=', selectedItem);
|
|
279
|
+
console.log('AutoPositionedPopup useEffect [selectedItem, state.selectedItem, tag]=', { tag, selectedItem, 'state.selectedItem': state.selectedItem });
|
|
276
280
|
console.log('AutoPositionedPopup useEffect state.selectedItem=', state.selectedItem);
|
|
277
281
|
if (((_a = state.selectedItem) === null || _a === void 0 ? void 0 : _a.id) !== (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) || ((_b = state.selectedItem) === null || _b === void 0 ? void 0 : _b.title) !== (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.title)) {
|
|
278
282
|
console.log('AutoPositionedPopup useEffect selectedItem!=state.selectedItem');
|
|
@@ -283,24 +287,55 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
283
287
|
}, [selectedItem, state.selectedItem, tag]);
|
|
284
288
|
useEffect(() => {
|
|
285
289
|
var _a, _b, _c;
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
console.log('AutoPositionedPopup useEffect
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
290
|
+
// Detect if keyboard state has actually changed to avoid false triggers during parent component re-renders
|
|
291
|
+
const keyboardStateChanged = prevIsKeyboardFullyShownRef.current !== isKeyboardFullyShown;
|
|
292
|
+
const propsChanged = prevPropsRef.current.CustomPopView !== CustomPopView ||
|
|
293
|
+
prevPropsRef.current.CustomPopViewStyle !== CustomPopViewStyle ||
|
|
294
|
+
prevPropsRef.current.TextInputProps !== TextInputProps;
|
|
295
|
+
console.log('AutoPositionedPopup useEffect [isKeyboardFullyShown,\n' +
|
|
296
|
+
' state.isFocus,\n' +
|
|
297
|
+
' useTextInput,\n' +
|
|
298
|
+
' CustomPopView,\n' +
|
|
299
|
+
' CustomPopViewStyle,\n' +
|
|
300
|
+
' forceRemoveAllRootViewOnItemSelected,\n' +
|
|
301
|
+
' tag, TextInputProps,\n' +
|
|
302
|
+
' state.selectedItem, showListEmptyComponent\n' +
|
|
303
|
+
' ]=', {
|
|
304
|
+
tag,
|
|
305
|
+
'state.isFocus': state.isFocus,
|
|
306
|
+
isKeyboardFullyShown,
|
|
307
|
+
'ref_isFocus.current': ref_isFocus.current,
|
|
308
|
+
'ref_isKeyboardFullyShown.current': ref_isKeyboardFullyShown.current,
|
|
309
|
+
useTextInput, TextInputProps,
|
|
310
|
+
'hasAddedRootView.current': hasAddedRootView.current,
|
|
311
|
+
'hasShownRootView.current': hasShownRootView.current,
|
|
312
|
+
'keyboardStateChanged': keyboardStateChanged,
|
|
313
|
+
'propsChanged': propsChanged
|
|
314
|
+
});
|
|
315
|
+
// Update ref to record current state
|
|
316
|
+
prevIsKeyboardFullyShownRef.current = isKeyboardFullyShown;
|
|
317
|
+
prevPropsRef.current = {
|
|
318
|
+
CustomPopView,
|
|
319
|
+
CustomPopViewStyle,
|
|
320
|
+
TextInputProps
|
|
321
|
+
};
|
|
322
|
+
// Only execute logic when keyboard state actually changes or user actively operates
|
|
323
|
+
if (!keyboardStateChanged && hasAddedRootView.current) {
|
|
324
|
+
console.log('AutoPositionedPopup: Skip execution - parent component re-rendered but keyboard state unchanged');
|
|
325
|
+
// if (!ref_isFocus.current) {
|
|
326
|
+
// textInputRef.current?.focus()
|
|
327
|
+
// }
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
295
330
|
if (useTextInput) {
|
|
296
331
|
if (isKeyboardFullyShown && hasAddedRootView.current && !hasShownRootView.current && state.isFocus) {
|
|
297
332
|
(_a = refAutoPositionedPopup.current) === null || _a === void 0 ? void 0 : _a.measureInWindow((x, y, width, height) => {
|
|
298
333
|
var _a;
|
|
299
|
-
console.log('AutoPositionedPopup measureInWindow
|
|
334
|
+
console.log('AutoPositionedPopup useTextInput measureInWindow=', { x, y, width, height });
|
|
300
335
|
// SIMPLE CENTER-BASED POSITIONING STRATEGY
|
|
301
336
|
const screenHeight = Dimensions.get('screen').height;
|
|
302
337
|
const screenCenter = screenHeight / 2;
|
|
303
|
-
console.log('AutoPositionedPopup
|
|
338
|
+
console.log('AutoPositionedPopup useTextInput measureInWindow =', { screenHeight, screenCenter, componentY: y });
|
|
304
339
|
// Simple rule: if component Y > screen center, show popup above; otherwise show below
|
|
305
340
|
if (y > screenCenter) {
|
|
306
341
|
console.log('AutoPositionedPopup with keyboard: showing above (Y > center)');
|
|
@@ -310,7 +345,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
310
345
|
console.log('AutoPositionedPopup with keyboard: showing below (Y <= center)');
|
|
311
346
|
ref_listPos.current = { x: x, y: y + height, width: width };
|
|
312
347
|
}
|
|
313
|
-
console.log('AutoPositionedPopup ref_listPos.current=', ref_listPos.current);
|
|
348
|
+
console.log('AutoPositionedPopup useTextInput ref_listPos.current=', ref_listPos.current);
|
|
314
349
|
setRootViewNativeStyle(tag, {
|
|
315
350
|
top: (_a = ref_listPos.current) === null || _a === void 0 ? void 0 : _a.y,
|
|
316
351
|
left: popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.left,
|
|
@@ -321,8 +356,9 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
321
356
|
hasShownRootView.current = true;
|
|
322
357
|
});
|
|
323
358
|
}
|
|
324
|
-
else if (!isKeyboardFullyShown && ref_isFocus.current) {
|
|
325
|
-
|
|
359
|
+
else if (!isKeyboardFullyShown && ref_isFocus.current && keyboardStateChanged) {
|
|
360
|
+
// Only execute close logic when keyboard state actually changes from true to false
|
|
361
|
+
console.log('AutoPositionedPopup isKeyboardFullyShown useEffect removeRootView (keyboard state changed)=', { tag, forceRemoveAllRootViewOnItemSelected, keyboardStateChanged });
|
|
326
362
|
removeRootView(tag, forceRemoveAllRootViewOnItemSelected);
|
|
327
363
|
setState((prevState) => {
|
|
328
364
|
return Object.assign(Object.assign({}, prevState), { isFocus: false });
|
|
@@ -335,10 +371,10 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
335
371
|
else {
|
|
336
372
|
if (state.isFocus) {
|
|
337
373
|
(_b = refAutoPositionedPopup.current) === null || _b === void 0 ? void 0 : _b.measureInWindow((x, y, width, height) => {
|
|
338
|
-
console.log('AutoPositionedPopup measureInWindow
|
|
374
|
+
console.log('AutoPositionedPopup !useTextInput measureInWindow=', { x, y, width, height });
|
|
339
375
|
// INTELLIGENT POSITION CALCULATION - MODIFIED VERSION WITH STATUS BAR SAFETY
|
|
340
376
|
const calculateOptimalPosition = (componentY, componentHeight, popupHeight) => {
|
|
341
|
-
console.log('🔥🔥🔥 NEW CALCULATE OPTIMAL POSITION FUNCTION EXECUTING 🔥🔥🔥');
|
|
377
|
+
console.log('AutoPositionedPopup 🔥🔥🔥 NEW CALCULATE OPTIMAL POSITION FUNCTION EXECUTING 🔥🔥🔥');
|
|
342
378
|
// Use window height (visible area) instead of screen height (includes status bar)
|
|
343
379
|
const windowHeight = Dimensions.get('window').height;
|
|
344
380
|
const visibleAreaCenter = windowHeight / 2;
|
|
@@ -355,7 +391,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
355
391
|
}
|
|
356
392
|
};
|
|
357
393
|
const statusBarHeight = getStatusBarHeight();
|
|
358
|
-
console.log('🔥 Cross-platform StatusBar height:', statusBarHeight, 'Platform:', Platform.OS);
|
|
394
|
+
console.log('AutoPositionedPopup 🔥 Cross-platform StatusBar height:', statusBarHeight, 'Platform:', Platform.OS);
|
|
359
395
|
// Calculate component center point as requested
|
|
360
396
|
const componentCenterY = componentY + componentHeight / 2;
|
|
361
397
|
console.log('AutoPositionedPopup positioning data:', {
|
|
@@ -403,7 +439,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
403
439
|
// Strong platform adjustment - much smaller for Android bottom components
|
|
404
440
|
const platformMultiplier = Platform.OS === 'ios' ? 1.0 : (isInBottomHalf ? 0.5 : 0.9);
|
|
405
441
|
const finalSpacing = Math.max(baseSpacing, relativeSpacing) * edgeProximityFactor * platformMultiplier;
|
|
406
|
-
console.log('🔥 Advanced spacing calculation:', {
|
|
442
|
+
console.log('AutoPositionedPopup 🔥 Advanced spacing calculation:', {
|
|
407
443
|
componentCenter,
|
|
408
444
|
screenCenter,
|
|
409
445
|
distanceFromCenter,
|
|
@@ -439,7 +475,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
439
475
|
// Component in bottom half + enough space above = FORCE ABOVE
|
|
440
476
|
showAbove = true;
|
|
441
477
|
finalY = componentY - popupHeight + componentHeight / 2;
|
|
442
|
-
console.log('🔥 AutoPositionedPopup: FORCE ABOVE - bottom half component with enough space, finalY=', finalY);
|
|
478
|
+
console.log('AutoPositionedPopup 🔥 AutoPositionedPopup: FORCE ABOVE - bottom half component with enough space, finalY=', finalY);
|
|
443
479
|
}
|
|
444
480
|
else if (!isInBottomHalf && spaceBelow >= popupHeight) {
|
|
445
481
|
// Component in top half + enough space below = show below
|
|
@@ -473,7 +509,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
473
509
|
}
|
|
474
510
|
}
|
|
475
511
|
// Enhanced boundary check with detailed logging
|
|
476
|
-
console.log('🔥 Pre-boundary check:', {
|
|
512
|
+
console.log('AutoPositionedPopup 🔥 Pre-boundary check:', {
|
|
477
513
|
originalFinalY: finalY,
|
|
478
514
|
showAbove,
|
|
479
515
|
statusBarHeight,
|
|
@@ -485,21 +521,21 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
485
521
|
if (showAbove && finalY < statusBarHeight) {
|
|
486
522
|
const oldFinalY = finalY;
|
|
487
523
|
finalY = statusBarHeight;
|
|
488
|
-
console.log('🔥 BOUNDARY
|
|
524
|
+
console.log('AutoPositionedPopup 🔥 BOUNDARY : Above display adjusted for status bar:', oldFinalY, '->', finalY);
|
|
489
525
|
}
|
|
490
526
|
if (!showAbove && finalY + popupHeight > windowHeight) {
|
|
491
527
|
const oldFinalY = finalY;
|
|
492
528
|
finalY = windowHeight - popupHeight;
|
|
493
|
-
console.log('🔥 BOUNDARY
|
|
529
|
+
console.log('AutoPositionedPopup 🔥 BOUNDARY : Below display adjusted to fit window:', oldFinalY, '->', finalY);
|
|
494
530
|
}
|
|
495
531
|
// CRITICAL CHECK: Detect if boundary check is changing display direction
|
|
496
532
|
if (showAbove && finalY + popupHeight > componentY) {
|
|
497
|
-
console.log('🚨 WARNING: Above positioning may overlap with component!');
|
|
533
|
+
console.log('AutoPositionedPopup 🚨 WARNING: Above positioning may overlap with component!');
|
|
498
534
|
}
|
|
499
535
|
if (!showAbove && finalY < componentY + componentHeight) {
|
|
500
|
-
console.log('🚨 WARNING: Below positioning may overlap with component!');
|
|
536
|
+
console.log('AutoPositionedPopup 🚨 WARNING: Below positioning may overlap with component!');
|
|
501
537
|
}
|
|
502
|
-
console.log('🔥 Post-boundary check final result:', {
|
|
538
|
+
console.log('AutoPositionedPopup 🔥 Post-boundary check final result:', {
|
|
503
539
|
finalY,
|
|
504
540
|
showAbove,
|
|
505
541
|
'popupTop': finalY,
|
|
@@ -513,17 +549,15 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
513
549
|
const actualPopupHeight = CustomPopView && CustomPopViewStyle && typeof CustomPopViewStyle.height === 'number'
|
|
514
550
|
? CustomPopViewStyle.height
|
|
515
551
|
: listLayout.height;
|
|
516
|
-
console.log('🔥 Using actualPopupHeight for calculation:', actualPopupHeight,
|
|
552
|
+
console.log('AutoPositionedPopup 🔥 Using actualPopupHeight for calculation:', { actualPopupHeight, CustomPopView: !!CustomPopView });
|
|
517
553
|
const positionResult = calculateOptimalPosition(y, height, actualPopupHeight);
|
|
518
554
|
console.log('AutoPositionedPopup FINAL position result:', positionResult);
|
|
519
555
|
ref_listPos.current = { x: x, y: positionResult.finalY, width: width };
|
|
520
|
-
console.log('AutoPositionedPopup ref_listPos.current=', ref_listPos.current);
|
|
556
|
+
console.log('AutoPositionedPopup !useTextInput ref_listPos.current=', ref_listPos.current);
|
|
521
557
|
if (CustomPopView && CustomPopViewStyle) {
|
|
522
|
-
console.log('AutoPositionedPopup CustomPopViewStyle=', CustomPopViewStyle);
|
|
523
558
|
// Position already calculated correctly above, no need to recalculate
|
|
524
559
|
const PopViewComponent = CustomPopView();
|
|
525
|
-
console.log('AutoPositionedPopup addRootView
|
|
526
|
-
console.log('AutoPositionedPopup addRootView state.selectedItem=', state.selectedItem);
|
|
560
|
+
console.log('AutoPositionedPopup !useTextInput addRootView=', { CustomPopViewStyle, PopViewComponent, 'state.selectedItem': state.selectedItem });
|
|
527
561
|
addRootView({
|
|
528
562
|
id: tag,
|
|
529
563
|
style: !centerDisplay
|
|
@@ -545,7 +579,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
545
579
|
});
|
|
546
580
|
}
|
|
547
581
|
else {
|
|
548
|
-
console.log('AutoPositionedPopup addRootView tag=', tag);
|
|
582
|
+
console.log('AutoPositionedPopup !useTextInput addRootView tag=', tag);
|
|
549
583
|
addRootView({
|
|
550
584
|
id: tag,
|
|
551
585
|
style: {
|
|
@@ -555,7 +589,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
555
589
|
height: listLayout.height,
|
|
556
590
|
opacity: 1,
|
|
557
591
|
},
|
|
558
|
-
component: (<AutoPositionedPopupList tag={tag} updateState={updateState} fetchData={fetchData} pageSize={pageSize} renderItem={renderItem} selectedItem={state.selectedItem} localSearch={localSearch}/>),
|
|
592
|
+
component: (<AutoPositionedPopupList tag={tag} updateState={updateState} fetchData={fetchData} pageSize={pageSize} renderItem={renderItem} selectedItem={state.selectedItem} localSearch={localSearch} showListEmptyComponent={showListEmptyComponent} emptyText={emptyText}/>),
|
|
559
593
|
useModal: true,
|
|
560
594
|
onModalClose: () => {
|
|
561
595
|
console.log('AutoPositionedPopup onModalClose tag=', tag);
|
|
@@ -587,8 +621,8 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
587
621
|
CustomPopView,
|
|
588
622
|
CustomPopViewStyle,
|
|
589
623
|
forceRemoveAllRootViewOnItemSelected,
|
|
590
|
-
tag,
|
|
591
|
-
state.selectedItem,
|
|
624
|
+
tag, TextInputProps,
|
|
625
|
+
state.selectedItem, showListEmptyComponent
|
|
592
626
|
]);
|
|
593
627
|
// Imperative handle for parent component access
|
|
594
628
|
useImperativeHandle(parentRef, () => ({
|
|
@@ -600,7 +634,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
600
634
|
},
|
|
601
635
|
}), []);
|
|
602
636
|
const updateState = (key, value) => {
|
|
603
|
-
console.log('AutoPositionedPopup updateState
|
|
637
|
+
console.log('AutoPositionedPopup updateState=', { key, value });
|
|
604
638
|
setState((prevState) => (Object.assign(Object.assign({}, prevState), { [key]: value })));
|
|
605
639
|
if (key === 'selectedItem' && onItemSelected) {
|
|
606
640
|
onItemSelected(value);
|
|
@@ -615,6 +649,179 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
615
649
|
setSearchQuery('');
|
|
616
650
|
}
|
|
617
651
|
};
|
|
652
|
+
// Simple deep comparison function (for style objects only)
|
|
653
|
+
const shallowEqual = (obj1, obj2) => {
|
|
654
|
+
if (obj1 === obj2)
|
|
655
|
+
return true;
|
|
656
|
+
if (!obj1 || !obj2)
|
|
657
|
+
return false;
|
|
658
|
+
if (typeof obj1 !== 'object' || typeof obj2 !== 'object')
|
|
659
|
+
return false;
|
|
660
|
+
const keys1 = Object.keys(obj1);
|
|
661
|
+
const keys2 = Object.keys(obj2);
|
|
662
|
+
if (keys1.length !== keys2.length)
|
|
663
|
+
return false;
|
|
664
|
+
for (const key of keys1) {
|
|
665
|
+
if (obj1[key] !== obj2[key])
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
return true;
|
|
669
|
+
};
|
|
670
|
+
// Use useMemo to create stable props reference
|
|
671
|
+
// Only update when deep comparison detects real changes to avoid TextInput recreation due to reference changes during parent component redraws
|
|
672
|
+
const stableInputStyle = useMemo(() => {
|
|
673
|
+
if (!shallowEqual(stableInputStyleRef.current, inputStyle)) {
|
|
674
|
+
console.log(`AutoPositionedPopup inputStyle deep change detected, updating stable reference - tag: ${tag}`);
|
|
675
|
+
stableInputStyleRef.current = inputStyle;
|
|
676
|
+
}
|
|
677
|
+
return stableInputStyleRef.current;
|
|
678
|
+
}, [inputStyle, tag]);
|
|
679
|
+
const stableTextInputProps = useMemo(() => {
|
|
680
|
+
if (!shallowEqual(stableTextInputPropsRef.current, TextInputProps)) {
|
|
681
|
+
console.log(`AutoPositionedPopup TextInputProps deep change detected, updating stable reference - tag: ${tag}`);
|
|
682
|
+
stableTextInputPropsRef.current = TextInputProps;
|
|
683
|
+
}
|
|
684
|
+
return stableTextInputPropsRef.current;
|
|
685
|
+
}, [TextInputProps, tag]);
|
|
686
|
+
// Use useCallback to stabilize onFocus and onBlur callback references
|
|
687
|
+
// Prevent creating new callback functions during parent component redraws to avoid TextInput re-triggering focus
|
|
688
|
+
// Use ref to store latest state values to avoid adding frequently changing values to dependencies
|
|
689
|
+
const stateRef = useRef(state);
|
|
690
|
+
stateRef.current = state;
|
|
691
|
+
const handleTextInputFocus = useCallback(() => {
|
|
692
|
+
const currentTime = Date.now();
|
|
693
|
+
const timeSinceLastFocus = currentTime - lastFocusTimeRef.current;
|
|
694
|
+
console.log('AutoPositionedPopup onFocus=', {
|
|
695
|
+
tag,
|
|
696
|
+
'state.selectedItem': stateRef.current.selectedItem,
|
|
697
|
+
'hasTriggeredFocus.current=': hasTriggeredFocus.current,
|
|
698
|
+
'textInputRef.current=': textInputRef.current,
|
|
699
|
+
'ref_searchQuery.current=': ref_searchQuery.current,
|
|
700
|
+
'timeSinceLastFocus': timeSinceLastFocus,
|
|
701
|
+
'isKeyboardFullyShown': isKeyboardFullyShown,
|
|
702
|
+
'isFocusEventProcessing': isFocusEventProcessingRef.current
|
|
703
|
+
});
|
|
704
|
+
// Prevent rapid repeated triggers (repeated events within 300ms are ignored)
|
|
705
|
+
if (timeSinceLastFocus < 300) {
|
|
706
|
+
console.log('AutoPositionedPopup onFocus: Skip - event triggered too quickly (< 300ms)');
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
// Skip if keyboard is already open and focus has been handled
|
|
710
|
+
if (isKeyboardFullyShown && hasTriggeredFocus.current) {
|
|
711
|
+
console.log('AutoPositionedPopup onFocus: Skip - keyboard already open and focus handled');
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
// Prevent concurrent processing
|
|
715
|
+
if (isFocusEventProcessingRef.current) {
|
|
716
|
+
console.log('AutoPositionedPopup onFocus: Skip - processing another focus event');
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
isFocusEventProcessingRef.current = true;
|
|
720
|
+
lastFocusTimeRef.current = currentTime;
|
|
721
|
+
if (!hasTriggeredFocus.current) {
|
|
722
|
+
hasTriggeredFocus.current = true;
|
|
723
|
+
ref_isFocus.current = true;
|
|
724
|
+
if (stateRef.current.selectedItem) {
|
|
725
|
+
ref_searchQuery.current = stateRef.current.selectedItem.title;
|
|
726
|
+
}
|
|
727
|
+
if (textInputRef.current && ref_searchQuery.current) {
|
|
728
|
+
textInputRef.current.setNativeProps({
|
|
729
|
+
text: ref_searchQuery.current,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
// Delay resetting processing flag to avoid blocking subsequent legitimate focus events
|
|
734
|
+
setTimeout(() => {
|
|
735
|
+
isFocusEventProcessingRef.current = false;
|
|
736
|
+
}, 100);
|
|
737
|
+
}, [tag, isKeyboardFullyShown]); // Remove state.selectedItem, use stateRef instead
|
|
738
|
+
const handleTextInputBlur = useCallback(() => {
|
|
739
|
+
console.log('AutoPositionedPopup onBlur=', {
|
|
740
|
+
tag,
|
|
741
|
+
'textInputRef.current': textInputRef.current,
|
|
742
|
+
'isKeyboardFullyShown': isKeyboardFullyShown,
|
|
743
|
+
'hasTriggeredFocus.current': hasTriggeredFocus.current
|
|
744
|
+
});
|
|
745
|
+
// If keyboard is still open, this is a false trigger caused by parent component re-render, should not reset
|
|
746
|
+
if (isKeyboardFullyShown && hasTriggeredFocus.current) {
|
|
747
|
+
console.log('AutoPositionedPopup onBlur: Skip - keyboard still open, possibly caused by parent component re-render');
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
// Only reset internal state, do not actively close keyboard
|
|
751
|
+
// Keyboard will close naturally when TextInput loses focus, no need to manually call Keyboard.dismiss()
|
|
752
|
+
hasTriggeredFocus.current = false;
|
|
753
|
+
hasAddedRootView.current = false;
|
|
754
|
+
hasShownRootView.current = false;
|
|
755
|
+
ref_isFocus.current = false;
|
|
756
|
+
setState((prevState) => {
|
|
757
|
+
return Object.assign(Object.assign({}, prevState), { isFocus: false });
|
|
758
|
+
});
|
|
759
|
+
removeRootView(tag, forceRemoveAllRootViewOnItemSelected);
|
|
760
|
+
setSearchQuery('');
|
|
761
|
+
if (textInputRef.current) {
|
|
762
|
+
textInputRef.current.setNativeProps({ text: '' });
|
|
763
|
+
ref_searchQuery.current = '';
|
|
764
|
+
// Remove textInputRef.current.blur() - avoid forcing blur causing keyboard to close
|
|
765
|
+
}
|
|
766
|
+
// Remove Keyboard.dismiss() - let keyboard close naturally to avoid triggering keyboardDidHide event
|
|
767
|
+
}, [tag, isKeyboardFullyShown, forceRemoveAllRootViewOnItemSelected]);
|
|
768
|
+
// Wrap TextInput independently in useMemo to recreate only when key props change
|
|
769
|
+
// This avoids repeated ref callback triggers due to other props changes during parent component redraws
|
|
770
|
+
const memoizedTextInput = useMemo(() => {
|
|
771
|
+
console.log(`AutoPositionedPopup useMemo creating TextInput - tag: ${tag}, isFocus: ${state.isFocus}`);
|
|
772
|
+
if (!useTextInput || !state.isFocus) {
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
return (<RNTextInput ref={(ref) => {
|
|
776
|
+
// Monitor TextInput mounting and unmounting
|
|
777
|
+
if (ref && !textInputRef.current) {
|
|
778
|
+
console.log(`AutoPositionedPopup TextInput created/mounted - tag: ${tag}, ref:`, ref);
|
|
779
|
+
}
|
|
780
|
+
else if (!ref && textInputRef.current) {
|
|
781
|
+
console.log(`AutoPositionedPopup TextInput unmounted - tag: ${tag}`);
|
|
782
|
+
}
|
|
783
|
+
else if (ref && textInputRef.current && ref !== textInputRef.current) {
|
|
784
|
+
console.log(`AutoPositionedPopup TextInput replaced - tag: ${tag}, oldRef:`, textInputRef.current, 'newRef:', ref);
|
|
785
|
+
}
|
|
786
|
+
textInputRef.current = ref;
|
|
787
|
+
}} key={`textinput-${tag}`} style={[
|
|
788
|
+
styles.inputStyle,
|
|
789
|
+
stableInputStyle,
|
|
790
|
+
]} textAlign={stableTextInputProps['textAlign'] || 'left'} multiline={stableTextInputProps['multiline'] || false} numberOfLines={stableTextInputProps['numberOfLines'] || 1} onChangeText={(searchQuery) => {
|
|
791
|
+
ref_searchQuery.current = searchQuery;
|
|
792
|
+
console.log('AutoPositionedPopup onChangeText rootViews=', rootViews);
|
|
793
|
+
if (!localSearch) {
|
|
794
|
+
if (debounceTimerRef.current) {
|
|
795
|
+
clearTimeout(debounceTimerRef.current);
|
|
796
|
+
}
|
|
797
|
+
debounceTimerRef.current = setTimeout(() => {
|
|
798
|
+
emitQueryChange(ref_searchQuery.current);
|
|
799
|
+
}, 500);
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
emitQueryChange(ref_searchQuery.current);
|
|
803
|
+
}
|
|
804
|
+
}} placeholderTextColor={theme.colors.placeholderText} placeholder={placeholder} onKeyPress={(e) => {
|
|
805
|
+
if (e.nativeEvent.key === 'Enter') {
|
|
806
|
+
Keyboard.dismiss();
|
|
807
|
+
}
|
|
808
|
+
}} keyboardType={stableTextInputProps['keyboardType'] || 'default'} clearButtonMode="while-editing" returnKeyType={stableTextInputProps['returnKeyType'] || 'done'} maxLength={stableTextInputProps['maxLength'] || 100} accessibilityLabel="selectInput" accessible={true} autoFocus={stableTextInputProps['autoFocus'] || false} autoCorrect={false} underlineColorAndroid="transparent" editable={stableTextInputProps['editable'] || true} secureTextEntry={stableTextInputProps['secureTextEntry'] || false} defaultValue="" caretHidden={false} enablesReturnKeyAutomatically onFocus={handleTextInputFocus} onBlur={handleTextInputBlur} selectTextOnFocus={stableTextInputProps['selectTextOnFocus'] || false} onSubmitEditing={(e) => {
|
|
809
|
+
console.log('AutoPositionedPopup.tsx onSubmitEditing e.nativeEvent.text=', e.nativeEvent.text);
|
|
810
|
+
onSubmitEditing && onSubmitEditing(e);
|
|
811
|
+
}}/>);
|
|
812
|
+
}, [
|
|
813
|
+
tag, // tag 是稳定的
|
|
814
|
+
useTextInput, // useTextInput 是稳定的
|
|
815
|
+
state.isFocus, // isFocus 控制显示/隐藏
|
|
816
|
+
handleTextInputFocus, // useCallback wrapped, reference stable
|
|
817
|
+
handleTextInputBlur, // useCallback wrapped, reference stable
|
|
818
|
+
stableInputStyle, // Use stable inputStyle reference (after deep comparison)
|
|
819
|
+
stableTextInputProps, // Use stable TextInputProps reference (after deep comparison)
|
|
820
|
+
placeholder, // placeholder usually stable
|
|
821
|
+
onSubmitEditing, // onSubmitEditing usually stable
|
|
822
|
+
// No longer use original inputStyle and TextInputProps, use stable references instead
|
|
823
|
+
// Stable references only update when deep comparison detects actual content changes, avoiding frequent TextInput recreation during parent component redraws
|
|
824
|
+
]);
|
|
618
825
|
// Render the component following project implementation
|
|
619
826
|
return useMemo(() => {
|
|
620
827
|
var _a;
|
|
@@ -622,17 +829,20 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
622
829
|
return (<CustomRow>
|
|
623
830
|
<View style={[styles.contain, style]} ref={refAutoPositionedPopup}>
|
|
624
831
|
{!state.isFocus || !useTextInput ? (<TouchableOpacity style={[styles.AutoPositionedPopupBtn, AutoPositionedPopupBtnStyle]} disabled={AutoPositionedPopupBtnDisabled} onPress={() => {
|
|
625
|
-
console.log('AutoPositionedPopup onPress
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
832
|
+
console.log('AutoPositionedPopup onPress=', {
|
|
833
|
+
tag,
|
|
834
|
+
'state.isFocus': state.isFocus,
|
|
835
|
+
useTextInput,
|
|
836
|
+
'hasAddedRootView.current': hasAddedRootView.current,
|
|
837
|
+
'hasShownRootView.current': hasShownRootView.current,
|
|
838
|
+
'hasTriggeredFocus.current': hasTriggeredFocus.current,
|
|
839
|
+
'state.selectedItem': state.selectedItem
|
|
840
|
+
});
|
|
632
841
|
setState((prevState) => {
|
|
633
842
|
return Object.assign(Object.assign({}, prevState), { isFocus: true });
|
|
634
843
|
});
|
|
635
844
|
if (!hasAddedRootView.current && useTextInput) {
|
|
845
|
+
// TextInput version: hide first, show after keyboard is fully displayed
|
|
636
846
|
hasAddedRootView.current = true;
|
|
637
847
|
hasShownRootView.current = false;
|
|
638
848
|
addRootView({
|
|
@@ -644,10 +854,11 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
644
854
|
height: listLayout.height,
|
|
645
855
|
opacity: 0,
|
|
646
856
|
},
|
|
647
|
-
component: (<AutoPositionedPopupList tag={tag} updateState={updateState} fetchData={fetchData} pageSize={pageSize} renderItem={renderItem} selectedItem={state.selectedItem} localSearch={localSearch}/>),
|
|
857
|
+
component: (<AutoPositionedPopupList tag={tag} updateState={updateState} fetchData={fetchData} pageSize={pageSize} renderItem={renderItem} selectedItem={state.selectedItem} localSearch={localSearch} showListEmptyComponent={showListEmptyComponent} emptyText={emptyText}/>),
|
|
648
858
|
useModal: false,
|
|
649
859
|
});
|
|
650
860
|
}
|
|
861
|
+
console.log('AutoPositionedPopup onPress done');
|
|
651
862
|
}}>
|
|
652
863
|
{!btwChildren ? (<Text style={[
|
|
653
864
|
styles.searchQueryTxt,
|
|
@@ -656,87 +867,36 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
656
867
|
]} numberOfLines={1} ellipsizeMode={'tail'}>
|
|
657
868
|
{((_a = state.selectedItem) === null || _a === void 0 ? void 0 : _a.title) || placeholder}
|
|
658
869
|
</Text>) : (btwChildren())}
|
|
659
|
-
</TouchableOpacity>) : (
|
|
660
|
-
state.isFocus && (<RNTextInput ref={textInputRef} key="fixed-textinput-key" style={[
|
|
661
|
-
styles.inputStyle,
|
|
662
|
-
inputStyle,
|
|
663
|
-
]} textAlign={TextInputProps['textAlign'] || 'left'} multiline={TextInputProps['multiline'] || false} numberOfLines={TextInputProps['numberOfLines'] || 1} onChangeText={(searchQuery) => {
|
|
664
|
-
ref_searchQuery.current = searchQuery;
|
|
665
|
-
console.log('AutoPositionedPopup onChangeText rootViews=', rootViews);
|
|
666
|
-
if (!localSearch) {
|
|
667
|
-
if (debounceTimerRef.current) {
|
|
668
|
-
clearTimeout(debounceTimerRef.current);
|
|
669
|
-
}
|
|
670
|
-
debounceTimerRef.current = setTimeout(() => {
|
|
671
|
-
emitQueryChange(ref_searchQuery.current);
|
|
672
|
-
}, 500);
|
|
673
|
-
}
|
|
674
|
-
else {
|
|
675
|
-
emitQueryChange(ref_searchQuery.current);
|
|
676
|
-
}
|
|
677
|
-
}} placeholderTextColor={theme.colors.placeholderText} placeholder={placeholder} onKeyPress={(e) => {
|
|
678
|
-
if (e.nativeEvent.key === 'Enter') {
|
|
679
|
-
Keyboard.dismiss();
|
|
680
|
-
}
|
|
681
|
-
}} keyboardType={TextInputProps['keyboardType'] || 'default'} clearButtonMode="while-editing" returnKeyType={TextInputProps['returnKeyType'] || 'done'} maxLength={TextInputProps['maxLength'] || 100} accessibilityLabel="selectInput" accessible={true} autoFocus={TextInputProps['autoFocus'] || false} autoCorrect={false} underlineColorAndroid="transparent" editable={TextInputProps['editable'] || true} secureTextEntry={TextInputProps['secureTextEntry'] || false} defaultValue="" caretHidden={false} enablesReturnKeyAutomatically onFocus={() => {
|
|
682
|
-
console.log('AutoPositionedPopup onFocus tag=', tag, ' selectedItem=', state.selectedItem, ' hasTriggeredFocus.current=', hasTriggeredFocus.current, ' textInputRef.current=', textInputRef.current, ' ref_searchQuery.current=', ref_searchQuery.current);
|
|
683
|
-
if (!hasTriggeredFocus.current) {
|
|
684
|
-
hasTriggeredFocus.current = true;
|
|
685
|
-
ref_isFocus.current = true;
|
|
686
|
-
if (state.selectedItem) {
|
|
687
|
-
ref_searchQuery.current = state.selectedItem.title;
|
|
688
|
-
}
|
|
689
|
-
if (textInputRef.current && ref_searchQuery.current) {
|
|
690
|
-
textInputRef.current.setNativeProps({
|
|
691
|
-
text: ref_searchQuery.current,
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
}} onBlur={() => {
|
|
696
|
-
console.log('AutoPositionedPopup onBlur tag=', tag, 'textInputRef.current=', textInputRef.current);
|
|
697
|
-
hasTriggeredFocus.current = false;
|
|
698
|
-
hasAddedRootView.current = false; // 重置 RootView 狀態
|
|
699
|
-
hasShownRootView.current = false;
|
|
700
|
-
ref_isFocus.current = false;
|
|
701
|
-
setState((prevState) => {
|
|
702
|
-
return Object.assign(Object.assign({}, prevState), { isFocus: false });
|
|
703
|
-
});
|
|
704
|
-
removeRootView(tag, forceRemoveAllRootViewOnItemSelected);
|
|
705
|
-
setSearchQuery('');
|
|
706
|
-
if (textInputRef.current) {
|
|
707
|
-
textInputRef.current.setNativeProps({ text: '' });
|
|
708
|
-
ref_searchQuery.current = '';
|
|
709
|
-
textInputRef.current.blur();
|
|
710
|
-
}
|
|
711
|
-
Keyboard.dismiss();
|
|
712
|
-
}} selectTextOnFocus={TextInputProps['selectTextOnFocus'] || false} onSubmitEditing={(e) => {
|
|
713
|
-
console.log('AutoPositionedPopup.tsx onSubmitEditing e.nativeEvent.text=', e.nativeEvent.text);
|
|
714
|
-
onSubmitEditing && onSubmitEditing(e);
|
|
715
|
-
}}/>))}
|
|
870
|
+
</TouchableOpacity>) : (memoizedTextInput)}
|
|
716
871
|
</View>
|
|
717
872
|
</CustomRow>);
|
|
718
|
-
}, [
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
873
|
+
}, [
|
|
874
|
+
tag,
|
|
875
|
+
// ✅ CRITICAL FIX: Remove all props that may change frequently or are inline functions
|
|
876
|
+
// Changes to these props should not cause the entire component tree to recreate, especially TextInput
|
|
877
|
+
// fetchData, // ❌ Removed: inline function
|
|
878
|
+
// renderItem, // ❌ Removed: possibly inline function
|
|
879
|
+
// onItemSelected, // ❌ Removed: possibly inline function
|
|
880
|
+
// onSubmitEditing, // ❌ Removed: possibly inline function
|
|
723
881
|
localSearch,
|
|
724
|
-
placeholder,
|
|
725
|
-
textAlign,
|
|
882
|
+
// placeholder, // ❌ Removed: may change
|
|
883
|
+
// textAlign, // ❌ Removed: may change
|
|
726
884
|
pageSize,
|
|
727
885
|
selectedItem,
|
|
728
|
-
CustomRow,
|
|
886
|
+
// CustomRow, // ❌ Removed: inline function, new reference each time
|
|
729
887
|
useTextInput,
|
|
730
|
-
btwChildren,
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
CustomPopViewStyle,
|
|
888
|
+
// btwChildren, // ❌ Removed: inline function
|
|
889
|
+
// keyExtractor, // ❌ Removed: possibly inline function
|
|
890
|
+
// AutoPositionedPopupBtnStyle, // ❌ Removed: possibly inline object
|
|
891
|
+
// CustomPopView, // ❌ Removed: may change
|
|
892
|
+
// CustomPopViewStyle, // ❌ Removed: may change
|
|
736
893
|
forceRemoveAllRootViewOnItemSelected,
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
894
|
+
state.isFocus,
|
|
895
|
+
showListEmptyComponent,
|
|
896
|
+
emptyText,
|
|
897
|
+
// ✅ Removed most dependencies that may cause re-rendering, keeping only core dependencies that truly affect component structure
|
|
898
|
+
// This prevents TextInput recreation due to inline functions/objects during parent component redraws
|
|
899
|
+
]);
|
|
740
900
|
}));
|
|
741
901
|
export default AutoPositionedPopup;
|
|
742
902
|
//# sourceMappingURL=AutoPositionedPopup.js.map
|