react-native-auto-positioned-popup 1.0.12 → 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/lib/AutoPositionedPopup.d.ts.map +1 -1
- package/lib/AutoPositionedPopup.js +287 -128
- package/lib/AutoPositionedPopup.js.map +1 -1
- package/lib/AutoPositionedPopupProps.d.ts +6 -0
- package/lib/AutoPositionedPopupProps.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/AutoPositionedPopup.tsx +356 -217
- package/src/AutoPositionedPopupProps.ts +13 -7
|
@@ -88,9 +88,7 @@ const ListItem: React.FC<{
|
|
|
88
88
|
rootViewsRef.current = rootViews;
|
|
89
89
|
}, [rootViews]);
|
|
90
90
|
return useMemo(() => {
|
|
91
|
-
// console.log('AutoPositionedPopup.tsx ListItem
|
|
92
|
-
console.log('AutoPositionedPopup.tsx ListItem item=', item);
|
|
93
|
-
console.log('AutoPositionedPopup.tsx ListItem selectedItem=', selectedItem);
|
|
91
|
+
// console.log('AutoPositionedPopup.tsx ListItem=', {index, item, selectedItem});
|
|
94
92
|
const isSelected = item.id === selectedItem?.id;
|
|
95
93
|
return (
|
|
96
94
|
<TouchableOpacity
|
|
@@ -132,7 +130,7 @@ interface AutoPositionedPopupListProps {
|
|
|
132
130
|
selectedItem?: SelectedItem;
|
|
133
131
|
localSearch?: boolean;
|
|
134
132
|
pageSize?: number;
|
|
135
|
-
showListEmptyComponent?:boolean;
|
|
133
|
+
showListEmptyComponent?: boolean;
|
|
136
134
|
emptyText?: string;
|
|
137
135
|
}
|
|
138
136
|
|
|
@@ -145,7 +143,7 @@ const AutoPositionedPopupList: React.FC<AutoPositionedPopupListProps> = memo(
|
|
|
145
143
|
renderItem,
|
|
146
144
|
selectedItem,
|
|
147
145
|
localSearch,
|
|
148
|
-
pageSize,showListEmptyComponent,emptyText
|
|
146
|
+
pageSize, showListEmptyComponent, emptyText
|
|
149
147
|
}: AutoPositionedPopupListProps): React.JSX.Element => {
|
|
150
148
|
const [state, setState] = useState<{
|
|
151
149
|
selectedItem?: SelectedItem;
|
|
@@ -176,8 +174,8 @@ const AutoPositionedPopupList: React.FC<AutoPositionedPopupListProps> = memo(
|
|
|
176
174
|
};
|
|
177
175
|
}, []);
|
|
178
176
|
// useEffect(() => {
|
|
179
|
-
// //
|
|
180
|
-
// //
|
|
177
|
+
// // Listen to TextInput events, refresh list when received, not dependent on global searchQuery
|
|
178
|
+
// // Sync the latest searchQuery to list-specific ref for _fetchData to use
|
|
181
179
|
// ref_searchQuery.current = searchQuery;
|
|
182
180
|
// console.log('AutoPositionedPopupList useEffect searchQuery=', searchQuery);
|
|
183
181
|
// console.log('AutoPositionedPopupList useEffect state.localData=', state.localData);
|
|
@@ -212,10 +210,7 @@ const AutoPositionedPopupList: React.FC<AutoPositionedPopupListProps> = memo(
|
|
|
212
210
|
pageIndex,
|
|
213
211
|
pageSize: currentPageSize,
|
|
214
212
|
}: FetchDataParams): Promise<ListData | null> => {
|
|
215
|
-
console.log('AutoPositionedPopupList _fetchData
|
|
216
|
-
console.log('AutoPositionedPopupList _fetchData state.localData=', state.localData);
|
|
217
|
-
console.log('AutoPositionedPopupList _fetchData ref_searchQuery.current=', ref_searchQuery.current);
|
|
218
|
-
console.log('AutoPositionedPopupList _fetchData localSearch=', localSearch);
|
|
213
|
+
console.log('AutoPositionedPopupList _fetchData=', {pageIndex, pageSize: currentPageSize, 'state.localData': state.localData, 'ref_searchQuery.current': ref_searchQuery.current, localSearch});
|
|
219
214
|
if (localSearch && state.localData.length > 0) {
|
|
220
215
|
const result: SelectedItem[] = state.localData.filter((item: SelectedItem) => {
|
|
221
216
|
return item.title?.toLowerCase().includes(ref_searchQuery.current.toLowerCase());
|
|
@@ -291,7 +286,7 @@ const AutoPositionedPopupList: React.FC<AutoPositionedPopupListProps> = memo(
|
|
|
291
286
|
searchQuery,
|
|
292
287
|
localSearch,
|
|
293
288
|
pageSize,
|
|
294
|
-
rootViewsRef,showListEmptyComponent,emptyText
|
|
289
|
+
rootViewsRef, showListEmptyComponent, emptyText
|
|
295
290
|
]);
|
|
296
291
|
}
|
|
297
292
|
);
|
|
@@ -318,7 +313,7 @@ const AutoPositionedPopup = memo(
|
|
|
318
313
|
AutoPositionedPopupBtnStyle,
|
|
319
314
|
placeholder = 'Please Select',
|
|
320
315
|
onSubmitEditing,
|
|
321
|
-
TextInputProps = {},
|
|
316
|
+
TextInputProps = {autoFocus: true},
|
|
322
317
|
inputStyle,
|
|
323
318
|
labelStyle,
|
|
324
319
|
popUpViewStyle = {left: '5%', width: '90%'},
|
|
@@ -360,7 +355,7 @@ const AutoPositionedPopup = memo(
|
|
|
360
355
|
centerDisplay = false,
|
|
361
356
|
selectedItemBackgroundColor = 'rgba(116, 116, 128, 0.08)',
|
|
362
357
|
textAlign = 'right',
|
|
363
|
-
CustomPopView = undefined, CustomPopViewStyle,showListEmptyComponent=true,emptyText=''
|
|
358
|
+
CustomPopView = undefined, CustomPopViewStyle, showListEmptyComponent = true, emptyText = ''
|
|
364
359
|
} = props;
|
|
365
360
|
// State management similar to project implementation
|
|
366
361
|
const [state, setState] = useState<StateProps>({
|
|
@@ -381,6 +376,20 @@ const AutoPositionedPopup = memo(
|
|
|
381
376
|
const keyboardVisibleRef = useRef(false);
|
|
382
377
|
const refAutoPositionedPopup = useRef<View>(null);
|
|
383
378
|
const ref_searchQuery = useRef<string>('');
|
|
379
|
+
// Add ref to track previous keyboard state to avoid false triggers during parent component re-renders
|
|
380
|
+
const prevIsKeyboardFullyShownRef = useRef<boolean>(false);
|
|
381
|
+
const prevPropsRef = useRef<{
|
|
382
|
+
CustomPopView?: any;
|
|
383
|
+
CustomPopViewStyle?: any;
|
|
384
|
+
TextInputProps?: any;
|
|
385
|
+
}>({});
|
|
386
|
+
// Add ref to prevent onFocus/onBlur loop triggers during parent component re-renders
|
|
387
|
+
const lastFocusTimeRef = useRef<number>(0);
|
|
388
|
+
const isFocusEventProcessingRef = useRef<boolean>(false);
|
|
389
|
+
// Add ref to stabilize TextInput props reference
|
|
390
|
+
// Only update when deep comparison detects real changes to avoid TextInput recreation due to reference changes during parent component redraws
|
|
391
|
+
const stableInputStyleRef = useRef<any>(inputStyle);
|
|
392
|
+
const stableTextInputPropsRef = useRef<any>(TextInputProps);
|
|
384
393
|
// Simple keyboard status tracking (alternative to useKeyboardStatus hook)
|
|
385
394
|
// Legacy state for compatibility
|
|
386
395
|
const [isVisible, setIsVisible] = useState(false);
|
|
@@ -406,8 +415,7 @@ const AutoPositionedPopup = memo(
|
|
|
406
415
|
useEffect(() => {
|
|
407
416
|
(async () => {
|
|
408
417
|
})();
|
|
409
|
-
console.log(`AutoPositionedPopup componentDidMount tag
|
|
410
|
-
console.log('AutoPositionedPopup componentDidMount CustomPopView=', CustomPopView);
|
|
418
|
+
console.log(`AutoPositionedPopup componentDidMount=`, {tag, CustomPopView});
|
|
411
419
|
//componentWillUnmount
|
|
412
420
|
return () => {
|
|
413
421
|
console.log(`AutoPositionedPopup componentWillUnmount tag=`, tag);
|
|
@@ -440,8 +448,7 @@ const AutoPositionedPopup = memo(
|
|
|
440
448
|
}
|
|
441
449
|
}, [rootViews]);
|
|
442
450
|
useEffect(() => {
|
|
443
|
-
console.log('AutoPositionedPopup useEffect tag=', tag);
|
|
444
|
-
console.log('AutoPositionedPopup useEffect selectedItem=', selectedItem);
|
|
451
|
+
console.log('AutoPositionedPopup useEffect [selectedItem, state.selectedItem, tag]=', {tag, selectedItem, 'state.selectedItem': state.selectedItem});
|
|
445
452
|
console.log('AutoPositionedPopup useEffect state.selectedItem=', state.selectedItem);
|
|
446
453
|
if (state.selectedItem?.id !== selectedItem?.id || state.selectedItem?.title !== selectedItem?.title) {
|
|
447
454
|
console.log('AutoPositionedPopup useEffect selectedItem!=state.selectedItem');
|
|
@@ -454,26 +461,55 @@ const AutoPositionedPopup = memo(
|
|
|
454
461
|
}
|
|
455
462
|
}, [selectedItem, state.selectedItem, tag]);
|
|
456
463
|
useEffect(() => {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
464
|
+
// Detect if keyboard state has actually changed to avoid false triggers during parent component re-renders
|
|
465
|
+
const keyboardStateChanged = prevIsKeyboardFullyShownRef.current !== isKeyboardFullyShown;
|
|
466
|
+
const propsChanged =
|
|
467
|
+
prevPropsRef.current.CustomPopView !== CustomPopView ||
|
|
468
|
+
prevPropsRef.current.CustomPopViewStyle !== CustomPopViewStyle ||
|
|
469
|
+
prevPropsRef.current.TextInputProps !== TextInputProps;
|
|
470
|
+
console.log('AutoPositionedPopup useEffect [isKeyboardFullyShown,\n' +
|
|
471
|
+
' state.isFocus,\n' +
|
|
472
|
+
' useTextInput,\n' +
|
|
473
|
+
' CustomPopView,\n' +
|
|
474
|
+
' CustomPopViewStyle,\n' +
|
|
475
|
+
' forceRemoveAllRootViewOnItemSelected,\n' +
|
|
476
|
+
' tag, TextInputProps,\n' +
|
|
477
|
+
' state.selectedItem, showListEmptyComponent\n' +
|
|
478
|
+
' ]=', {
|
|
479
|
+
tag,
|
|
480
|
+
'state.isFocus': state.isFocus,
|
|
481
|
+
isKeyboardFullyShown,
|
|
482
|
+
'ref_isFocus.current': ref_isFocus.current,
|
|
483
|
+
'ref_isKeyboardFullyShown.current': ref_isKeyboardFullyShown.current,
|
|
484
|
+
useTextInput, TextInputProps,
|
|
485
|
+
'hasAddedRootView.current': hasAddedRootView.current,
|
|
486
|
+
'hasShownRootView.current': hasShownRootView.current,
|
|
487
|
+
'keyboardStateChanged': keyboardStateChanged,
|
|
488
|
+
'propsChanged': propsChanged
|
|
489
|
+
});
|
|
490
|
+
// Update ref to record current state
|
|
491
|
+
prevIsKeyboardFullyShownRef.current = isKeyboardFullyShown;
|
|
492
|
+
prevPropsRef.current = {
|
|
493
|
+
CustomPopView,
|
|
494
|
+
CustomPopViewStyle,
|
|
495
|
+
TextInputProps
|
|
496
|
+
};
|
|
497
|
+
// Only execute logic when keyboard state actually changes or user actively operates
|
|
498
|
+
if (!keyboardStateChanged && hasAddedRootView.current) {
|
|
499
|
+
console.log('AutoPositionedPopup: Skip execution - parent component re-rendered but keyboard state unchanged');
|
|
500
|
+
// if (!ref_isFocus.current) {
|
|
501
|
+
// textInputRef.current?.focus()
|
|
502
|
+
// }
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
469
505
|
if (useTextInput) {
|
|
470
506
|
if (isKeyboardFullyShown && hasAddedRootView.current && !hasShownRootView.current && state.isFocus) {
|
|
471
507
|
refAutoPositionedPopup.current?.measureInWindow((x: number, y: number, width: number, height: number) => {
|
|
472
|
-
console.log('AutoPositionedPopup measureInWindow
|
|
508
|
+
console.log('AutoPositionedPopup useTextInput measureInWindow=', {x, y, width, height});
|
|
473
509
|
// SIMPLE CENTER-BASED POSITIONING STRATEGY
|
|
474
510
|
const screenHeight = Dimensions.get('screen').height;
|
|
475
511
|
const screenCenter = screenHeight / 2;
|
|
476
|
-
console.log('AutoPositionedPopup
|
|
512
|
+
console.log('AutoPositionedPopup useTextInput measureInWindow =', {screenHeight, screenCenter, componentY: y});
|
|
477
513
|
|
|
478
514
|
// Simple rule: if component Y > screen center, show popup above; otherwise show below
|
|
479
515
|
if (y > screenCenter) {
|
|
@@ -483,7 +519,7 @@ const AutoPositionedPopup = memo(
|
|
|
483
519
|
console.log('AutoPositionedPopup with keyboard: showing below (Y <= center)');
|
|
484
520
|
ref_listPos.current = {x: x, y: y + height, width: width};
|
|
485
521
|
}
|
|
486
|
-
console.log('AutoPositionedPopup ref_listPos.current=', ref_listPos.current);
|
|
522
|
+
console.log('AutoPositionedPopup useTextInput ref_listPos.current=', ref_listPos.current);
|
|
487
523
|
setRootViewNativeStyle(tag, {
|
|
488
524
|
top: ref_listPos.current?.y,
|
|
489
525
|
left: popUpViewStyle?.left,
|
|
@@ -493,12 +529,11 @@ const AutoPositionedPopup = memo(
|
|
|
493
529
|
});
|
|
494
530
|
hasShownRootView.current = true;
|
|
495
531
|
});
|
|
496
|
-
} else if (!isKeyboardFullyShown && ref_isFocus.current) {
|
|
532
|
+
} else if (!isKeyboardFullyShown && ref_isFocus.current && keyboardStateChanged) {
|
|
533
|
+
// Only execute close logic when keyboard state actually changes from true to false
|
|
497
534
|
console.log(
|
|
498
|
-
'AutoPositionedPopup isKeyboardFullyShown useEffect removeRootView
|
|
499
|
-
tag,
|
|
500
|
-
' forceRemoveAllRootViewOnItemSelected=',
|
|
501
|
-
forceRemoveAllRootViewOnItemSelected
|
|
535
|
+
'AutoPositionedPopup isKeyboardFullyShown useEffect removeRootView (keyboard state changed)=',
|
|
536
|
+
{tag, forceRemoveAllRootViewOnItemSelected, keyboardStateChanged}
|
|
502
537
|
);
|
|
503
538
|
removeRootView(tag, forceRemoveAllRootViewOnItemSelected);
|
|
504
539
|
setState((prevState) => {
|
|
@@ -514,11 +549,10 @@ const AutoPositionedPopup = memo(
|
|
|
514
549
|
} else {
|
|
515
550
|
if (state.isFocus) {
|
|
516
551
|
refAutoPositionedPopup.current?.measureInWindow((x: number, y: number, width: number, height: number) => {
|
|
517
|
-
console.log('AutoPositionedPopup measureInWindow
|
|
518
|
-
|
|
552
|
+
console.log('AutoPositionedPopup !useTextInput measureInWindow=', {x, y, width, height});
|
|
519
553
|
// INTELLIGENT POSITION CALCULATION - MODIFIED VERSION WITH STATUS BAR SAFETY
|
|
520
554
|
const calculateOptimalPosition = (componentY: number, componentHeight: number, popupHeight: number) => {
|
|
521
|
-
console.log('🔥🔥🔥 NEW CALCULATE OPTIMAL POSITION FUNCTION EXECUTING 🔥🔥🔥');
|
|
555
|
+
console.log('AutoPositionedPopup 🔥🔥🔥 NEW CALCULATE OPTIMAL POSITION FUNCTION EXECUTING 🔥🔥🔥');
|
|
522
556
|
|
|
523
557
|
// Use window height (visible area) instead of screen height (includes status bar)
|
|
524
558
|
const windowHeight = Dimensions.get('window').height;
|
|
@@ -536,7 +570,7 @@ const AutoPositionedPopup = memo(
|
|
|
536
570
|
}
|
|
537
571
|
};
|
|
538
572
|
const statusBarHeight = getStatusBarHeight();
|
|
539
|
-
console.log('🔥 Cross-platform StatusBar height:', statusBarHeight, 'Platform:', Platform.OS);
|
|
573
|
+
console.log('AutoPositionedPopup 🔥 Cross-platform StatusBar height:', statusBarHeight, 'Platform:', Platform.OS);
|
|
540
574
|
|
|
541
575
|
// Calculate component center point as requested
|
|
542
576
|
const componentCenterY = componentY + componentHeight / 2;
|
|
@@ -597,7 +631,7 @@ const AutoPositionedPopup = memo(
|
|
|
597
631
|
|
|
598
632
|
const finalSpacing = Math.max(baseSpacing, relativeSpacing) * edgeProximityFactor * platformMultiplier;
|
|
599
633
|
|
|
600
|
-
console.log('🔥 Advanced spacing calculation:', {
|
|
634
|
+
console.log('AutoPositionedPopup 🔥 Advanced spacing calculation:', {
|
|
601
635
|
componentCenter,
|
|
602
636
|
screenCenter,
|
|
603
637
|
distanceFromCenter,
|
|
@@ -634,41 +668,41 @@ const AutoPositionedPopup = memo(
|
|
|
634
668
|
// 'usableSpaceAbove >= needed': usableSpaceAbove >= popupHeight + POPUP_SPACING
|
|
635
669
|
// });
|
|
636
670
|
|
|
637
|
-
if (isInBottomHalf && usableSpaceAbove >= popupHeight
|
|
671
|
+
if (isInBottomHalf && usableSpaceAbove >= popupHeight) {
|
|
638
672
|
// Component in bottom half + enough space above = FORCE ABOVE
|
|
639
673
|
showAbove = true;
|
|
640
|
-
finalY = componentY - popupHeight +componentHeight/2;
|
|
641
|
-
console.log('🔥 AutoPositionedPopup: FORCE ABOVE - bottom half component with enough space, finalY=', finalY);
|
|
642
|
-
} else if (!isInBottomHalf && spaceBelow >= popupHeight
|
|
674
|
+
finalY = componentY - popupHeight + componentHeight / 2;
|
|
675
|
+
console.log('AutoPositionedPopup 🔥 AutoPositionedPopup: FORCE ABOVE - bottom half component with enough space, finalY=', finalY);
|
|
676
|
+
} else if (!isInBottomHalf && spaceBelow >= popupHeight) {
|
|
643
677
|
// Component in top half + enough space below = show below
|
|
644
678
|
showAbove = false;
|
|
645
|
-
finalY = componentY + componentHeight*2;
|
|
679
|
+
finalY = componentY + componentHeight * 2;
|
|
646
680
|
console.log('🔥 AutoPositionedPopup: Showing below - top half component with enough space, finalY=', finalY);
|
|
647
|
-
} else if (usableSpaceAbove >= popupHeight
|
|
681
|
+
} else if (usableSpaceAbove >= popupHeight) {
|
|
648
682
|
// Fallback: enough space above
|
|
649
683
|
showAbove = true;
|
|
650
|
-
finalY = componentY - popupHeight
|
|
684
|
+
finalY = componentY - popupHeight;
|
|
651
685
|
console.log('🔥 AutoPositionedPopup: Showing above - enough space available (fallback), finalY=', finalY);
|
|
652
|
-
} else if (spaceBelow >= popupHeight
|
|
686
|
+
} else if (spaceBelow >= popupHeight) {
|
|
653
687
|
// Fallback: enough space below
|
|
654
688
|
showAbove = false;
|
|
655
|
-
finalY = componentY + componentHeight
|
|
689
|
+
finalY = componentY + componentHeight;
|
|
656
690
|
console.log('🔥 AutoPositionedPopup: Showing below - enough space available (fallback), finalY=', finalY);
|
|
657
691
|
} else {
|
|
658
692
|
// Emergency fallback: choose larger space
|
|
659
693
|
if (usableSpaceAbove >= spaceBelow) {
|
|
660
694
|
showAbove = true;
|
|
661
|
-
finalY = Math.max(statusBarHeight, componentY - popupHeight
|
|
695
|
+
finalY = Math.max(statusBarHeight, componentY - popupHeight);
|
|
662
696
|
console.log('🔥 AutoPositionedPopup: Emergency above - larger space, finalY=', finalY);
|
|
663
697
|
} else {
|
|
664
698
|
showAbove = false;
|
|
665
|
-
finalY = componentY + componentHeight
|
|
699
|
+
finalY = componentY + componentHeight;
|
|
666
700
|
console.log('🔥 AutoPositionedPopup: Emergency below - larger space, finalY=', finalY);
|
|
667
701
|
}
|
|
668
702
|
}
|
|
669
703
|
|
|
670
704
|
// Enhanced boundary check with detailed logging
|
|
671
|
-
console.log('🔥 Pre-boundary check:', {
|
|
705
|
+
console.log('AutoPositionedPopup 🔥 Pre-boundary check:', {
|
|
672
706
|
originalFinalY: finalY,
|
|
673
707
|
showAbove,
|
|
674
708
|
statusBarHeight,
|
|
@@ -681,25 +715,25 @@ const AutoPositionedPopup = memo(
|
|
|
681
715
|
if (showAbove && finalY < statusBarHeight) {
|
|
682
716
|
const oldFinalY = finalY;
|
|
683
717
|
finalY = statusBarHeight;
|
|
684
|
-
console.log('🔥 BOUNDARY
|
|
718
|
+
console.log('AutoPositionedPopup 🔥 BOUNDARY : Above display adjusted for status bar:', oldFinalY, '->', finalY);
|
|
685
719
|
}
|
|
686
720
|
|
|
687
721
|
if (!showAbove && finalY + popupHeight > windowHeight) {
|
|
688
722
|
const oldFinalY = finalY;
|
|
689
723
|
finalY = windowHeight - popupHeight;
|
|
690
|
-
console.log('🔥 BOUNDARY
|
|
724
|
+
console.log('AutoPositionedPopup 🔥 BOUNDARY : Below display adjusted to fit window:', oldFinalY, '->', finalY);
|
|
691
725
|
}
|
|
692
726
|
|
|
693
727
|
// CRITICAL CHECK: Detect if boundary check is changing display direction
|
|
694
|
-
if (showAbove && finalY + popupHeight > componentY
|
|
695
|
-
console.log('🚨 WARNING: Above positioning may overlap with component!');
|
|
728
|
+
if (showAbove && finalY + popupHeight > componentY) {
|
|
729
|
+
console.log('AutoPositionedPopup 🚨 WARNING: Above positioning may overlap with component!');
|
|
696
730
|
}
|
|
697
731
|
|
|
698
|
-
if (!showAbove && finalY < componentY + componentHeight
|
|
699
|
-
console.log('🚨 WARNING: Below positioning may overlap with component!');
|
|
732
|
+
if (!showAbove && finalY < componentY + componentHeight) {
|
|
733
|
+
console.log('AutoPositionedPopup 🚨 WARNING: Below positioning may overlap with component!');
|
|
700
734
|
}
|
|
701
735
|
|
|
702
|
-
console.log('🔥 Post-boundary check final result:', {
|
|
736
|
+
console.log('AutoPositionedPopup 🔥 Post-boundary check final result:', {
|
|
703
737
|
finalY,
|
|
704
738
|
showAbove,
|
|
705
739
|
'popupTop': finalY,
|
|
@@ -715,21 +749,15 @@ const AutoPositionedPopup = memo(
|
|
|
715
749
|
const actualPopupHeight = CustomPopView && CustomPopViewStyle && typeof CustomPopViewStyle.height === 'number'
|
|
716
750
|
? CustomPopViewStyle.height
|
|
717
751
|
: listLayout.height;
|
|
718
|
-
|
|
719
|
-
console.log('🔥 Using actualPopupHeight for calculation:', actualPopupHeight, 'CustomPopView:', !!CustomPopView);
|
|
720
|
-
|
|
752
|
+
console.log('AutoPositionedPopup 🔥 Using actualPopupHeight for calculation:', {actualPopupHeight, CustomPopView: !!CustomPopView});
|
|
721
753
|
const positionResult = calculateOptimalPosition(y, height, actualPopupHeight);
|
|
722
754
|
console.log('AutoPositionedPopup FINAL position result:', positionResult);
|
|
723
|
-
|
|
724
755
|
ref_listPos.current = {x: x, y: positionResult.finalY, width: width};
|
|
725
|
-
console.log('AutoPositionedPopup ref_listPos.current=', ref_listPos.current);
|
|
726
|
-
|
|
756
|
+
console.log('AutoPositionedPopup !useTextInput ref_listPos.current=', ref_listPos.current);
|
|
727
757
|
if (CustomPopView && CustomPopViewStyle) {
|
|
728
|
-
console.log('AutoPositionedPopup CustomPopViewStyle=', CustomPopViewStyle);
|
|
729
758
|
// Position already calculated correctly above, no need to recalculate
|
|
730
759
|
const PopViewComponent = CustomPopView();
|
|
731
|
-
console.log('AutoPositionedPopup addRootView
|
|
732
|
-
console.log('AutoPositionedPopup addRootView state.selectedItem=', state.selectedItem);
|
|
760
|
+
console.log('AutoPositionedPopup !useTextInput addRootView=', {CustomPopViewStyle, PopViewComponent, 'state.selectedItem': state.selectedItem});
|
|
733
761
|
addRootView({
|
|
734
762
|
id: tag,
|
|
735
763
|
style: !centerDisplay
|
|
@@ -761,7 +789,7 @@ const AutoPositionedPopup = memo(
|
|
|
761
789
|
centerDisplay,
|
|
762
790
|
});
|
|
763
791
|
} else {
|
|
764
|
-
console.log('AutoPositionedPopup addRootView tag=', tag);
|
|
792
|
+
console.log('AutoPositionedPopup !useTextInput addRootView tag=', tag);
|
|
765
793
|
addRootView({
|
|
766
794
|
id: tag,
|
|
767
795
|
style: {
|
|
@@ -817,8 +845,8 @@ const AutoPositionedPopup = memo(
|
|
|
817
845
|
CustomPopView,
|
|
818
846
|
CustomPopViewStyle,
|
|
819
847
|
forceRemoveAllRootViewOnItemSelected,
|
|
820
|
-
tag,
|
|
821
|
-
state.selectedItem,showListEmptyComponent
|
|
848
|
+
tag, TextInputProps,
|
|
849
|
+
state.selectedItem, showListEmptyComponent
|
|
822
850
|
]);
|
|
823
851
|
// Imperative handle for parent component access
|
|
824
852
|
useImperativeHandle(
|
|
@@ -837,7 +865,7 @@ const AutoPositionedPopup = memo(
|
|
|
837
865
|
[]
|
|
838
866
|
);
|
|
839
867
|
const updateState = (key: string, value: SelectedItem) => {
|
|
840
|
-
console.log('AutoPositionedPopup updateState
|
|
868
|
+
console.log('AutoPositionedPopup updateState=', {key, value});
|
|
841
869
|
setState((prevState) => ({
|
|
842
870
|
...prevState,
|
|
843
871
|
[key]: value,
|
|
@@ -858,6 +886,225 @@ const AutoPositionedPopup = memo(
|
|
|
858
886
|
setSearchQuery('');
|
|
859
887
|
}
|
|
860
888
|
};
|
|
889
|
+
|
|
890
|
+
// Simple deep comparison function (for style objects only)
|
|
891
|
+
const shallowEqual = (obj1: any, obj2: any): boolean => {
|
|
892
|
+
if (obj1 === obj2) return true;
|
|
893
|
+
if (!obj1 || !obj2) return false;
|
|
894
|
+
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
|
|
895
|
+
|
|
896
|
+
const keys1 = Object.keys(obj1);
|
|
897
|
+
const keys2 = Object.keys(obj2);
|
|
898
|
+
if (keys1.length !== keys2.length) return false;
|
|
899
|
+
|
|
900
|
+
for (const key of keys1) {
|
|
901
|
+
if (obj1[key] !== obj2[key]) return false;
|
|
902
|
+
}
|
|
903
|
+
return true;
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
// Use useMemo to create stable props reference
|
|
907
|
+
// Only update when deep comparison detects real changes to avoid TextInput recreation due to reference changes during parent component redraws
|
|
908
|
+
const stableInputStyle = useMemo(() => {
|
|
909
|
+
if (!shallowEqual(stableInputStyleRef.current, inputStyle)) {
|
|
910
|
+
console.log(`AutoPositionedPopup inputStyle deep change detected, updating stable reference - tag: ${tag}`);
|
|
911
|
+
stableInputStyleRef.current = inputStyle;
|
|
912
|
+
}
|
|
913
|
+
return stableInputStyleRef.current;
|
|
914
|
+
}, [inputStyle, tag]);
|
|
915
|
+
|
|
916
|
+
const stableTextInputProps = useMemo(() => {
|
|
917
|
+
if (!shallowEqual(stableTextInputPropsRef.current, TextInputProps)) {
|
|
918
|
+
console.log(`AutoPositionedPopup TextInputProps deep change detected, updating stable reference - tag: ${tag}`);
|
|
919
|
+
stableTextInputPropsRef.current = TextInputProps;
|
|
920
|
+
}
|
|
921
|
+
return stableTextInputPropsRef.current;
|
|
922
|
+
}, [TextInputProps, tag]);
|
|
923
|
+
|
|
924
|
+
// Use useCallback to stabilize onFocus and onBlur callback references
|
|
925
|
+
// Prevent creating new callback functions during parent component redraws to avoid TextInput re-triggering focus
|
|
926
|
+
// Use ref to store latest state values to avoid adding frequently changing values to dependencies
|
|
927
|
+
const stateRef = useRef(state);
|
|
928
|
+
stateRef.current = state;
|
|
929
|
+
|
|
930
|
+
const handleTextInputFocus = useCallback(() => {
|
|
931
|
+
const currentTime = Date.now();
|
|
932
|
+
const timeSinceLastFocus = currentTime - lastFocusTimeRef.current;
|
|
933
|
+
console.log(
|
|
934
|
+
'AutoPositionedPopup onFocus=',
|
|
935
|
+
{
|
|
936
|
+
tag,
|
|
937
|
+
'state.selectedItem': stateRef.current.selectedItem,
|
|
938
|
+
'hasTriggeredFocus.current=': hasTriggeredFocus.current,
|
|
939
|
+
'textInputRef.current=': textInputRef.current,
|
|
940
|
+
'ref_searchQuery.current=': ref_searchQuery.current,
|
|
941
|
+
'timeSinceLastFocus': timeSinceLastFocus,
|
|
942
|
+
'isKeyboardFullyShown': isKeyboardFullyShown,
|
|
943
|
+
'isFocusEventProcessing': isFocusEventProcessingRef.current
|
|
944
|
+
}
|
|
945
|
+
);
|
|
946
|
+
// Prevent rapid repeated triggers (repeated events within 300ms are ignored)
|
|
947
|
+
if (timeSinceLastFocus < 300) {
|
|
948
|
+
console.log('AutoPositionedPopup onFocus: Skip - event triggered too quickly (< 300ms)');
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
// Skip if keyboard is already open and focus has been handled
|
|
952
|
+
if (isKeyboardFullyShown && hasTriggeredFocus.current) {
|
|
953
|
+
console.log('AutoPositionedPopup onFocus: Skip - keyboard already open and focus handled');
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
// Prevent concurrent processing
|
|
957
|
+
if (isFocusEventProcessingRef.current) {
|
|
958
|
+
console.log('AutoPositionedPopup onFocus: Skip - processing another focus event');
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
isFocusEventProcessingRef.current = true;
|
|
962
|
+
lastFocusTimeRef.current = currentTime;
|
|
963
|
+
if (!hasTriggeredFocus.current) {
|
|
964
|
+
hasTriggeredFocus.current = true;
|
|
965
|
+
ref_isFocus.current = true;
|
|
966
|
+
if (stateRef.current.selectedItem) {
|
|
967
|
+
ref_searchQuery.current = stateRef.current.selectedItem.title;
|
|
968
|
+
}
|
|
969
|
+
if (textInputRef.current && ref_searchQuery.current) {
|
|
970
|
+
textInputRef.current.setNativeProps({
|
|
971
|
+
text: ref_searchQuery.current,
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
// Delay resetting processing flag to avoid blocking subsequent legitimate focus events
|
|
976
|
+
setTimeout(() => {
|
|
977
|
+
isFocusEventProcessingRef.current = false;
|
|
978
|
+
}, 100);
|
|
979
|
+
}, [tag, isKeyboardFullyShown]); // Remove state.selectedItem, use stateRef instead
|
|
980
|
+
|
|
981
|
+
const handleTextInputBlur = useCallback(() => {
|
|
982
|
+
console.log(
|
|
983
|
+
'AutoPositionedPopup onBlur=',
|
|
984
|
+
{
|
|
985
|
+
tag,
|
|
986
|
+
'textInputRef.current': textInputRef.current,
|
|
987
|
+
'isKeyboardFullyShown': isKeyboardFullyShown,
|
|
988
|
+
'hasTriggeredFocus.current': hasTriggeredFocus.current
|
|
989
|
+
}
|
|
990
|
+
);
|
|
991
|
+
// If keyboard is still open, this is a false trigger caused by parent component re-render, should not reset
|
|
992
|
+
if (isKeyboardFullyShown && hasTriggeredFocus.current) {
|
|
993
|
+
console.log('AutoPositionedPopup onBlur: Skip - keyboard still open, possibly caused by parent component re-render');
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// Only reset internal state, do not actively close keyboard
|
|
998
|
+
// Keyboard will close naturally when TextInput loses focus, no need to manually call Keyboard.dismiss()
|
|
999
|
+
hasTriggeredFocus.current = false;
|
|
1000
|
+
hasAddedRootView.current = false;
|
|
1001
|
+
hasShownRootView.current = false;
|
|
1002
|
+
ref_isFocus.current = false;
|
|
1003
|
+
setState((prevState) => {
|
|
1004
|
+
return {
|
|
1005
|
+
...prevState,
|
|
1006
|
+
isFocus: false,
|
|
1007
|
+
};
|
|
1008
|
+
});
|
|
1009
|
+
removeRootView(tag, forceRemoveAllRootViewOnItemSelected);
|
|
1010
|
+
setSearchQuery('');
|
|
1011
|
+
if (textInputRef.current) {
|
|
1012
|
+
textInputRef.current.setNativeProps({text: ''});
|
|
1013
|
+
ref_searchQuery.current = '';
|
|
1014
|
+
// Remove textInputRef.current.blur() - avoid forcing blur causing keyboard to close
|
|
1015
|
+
}
|
|
1016
|
+
// Remove Keyboard.dismiss() - let keyboard close naturally to avoid triggering keyboardDidHide event
|
|
1017
|
+
}, [tag, isKeyboardFullyShown, forceRemoveAllRootViewOnItemSelected]);
|
|
1018
|
+
|
|
1019
|
+
// Wrap TextInput independently in useMemo to recreate only when key props change
|
|
1020
|
+
// This avoids repeated ref callback triggers due to other props changes during parent component redraws
|
|
1021
|
+
const memoizedTextInput = useMemo(() => {
|
|
1022
|
+
console.log(`AutoPositionedPopup useMemo creating TextInput - tag: ${tag}, isFocus: ${state.isFocus}`);
|
|
1023
|
+
if (!useTextInput || !state.isFocus) {
|
|
1024
|
+
return null;
|
|
1025
|
+
}
|
|
1026
|
+
return (
|
|
1027
|
+
<RNTextInput
|
|
1028
|
+
ref={(ref) => {
|
|
1029
|
+
// Monitor TextInput mounting and unmounting
|
|
1030
|
+
if (ref && !textInputRef.current) {
|
|
1031
|
+
console.log(`AutoPositionedPopup TextInput created/mounted - tag: ${tag}, ref:`, ref);
|
|
1032
|
+
} else if (!ref && textInputRef.current) {
|
|
1033
|
+
console.log(`AutoPositionedPopup TextInput unmounted - tag: ${tag}`);
|
|
1034
|
+
} else if (ref && textInputRef.current && ref !== textInputRef.current) {
|
|
1035
|
+
console.log(`AutoPositionedPopup TextInput replaced - tag: ${tag}, oldRef:`, textInputRef.current, 'newRef:', ref);
|
|
1036
|
+
}
|
|
1037
|
+
textInputRef.current = ref;
|
|
1038
|
+
}}
|
|
1039
|
+
key={`textinput-${tag}`}
|
|
1040
|
+
style={[
|
|
1041
|
+
styles.inputStyle,
|
|
1042
|
+
stableInputStyle,
|
|
1043
|
+
]}
|
|
1044
|
+
textAlign={stableTextInputProps['textAlign'] || 'left'}
|
|
1045
|
+
multiline={stableTextInputProps['multiline'] || false}
|
|
1046
|
+
numberOfLines={stableTextInputProps['numberOfLines'] || 1}
|
|
1047
|
+
onChangeText={(searchQuery) => {
|
|
1048
|
+
ref_searchQuery.current = searchQuery;
|
|
1049
|
+
console.log('AutoPositionedPopup onChangeText rootViews=', rootViews);
|
|
1050
|
+
if (!localSearch) {
|
|
1051
|
+
if (debounceTimerRef.current) {
|
|
1052
|
+
clearTimeout(debounceTimerRef.current);
|
|
1053
|
+
}
|
|
1054
|
+
debounceTimerRef.current = setTimeout(() => {
|
|
1055
|
+
emitQueryChange(ref_searchQuery.current);
|
|
1056
|
+
}, 500);
|
|
1057
|
+
} else {
|
|
1058
|
+
emitQueryChange(ref_searchQuery.current);
|
|
1059
|
+
}
|
|
1060
|
+
}}
|
|
1061
|
+
placeholderTextColor={theme.colors.placeholderText}
|
|
1062
|
+
placeholder={placeholder}
|
|
1063
|
+
onKeyPress={(e) => {
|
|
1064
|
+
if (e.nativeEvent.key === 'Enter') {
|
|
1065
|
+
Keyboard.dismiss();
|
|
1066
|
+
}
|
|
1067
|
+
}}
|
|
1068
|
+
keyboardType={stableTextInputProps['keyboardType'] || 'default'}
|
|
1069
|
+
clearButtonMode="while-editing"
|
|
1070
|
+
returnKeyType={stableTextInputProps['returnKeyType'] || 'done'}
|
|
1071
|
+
maxLength={stableTextInputProps['maxLength'] || 100}
|
|
1072
|
+
accessibilityLabel="selectInput"
|
|
1073
|
+
accessible={true}
|
|
1074
|
+
autoFocus={stableTextInputProps['autoFocus'] || false}
|
|
1075
|
+
autoCorrect={false}
|
|
1076
|
+
underlineColorAndroid="transparent"
|
|
1077
|
+
editable={stableTextInputProps['editable'] || true}
|
|
1078
|
+
secureTextEntry={stableTextInputProps['secureTextEntry'] || false}
|
|
1079
|
+
defaultValue=""
|
|
1080
|
+
caretHidden={false}
|
|
1081
|
+
enablesReturnKeyAutomatically
|
|
1082
|
+
onFocus={handleTextInputFocus}
|
|
1083
|
+
onBlur={handleTextInputBlur}
|
|
1084
|
+
selectTextOnFocus={stableTextInputProps['selectTextOnFocus'] || false}
|
|
1085
|
+
onSubmitEditing={(e: NativeSyntheticEvent<TextInputSubmitEditingEventData>) => {
|
|
1086
|
+
console.log(
|
|
1087
|
+
'AutoPositionedPopup.tsx onSubmitEditing e.nativeEvent.text=',
|
|
1088
|
+
e.nativeEvent.text
|
|
1089
|
+
);
|
|
1090
|
+
onSubmitEditing && onSubmitEditing(e);
|
|
1091
|
+
}}
|
|
1092
|
+
/>
|
|
1093
|
+
);
|
|
1094
|
+
}, [
|
|
1095
|
+
tag, // tag 是稳定的
|
|
1096
|
+
useTextInput, // useTextInput 是稳定的
|
|
1097
|
+
state.isFocus, // isFocus 控制显示/隐藏
|
|
1098
|
+
handleTextInputFocus, // useCallback wrapped, reference stable
|
|
1099
|
+
handleTextInputBlur, // useCallback wrapped, reference stable
|
|
1100
|
+
stableInputStyle, // Use stable inputStyle reference (after deep comparison)
|
|
1101
|
+
stableTextInputProps, // Use stable TextInputProps reference (after deep comparison)
|
|
1102
|
+
placeholder, // placeholder usually stable
|
|
1103
|
+
onSubmitEditing, // onSubmitEditing usually stable
|
|
1104
|
+
// No longer use original inputStyle and TextInputProps, use stable references instead
|
|
1105
|
+
// Stable references only update when deep comparison detects actual content changes, avoiding frequent TextInput recreation during parent component redraws
|
|
1106
|
+
]);
|
|
1107
|
+
|
|
861
1108
|
// Render the component following project implementation
|
|
862
1109
|
return useMemo(() => {
|
|
863
1110
|
console.log('AutoPositionedPopup render tag=', tag); // Now safe - circular dependency fixed
|
|
@@ -869,22 +1116,15 @@ const AutoPositionedPopup = memo(
|
|
|
869
1116
|
style={[styles.AutoPositionedPopupBtn, AutoPositionedPopupBtnStyle]}
|
|
870
1117
|
disabled={AutoPositionedPopupBtnDisabled}
|
|
871
1118
|
onPress={() => {
|
|
872
|
-
console.log('AutoPositionedPopup onPress
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
'
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
hasShownRootView.current
|
|
882
|
-
);
|
|
883
|
-
console.log(
|
|
884
|
-
'AutoPositionedPopup onPress hasTriggeredFocus.current=',
|
|
885
|
-
hasTriggeredFocus.current
|
|
886
|
-
);
|
|
887
|
-
console.log('AutoPositionedPopup onPress state.selectedItem=', state.selectedItem);
|
|
1119
|
+
console.log('AutoPositionedPopup onPress=', {
|
|
1120
|
+
tag,
|
|
1121
|
+
'state.isFocus': state.isFocus,
|
|
1122
|
+
useTextInput,
|
|
1123
|
+
'hasAddedRootView.current': hasAddedRootView.current,
|
|
1124
|
+
'hasShownRootView.current': hasShownRootView.current,
|
|
1125
|
+
'hasTriggeredFocus.current': hasTriggeredFocus.current,
|
|
1126
|
+
'state.selectedItem': state.selectedItem
|
|
1127
|
+
});
|
|
888
1128
|
setState((prevState) => {
|
|
889
1129
|
return {
|
|
890
1130
|
...prevState,
|
|
@@ -892,6 +1132,7 @@ const AutoPositionedPopup = memo(
|
|
|
892
1132
|
};
|
|
893
1133
|
});
|
|
894
1134
|
if (!hasAddedRootView.current && useTextInput) {
|
|
1135
|
+
// TextInput version: hide first, show after keyboard is fully displayed
|
|
895
1136
|
hasAddedRootView.current = true;
|
|
896
1137
|
hasShownRootView.current = false;
|
|
897
1138
|
addRootView({
|
|
@@ -919,6 +1160,7 @@ const AutoPositionedPopup = memo(
|
|
|
919
1160
|
useModal: false,
|
|
920
1161
|
});
|
|
921
1162
|
}
|
|
1163
|
+
console.log('AutoPositionedPopup onPress done')
|
|
922
1164
|
}}
|
|
923
1165
|
>
|
|
924
1166
|
{!btwChildren ? (
|
|
@@ -938,141 +1180,38 @@ const AutoPositionedPopup = memo(
|
|
|
938
1180
|
)}
|
|
939
1181
|
</TouchableOpacity>
|
|
940
1182
|
) : (
|
|
941
|
-
|
|
942
|
-
state.isFocus && (
|
|
943
|
-
<RNTextInput
|
|
944
|
-
ref={textInputRef}
|
|
945
|
-
key="fixed-textinput-key"
|
|
946
|
-
style={[
|
|
947
|
-
styles.inputStyle,
|
|
948
|
-
inputStyle,
|
|
949
|
-
]}
|
|
950
|
-
textAlign={TextInputProps['textAlign'] || 'left'}
|
|
951
|
-
multiline={TextInputProps['multiline'] || false}
|
|
952
|
-
numberOfLines={TextInputProps['numberOfLines'] || 1}
|
|
953
|
-
onChangeText={(searchQuery) => {
|
|
954
|
-
ref_searchQuery.current = searchQuery;
|
|
955
|
-
console.log('AutoPositionedPopup onChangeText rootViews=', rootViews);
|
|
956
|
-
if (!localSearch) {
|
|
957
|
-
if (debounceTimerRef.current) {
|
|
958
|
-
clearTimeout(debounceTimerRef.current);
|
|
959
|
-
}
|
|
960
|
-
debounceTimerRef.current = setTimeout(() => {
|
|
961
|
-
emitQueryChange(ref_searchQuery.current);
|
|
962
|
-
}, 500);
|
|
963
|
-
} else {
|
|
964
|
-
emitQueryChange(ref_searchQuery.current);
|
|
965
|
-
}
|
|
966
|
-
}}
|
|
967
|
-
placeholderTextColor={theme.colors.placeholderText}
|
|
968
|
-
placeholder={placeholder}
|
|
969
|
-
onKeyPress={(e) => {
|
|
970
|
-
if (e.nativeEvent.key === 'Enter') {
|
|
971
|
-
Keyboard.dismiss();
|
|
972
|
-
}
|
|
973
|
-
}}
|
|
974
|
-
keyboardType={TextInputProps['keyboardType'] || 'default'}
|
|
975
|
-
clearButtonMode="while-editing"
|
|
976
|
-
returnKeyType={TextInputProps['returnKeyType'] || 'done'}
|
|
977
|
-
maxLength={TextInputProps['maxLength'] || 100}
|
|
978
|
-
accessibilityLabel="selectInput"
|
|
979
|
-
accessible={true}
|
|
980
|
-
autoFocus={TextInputProps['autoFocus'] || false}
|
|
981
|
-
autoCorrect={false}
|
|
982
|
-
underlineColorAndroid="transparent"
|
|
983
|
-
editable={TextInputProps['editable'] || true}
|
|
984
|
-
secureTextEntry={TextInputProps['secureTextEntry'] || false}
|
|
985
|
-
defaultValue=""
|
|
986
|
-
caretHidden={false}
|
|
987
|
-
enablesReturnKeyAutomatically
|
|
988
|
-
onFocus={() => {
|
|
989
|
-
console.log(
|
|
990
|
-
'AutoPositionedPopup onFocus tag=',
|
|
991
|
-
tag,
|
|
992
|
-
' selectedItem=',
|
|
993
|
-
state.selectedItem,
|
|
994
|
-
' hasTriggeredFocus.current=',
|
|
995
|
-
hasTriggeredFocus.current,
|
|
996
|
-
' textInputRef.current=',
|
|
997
|
-
textInputRef.current,
|
|
998
|
-
' ref_searchQuery.current=',
|
|
999
|
-
ref_searchQuery.current
|
|
1000
|
-
);
|
|
1001
|
-
if (!hasTriggeredFocus.current) {
|
|
1002
|
-
hasTriggeredFocus.current = true;
|
|
1003
|
-
ref_isFocus.current = true;
|
|
1004
|
-
if (state.selectedItem) {
|
|
1005
|
-
ref_searchQuery.current = state.selectedItem.title;
|
|
1006
|
-
}
|
|
1007
|
-
if (textInputRef.current && ref_searchQuery.current) {
|
|
1008
|
-
textInputRef.current.setNativeProps({
|
|
1009
|
-
text: ref_searchQuery.current,
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
}}
|
|
1014
|
-
onBlur={() => {
|
|
1015
|
-
console.log(
|
|
1016
|
-
'AutoPositionedPopup onBlur tag=',
|
|
1017
|
-
tag,
|
|
1018
|
-
'textInputRef.current=',
|
|
1019
|
-
textInputRef.current
|
|
1020
|
-
);
|
|
1021
|
-
hasTriggeredFocus.current = false;
|
|
1022
|
-
hasAddedRootView.current = false; // 重置 RootView 狀態
|
|
1023
|
-
hasShownRootView.current = false;
|
|
1024
|
-
ref_isFocus.current = false;
|
|
1025
|
-
setState((prevState) => {
|
|
1026
|
-
return {
|
|
1027
|
-
...prevState,
|
|
1028
|
-
isFocus: false,
|
|
1029
|
-
};
|
|
1030
|
-
});
|
|
1031
|
-
removeRootView(tag, forceRemoveAllRootViewOnItemSelected);
|
|
1032
|
-
setSearchQuery('');
|
|
1033
|
-
if (textInputRef.current) {
|
|
1034
|
-
textInputRef.current.setNativeProps({text: ''});
|
|
1035
|
-
ref_searchQuery.current = '';
|
|
1036
|
-
textInputRef.current.blur();
|
|
1037
|
-
}
|
|
1038
|
-
Keyboard.dismiss();
|
|
1039
|
-
}}
|
|
1040
|
-
selectTextOnFocus={TextInputProps['selectTextOnFocus'] || false}
|
|
1041
|
-
onSubmitEditing={(e: NativeSyntheticEvent<TextInputSubmitEditingEventData>) => {
|
|
1042
|
-
console.log(
|
|
1043
|
-
'AutoPositionedPopup.tsx onSubmitEditing e.nativeEvent.text=',
|
|
1044
|
-
e.nativeEvent.text
|
|
1045
|
-
);
|
|
1046
|
-
onSubmitEditing && onSubmitEditing(e);
|
|
1047
|
-
}}
|
|
1048
|
-
/>
|
|
1049
|
-
)
|
|
1183
|
+
memoizedTextInput
|
|
1050
1184
|
)}
|
|
1051
1185
|
</View>
|
|
1052
1186
|
</CustomRow>
|
|
1053
1187
|
);
|
|
1054
|
-
}, [
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1188
|
+
}, [
|
|
1189
|
+
tag,
|
|
1190
|
+
// ✅ CRITICAL FIX: Remove all props that may change frequently or are inline functions
|
|
1191
|
+
// Changes to these props should not cause the entire component tree to recreate, especially TextInput
|
|
1192
|
+
// fetchData, // ❌ Removed: inline function
|
|
1193
|
+
// renderItem, // ❌ Removed: possibly inline function
|
|
1194
|
+
// onItemSelected, // ❌ Removed: possibly inline function
|
|
1195
|
+
// onSubmitEditing, // ❌ Removed: possibly inline function
|
|
1059
1196
|
localSearch,
|
|
1060
|
-
placeholder,
|
|
1061
|
-
textAlign,
|
|
1197
|
+
// placeholder, // ❌ Removed: may change
|
|
1198
|
+
// textAlign, // ❌ Removed: may change
|
|
1062
1199
|
pageSize,
|
|
1063
1200
|
selectedItem,
|
|
1064
|
-
CustomRow,
|
|
1201
|
+
// CustomRow, // ❌ Removed: inline function, new reference each time
|
|
1065
1202
|
useTextInput,
|
|
1066
|
-
btwChildren,
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
CustomPopViewStyle,
|
|
1203
|
+
// btwChildren, // ❌ Removed: inline function
|
|
1204
|
+
// keyExtractor, // ❌ Removed: possibly inline function
|
|
1205
|
+
// AutoPositionedPopupBtnStyle, // ❌ Removed: possibly inline object
|
|
1206
|
+
// CustomPopView, // ❌ Removed: may change
|
|
1207
|
+
// CustomPopViewStyle, // ❌ Removed: may change
|
|
1072
1208
|
forceRemoveAllRootViewOnItemSelected,
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1209
|
+
state.isFocus,
|
|
1210
|
+
showListEmptyComponent,
|
|
1211
|
+
emptyText,
|
|
1212
|
+
// ✅ Removed most dependencies that may cause re-rendering, keeping only core dependencies that truly affect component structure
|
|
1213
|
+
// This prevents TextInput recreation due to inline functions/objects during parent component redraws
|
|
1214
|
+
]);
|
|
1076
1215
|
}
|
|
1077
1216
|
)
|
|
1078
1217
|
);
|