react-native-auto-positioned-popup 1.0.5 → 1.0.6
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 +410 -365
- package/lib/AutoPositionedPopup.js.map +1 -0
- package/lib/AutoPositionedPopup.style.d.ts +29 -0
- package/lib/AutoPositionedPopup.style.d.ts.map +1 -1
- package/lib/AutoPositionedPopup.style.js +20 -2
- package/lib/AutoPositionedPopup.style.js.map +1 -0
- package/lib/AutoPositionedPopupProps.js +1 -0
- package/lib/AutoPositionedPopupProps.js.map +1 -0
- package/lib/KeyboardManager.d.ts +2 -0
- package/lib/KeyboardManager.d.ts.map +1 -0
- package/lib/KeyboardManager.js +57 -0
- package/lib/KeyboardManager.js.map +1 -0
- package/lib/RootViewContext.d.ts.map +1 -1
- package/lib/RootViewContext.js +1 -1
- package/lib/RootViewContext.js.map +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -0
- package/package.json +6 -5
- package/src/AutoPositionedPopup.style.ts +24 -3
- package/src/AutoPositionedPopup.tsx +599 -571
- package/src/KeyboardManager.tsx +82 -0
- package/src/RootViewContext.tsx +0 -1
- package/src/types/react-native-advanced-flatlist.d.ts +19 -0
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import React, { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
2
2
|
import { Dimensions, Keyboard, Text, TextInput as RNTextInput, TouchableOpacity, View, } from 'react-native';
|
|
3
|
-
|
|
3
|
+
// @ts-ignore - Skip type checking for third-party library with type issues
|
|
4
|
+
import { AdvancedFlatList as AdvancedFlatListLib } from 'react-native-advanced-flatlist';
|
|
5
|
+
// @ts-ignore - Direct import from source when using fake data
|
|
6
|
+
import AdvancedFlatListSource from 'react-native-advanced-flatlist/src/AdvancedFlatList.tsx';
|
|
4
7
|
import styles from './AutoPositionedPopup.style';
|
|
5
8
|
import { useRootView } from './RootViewContext';
|
|
9
|
+
import { useKeyboardStatus } from './KeyboardManager';
|
|
10
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
6
11
|
const queryChangeListeners = [];
|
|
7
12
|
const emitQueryChange = (query) => {
|
|
8
13
|
console.log('AutoPositionedPopup.tsx emitQueryChange query=', query, ' listeners=', queryChangeListeners.length);
|
|
@@ -26,67 +31,146 @@ const defaultTheme = {
|
|
|
26
31
|
},
|
|
27
32
|
};
|
|
28
33
|
// List item component for rendering individual items
|
|
29
|
-
const ListItem = memo(({ item, index, selectedItem,
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
const ListItem = memo(({ updateState, item, index, selectedItem, }) => {
|
|
35
|
+
const { addRootView, setRootViewNativeStyle, removeRootView, rootViews } = useRootView();
|
|
36
|
+
const rootViewsRef = useRef(rootViews);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
rootViewsRef.current = rootViews;
|
|
39
|
+
}, [rootViews]);
|
|
40
|
+
return useMemo(() => {
|
|
41
|
+
// console.log('AutoPositionedPopup.tsx ListItem index=', index);
|
|
42
|
+
// console.log('AutoPositionedPopup.tsx ListItem item=', item);
|
|
43
|
+
const isSelected = item.id === (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id);
|
|
44
|
+
return (<TouchableOpacity key={item.id} style={[
|
|
45
|
+
styles.commonModalRow,
|
|
46
|
+
{ backgroundColor: isSelected ? 'rgba(116, 116, 128, 0.08)' : 'transparent' },
|
|
47
|
+
]} onPress={() => {
|
|
48
|
+
// console.log('AutoPositionedPopup.tsx ListItem onPress item=', item); // Commented to prevent spam
|
|
49
|
+
// console.log('AutoPositionedPopup.tsx ListItem onPress rootViews=', rootViewsRef.current); // Commented to prevent spam
|
|
50
|
+
updateState('selectedItem', item);
|
|
51
|
+
}}>
|
|
52
|
+
<Text style={styles.ListItemCode} numberOfLines={1} ellipsizeMode="tail">
|
|
53
|
+
{item.title}
|
|
54
|
+
</Text>
|
|
55
|
+
</TouchableOpacity>);
|
|
56
|
+
}, [updateState, item, index, selectedItem, rootViewsRef]);
|
|
48
57
|
});
|
|
49
|
-
const
|
|
50
|
-
const [
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
const AutoPositionedPopupList = memo(({ tag, updateState, fetchData, keyExtractor = (item) => String(item.id), renderItem, selectedItem, localSearch, pageSize, }) => {
|
|
59
|
+
const [state, setState] = useState({
|
|
60
|
+
selectedItem: selectedItem,
|
|
61
|
+
localData: [],
|
|
62
|
+
});
|
|
63
|
+
// Define an interface that matches the methods we need from CsxFlatList
|
|
64
|
+
const ref_list = useRef(null);
|
|
65
|
+
const ref_searchQuery = useRef('');
|
|
66
|
+
const { searchQuery, setSearchQuery, rootViews } = useRootView();
|
|
67
|
+
const rootViewsRef = useRef(rootViews);
|
|
53
68
|
useEffect(() => {
|
|
54
|
-
|
|
55
|
-
}, [
|
|
56
|
-
|
|
69
|
+
rootViewsRef.current = rootViews;
|
|
70
|
+
}, [rootViews]);
|
|
71
|
+
/**
|
|
72
|
+
* componentDidMount && componentWillUnmount
|
|
73
|
+
*/
|
|
57
74
|
useEffect(() => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
(async () => {
|
|
76
|
+
})();
|
|
77
|
+
console.log(`AutoPositionedPopupList componentDidMount`);
|
|
78
|
+
//componentWillUnmount
|
|
79
|
+
return () => {
|
|
80
|
+
console.log(`AutoPositionedPopupList componentWillUnmount`);
|
|
81
|
+
setSearchQuery('');
|
|
82
|
+
};
|
|
83
|
+
}, []);
|
|
84
|
+
// useEffect(() => {
|
|
85
|
+
// // 監聽 TextInput 事件,收到就刷新列表,不依賴 global searchQuery
|
|
86
|
+
// // 將最新的 searchQuery 同步到 list 專用的 ref,供 _fetchData 使用
|
|
87
|
+
// ref_searchQuery.current = searchQuery;
|
|
88
|
+
// console.log('AutoPositionedPopupList useEffect searchQuery=', searchQuery);
|
|
89
|
+
// console.log('AutoPositionedPopupList useEffect state.localData=', state.localData);
|
|
90
|
+
// console.log('AutoPositionedPopupList useEffect ref_list.current=', ref_list.current);
|
|
91
|
+
// console.log('AutoPositionedPopupList useEffect localSearch=', localSearch);
|
|
92
|
+
// if (ref_list.current && (localSearch && state.localData.length > 0 || !localSearch)) {
|
|
93
|
+
// ref_list.current.scrollToTop();
|
|
94
|
+
// ref_list.current.refresh();
|
|
95
|
+
// }
|
|
96
|
+
// }, [searchQuery, state.localData, localSearch]);
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const unsubscribe = subscribeQueryChange((newQuery) => {
|
|
99
|
+
console.log('AutoPositionedPopupList useEffect subscribeQueryChange newQuery=', newQuery);
|
|
100
|
+
ref_searchQuery.current = newQuery;
|
|
101
|
+
if (ref_list.current) {
|
|
102
|
+
ref_list.current.scrollToTop();
|
|
103
|
+
ref_list.current.refresh();
|
|
82
104
|
}
|
|
83
105
|
});
|
|
84
106
|
return unsubscribe;
|
|
85
|
-
}, [
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
107
|
+
}, []);
|
|
108
|
+
const _updateState = (key, value) => {
|
|
109
|
+
console.log('AutoPositionedPopupList _updateState key=', key, ' value=', value);
|
|
110
|
+
setState((prevState) => (Object.assign(Object.assign({}, prevState), { [key]: value })));
|
|
111
|
+
console.log('AutoPositionedPopupList _updateState rootViews=', rootViewsRef.current);
|
|
112
|
+
updateState(key, value);
|
|
113
|
+
};
|
|
114
|
+
const _fetchData = async ({ pageIndex, pageSize: currentPageSize, }) => {
|
|
115
|
+
console.log('AutoPositionedPopupList _fetchData pageIndex=', pageIndex, ' pageSize=', currentPageSize);
|
|
116
|
+
console.log('AutoPositionedPopupList _fetchData state.localData=', state.localData);
|
|
117
|
+
console.log('AutoPositionedPopupList _fetchData ref_searchQuery.current=', ref_searchQuery.current);
|
|
118
|
+
console.log('AutoPositionedPopupList _fetchData localSearch=', localSearch);
|
|
119
|
+
if (localSearch && state.localData.length > 0) {
|
|
120
|
+
const result = state.localData.filter((item) => {
|
|
121
|
+
var _a;
|
|
122
|
+
return (_a = item.title) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(ref_searchQuery.current.toLowerCase());
|
|
123
|
+
});
|
|
124
|
+
console.log('AutoPositionedPopupList _fetchData localSearch result=', result);
|
|
125
|
+
return Promise.resolve({
|
|
126
|
+
items: result,
|
|
127
|
+
pageIndex: 0,
|
|
128
|
+
needLoadMore: false,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const res = await fetchData({
|
|
133
|
+
pageIndex,
|
|
134
|
+
pageSize: pageSize || 10,
|
|
135
|
+
searchQuery: ref_searchQuery.current,
|
|
136
|
+
});
|
|
137
|
+
console.log('AutoPositionedPopupList _fetchData res=', res);
|
|
138
|
+
if ((res === null || res === void 0 ? void 0 : res.items) && localSearch) {
|
|
139
|
+
setState((prevState) => {
|
|
140
|
+
return Object.assign(Object.assign({}, prevState), { localData: res.items });
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return Promise.resolve(res);
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
console.warn('Error in fetchData:', e);
|
|
147
|
+
}
|
|
148
|
+
console.log('AutoPositionedPopupList _fetchData res=', null);
|
|
149
|
+
return null;
|
|
150
|
+
};
|
|
151
|
+
const _renderItem = useCallback(({ item, index }) => {
|
|
152
|
+
return <ListItem item={item} index={index} updateState={_updateState} selectedItem={state.selectedItem}/>;
|
|
153
|
+
}, [state.selectedItem]);
|
|
154
|
+
return useMemo(() => {
|
|
155
|
+
console.log('AutoPositionedPopupList (global as any)?.$fake=', global === null || global === void 0 ? void 0 : global.$fake);
|
|
156
|
+
// Choose AdvancedFlatList version based on global.$fake
|
|
157
|
+
const AdvancedFlatListComponent = (global === null || global === void 0 ? void 0 : global.$fake) ? AdvancedFlatListSource : AdvancedFlatListLib;
|
|
158
|
+
return (<View style={[styles.baseModalView, styles.autoPositionedPopupList]}>
|
|
159
|
+
{/* @ts-ignore - Type assertion to bypass third-party library type issues */}
|
|
160
|
+
<AdvancedFlatListComponent style={[{ borderRadius: 0 }]} {...(ref_list && { ref: ref_list })} keyExtractor={keyExtractor} keyboardShouldPersistTaps={'always'} {...({ fetchData: _fetchData })} renderItem={renderItem || _renderItem}/>
|
|
161
|
+
</View>);
|
|
162
|
+
}, [tag,
|
|
163
|
+
updateState,
|
|
164
|
+
fetchData,
|
|
165
|
+
keyExtractor,
|
|
166
|
+
renderItem,
|
|
167
|
+
state.selectedItem,
|
|
168
|
+
state.localData,
|
|
169
|
+
searchQuery,
|
|
170
|
+
localSearch,
|
|
171
|
+
pageSize,
|
|
172
|
+
rootViewsRef,
|
|
173
|
+
]);
|
|
90
174
|
});
|
|
91
175
|
// List layout constants
|
|
92
176
|
const listLayout = {
|
|
@@ -95,14 +179,31 @@ const listLayout = {
|
|
|
95
179
|
// Main AutoPositionedPopup component
|
|
96
180
|
const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
97
181
|
console.log('AutoPositionedPopup props=', props);
|
|
98
|
-
const { tag, style, AutoPositionedPopupBtnStyle, placeholder = 'Please Select', onSubmitEditing, TextInputProps = {}, inputStyle, labelStyle, popUpViewStyle = { left: '5%', width: '90%' }, fetchData
|
|
182
|
+
const { tag, style, AutoPositionedPopupBtnStyle, placeholder = 'Please Select', onSubmitEditing, TextInputProps = {}, inputStyle, labelStyle, popUpViewStyle = { left: '5%', width: '90%' }, fetchData = async ({ pageIndex, pageSize, searchQuery, }) => {
|
|
183
|
+
const res = {
|
|
184
|
+
items: [],
|
|
185
|
+
pageIndex,
|
|
186
|
+
needLoadMore: false,
|
|
187
|
+
};
|
|
188
|
+
try {
|
|
189
|
+
// const res1: any[] = await $api.xxx(pageSize)
|
|
190
|
+
// console.log('${NAME} xxx res=', res)
|
|
191
|
+
// res.items = res1
|
|
192
|
+
// res.needLoadMore = res1.length === pageSize
|
|
193
|
+
}
|
|
194
|
+
catch (e) {
|
|
195
|
+
console.warn('Error in fetch operation:', e);
|
|
196
|
+
}
|
|
197
|
+
return res;
|
|
198
|
+
}, 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;
|
|
99
199
|
// State management similar to project implementation
|
|
100
200
|
const [state, setState] = useState({
|
|
101
201
|
isFocus: false,
|
|
102
202
|
selectedItem: selectedItem,
|
|
103
203
|
});
|
|
104
204
|
// Use RootView context
|
|
105
|
-
const { addRootView, removeRootView, rootViews, setSearchQuery
|
|
205
|
+
const { addRootView, setRootViewNativeStyle, removeRootView, rootViews, setSearchQuery } = useRootView();
|
|
206
|
+
const insets = useSafeAreaInsets();
|
|
106
207
|
const rootViewsRef = useRef(rootViews);
|
|
107
208
|
// Track TextInput focus and RootView states like project implementation
|
|
108
209
|
const hasTriggeredFocus = useRef(false);
|
|
@@ -114,19 +215,42 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
114
215
|
const ref_listPos = useRef();
|
|
115
216
|
const keyboardVisibleRef = useRef(false);
|
|
116
217
|
const refAutoPositionedPopup = useRef(null);
|
|
218
|
+
const ref_searchQuery = useRef('');
|
|
117
219
|
// Simple keyboard status tracking (alternative to useKeyboardStatus hook)
|
|
118
|
-
|
|
119
|
-
|
|
220
|
+
// Legacy state for compatibility
|
|
221
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
222
|
+
const [data, setData] = useState([]);
|
|
223
|
+
const [loading, setLoading] = useState(false);
|
|
224
|
+
const [popupPosition, setPopupPosition] = useState({ top: 0, left: 0, width: 0 });
|
|
225
|
+
// Refs for performance optimization
|
|
226
|
+
const containerRef = useRef(null);
|
|
227
|
+
const textInputRef = useRef(null);
|
|
228
|
+
const debounceTimerRef = useRef(null);
|
|
229
|
+
const searchQueryRef = useRef(''); // Use ref instead of state to avoid re-renders
|
|
230
|
+
// Refs to store latest values for useEffect without adding to dependency array
|
|
231
|
+
const dataRef = useRef(data);
|
|
232
|
+
const isKeyboardFullyShown = useKeyboardStatus();
|
|
233
|
+
const theme = defaultTheme;
|
|
234
|
+
/**
|
|
235
|
+
* componentDidMount && componentWillUnmount
|
|
236
|
+
*/
|
|
120
237
|
useEffect(() => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
});
|
|
238
|
+
(async () => {
|
|
239
|
+
})();
|
|
240
|
+
console.log(`AutoPositionedPopup componentDidMount tag=`, tag);
|
|
241
|
+
console.log('AutoPositionedPopup componentDidMount CustomPopView=', CustomPopView);
|
|
242
|
+
//componentWillUnmount
|
|
127
243
|
return () => {
|
|
128
|
-
|
|
129
|
-
|
|
244
|
+
console.log(`AutoPositionedPopup componentWillUnmount tag=`, tag);
|
|
245
|
+
removeRootView(tag, forceRemoveAllRootViewOnItemSelected, rootViewsRef.current);
|
|
246
|
+
setSearchQuery('');
|
|
247
|
+
if (textInputRef.current) {
|
|
248
|
+
textInputRef.current.blur();
|
|
249
|
+
hasTriggeredFocus.current = false;
|
|
250
|
+
hasAddedRootView.current = false;
|
|
251
|
+
hasShownRootView.current = false;
|
|
252
|
+
ref_isFocus.current = false;
|
|
253
|
+
}
|
|
130
254
|
};
|
|
131
255
|
}, []);
|
|
132
256
|
useEffect(() => {
|
|
@@ -135,13 +259,14 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
135
259
|
if (rootViews.length === 0) {
|
|
136
260
|
hasAddedRootView.current = false;
|
|
137
261
|
hasShownRootView.current = false;
|
|
262
|
+
ref_isFocus.current = false;
|
|
263
|
+
ref_isKeyboardFullyShown.current = false;
|
|
138
264
|
hasTriggeredFocus.current = false;
|
|
139
265
|
setState((prevState) => {
|
|
140
266
|
return Object.assign(Object.assign({}, prevState), { isFocus: false });
|
|
141
267
|
});
|
|
142
268
|
}
|
|
143
269
|
}, [rootViews]);
|
|
144
|
-
// Sync selectedItem changes like project implementation
|
|
145
270
|
useEffect(() => {
|
|
146
271
|
var _a, _b;
|
|
147
272
|
console.log('AutoPositionedPopup useEffect tag=', tag);
|
|
@@ -154,229 +279,6 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
154
279
|
});
|
|
155
280
|
}
|
|
156
281
|
}, [selectedItem, state.selectedItem, tag]);
|
|
157
|
-
// Legacy state for compatibility
|
|
158
|
-
const [isVisible, setIsVisible] = useState(false);
|
|
159
|
-
const [data, setData] = useState([]);
|
|
160
|
-
const [loading, setLoading] = useState(false);
|
|
161
|
-
const [popupPosition, setPopupPosition] = useState({ top: 0, left: 0, width: 0 });
|
|
162
|
-
const popupId = useRef(`popup-${tag}-${Date.now()}`);
|
|
163
|
-
// Refs for performance optimization
|
|
164
|
-
const containerRef = useRef(null);
|
|
165
|
-
const textInputRef = useRef(null);
|
|
166
|
-
const debounceTimerRef = useRef(null);
|
|
167
|
-
const searchQueryRef = useRef(''); // Use ref instead of state to avoid re-renders
|
|
168
|
-
// Constants
|
|
169
|
-
const LIST_HEIGHT = 200;
|
|
170
|
-
const theme = defaultTheme;
|
|
171
|
-
// Fetch data function
|
|
172
|
-
const loadData = useCallback(async (query = '') => {
|
|
173
|
-
if (!fetchData)
|
|
174
|
-
return;
|
|
175
|
-
setLoading(true);
|
|
176
|
-
try {
|
|
177
|
-
const result = await fetchData({
|
|
178
|
-
pageIndex: 0,
|
|
179
|
-
pageSize,
|
|
180
|
-
searchQuery: query,
|
|
181
|
-
});
|
|
182
|
-
if (result === null || result === void 0 ? void 0 : result.items) {
|
|
183
|
-
setData(result.items);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
console.error('Error loading data:', error);
|
|
188
|
-
}
|
|
189
|
-
finally {
|
|
190
|
-
setLoading(false);
|
|
191
|
-
}
|
|
192
|
-
}, [fetchData, pageSize]);
|
|
193
|
-
// Handle search query change with debounce and event emission
|
|
194
|
-
const handleSearchChange = useCallback((query) => {
|
|
195
|
-
// Store in ref to avoid re-renders
|
|
196
|
-
searchQueryRef.current = query;
|
|
197
|
-
// Update TextInput value directly if needed
|
|
198
|
-
if (textInputRef.current) {
|
|
199
|
-
// The TextInput's value will be controlled by its own state
|
|
200
|
-
}
|
|
201
|
-
// Clear previous debounce timer
|
|
202
|
-
if (debounceTimerRef.current) {
|
|
203
|
-
clearTimeout(debounceTimerRef.current);
|
|
204
|
-
}
|
|
205
|
-
// Use debounce for performance optimization
|
|
206
|
-
debounceTimerRef.current = setTimeout(() => {
|
|
207
|
-
// Emit query change event to decouple components and avoid context re-rendering
|
|
208
|
-
emitQueryChange(searchQueryRef.current);
|
|
209
|
-
}, 300); // Use 300ms debounce like the original
|
|
210
|
-
}, []);
|
|
211
|
-
// Calculate popup position
|
|
212
|
-
const calculatePosition = useCallback(() => {
|
|
213
|
-
if (!containerRef.current)
|
|
214
|
-
return;
|
|
215
|
-
containerRef.current.measureInWindow((x, y, width, height) => {
|
|
216
|
-
const screenHeight = Dimensions.get('screen').height;
|
|
217
|
-
const screenWidth = Dimensions.get('screen').width;
|
|
218
|
-
let top = y + height;
|
|
219
|
-
let left = x;
|
|
220
|
-
let popupWidth = width;
|
|
221
|
-
// Check if popup should appear above the input
|
|
222
|
-
if (y + height + LIST_HEIGHT > screenHeight) {
|
|
223
|
-
top = y - LIST_HEIGHT;
|
|
224
|
-
}
|
|
225
|
-
// Adjust horizontal position if needed
|
|
226
|
-
if ((popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.left) && (popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.width)) {
|
|
227
|
-
const leftPercent = parseFloat(String(popUpViewStyle.left).replace('%', '')) / 100;
|
|
228
|
-
const widthPercent = parseFloat(String(popUpViewStyle.width).replace('%', '')) / 100;
|
|
229
|
-
left = screenWidth * leftPercent;
|
|
230
|
-
popupWidth = screenWidth * widthPercent;
|
|
231
|
-
}
|
|
232
|
-
setPopupPosition({ top, left, width: popupWidth });
|
|
233
|
-
});
|
|
234
|
-
}, [popUpViewStyle]);
|
|
235
|
-
// Hide popup using RootView
|
|
236
|
-
const hidePopup = useCallback(() => {
|
|
237
|
-
var _a, _b;
|
|
238
|
-
setIsVisible(false);
|
|
239
|
-
// Reset search query
|
|
240
|
-
searchQueryRef.current = '';
|
|
241
|
-
if (textInputRef.current) {
|
|
242
|
-
textInputRef.current.blur();
|
|
243
|
-
(_b = (_a = textInputRef.current).clear) === null || _b === void 0 ? void 0 : _b.call(_a); // Clear the TextInput
|
|
244
|
-
}
|
|
245
|
-
removeRootView(popupId.current, forceRemoveAllRootViewOnItemSelected, rootViewsRef.current);
|
|
246
|
-
}, [removeRootView, forceRemoveAllRootViewOnItemSelected]);
|
|
247
|
-
// Handle data updates from PopupList
|
|
248
|
-
const handleDataUpdate = useCallback((newData) => {
|
|
249
|
-
setData(newData);
|
|
250
|
-
}, []);
|
|
251
|
-
// Handle item selection
|
|
252
|
-
const handleItemPress = useCallback((item) => {
|
|
253
|
-
onItemSelected === null || onItemSelected === void 0 ? void 0 : onItemSelected(item);
|
|
254
|
-
hidePopup();
|
|
255
|
-
}, [onItemSelected, hidePopup]);
|
|
256
|
-
// Show popup using RootView
|
|
257
|
-
const showPopup = useCallback(() => {
|
|
258
|
-
calculatePosition();
|
|
259
|
-
setIsVisible(true);
|
|
260
|
-
loadData(searchQueryRef.current);
|
|
261
|
-
// Wait for position to be calculated
|
|
262
|
-
setTimeout(() => {
|
|
263
|
-
const popupComponent = (<TouchableOpacity style={{
|
|
264
|
-
flex: 1,
|
|
265
|
-
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
|
266
|
-
}} activeOpacity={1} onPress={hidePopup}>
|
|
267
|
-
<View style={{
|
|
268
|
-
position: 'absolute',
|
|
269
|
-
top: popupPosition.top,
|
|
270
|
-
left: popupPosition.left,
|
|
271
|
-
width: popupPosition.width,
|
|
272
|
-
height: LIST_HEIGHT,
|
|
273
|
-
backgroundColor: theme.colors.background,
|
|
274
|
-
borderRadius: 8,
|
|
275
|
-
shadowColor: '#000',
|
|
276
|
-
shadowOffset: { width: 0, height: 2 },
|
|
277
|
-
shadowOpacity: 0.25,
|
|
278
|
-
shadowRadius: 3.84,
|
|
279
|
-
elevation: 5,
|
|
280
|
-
}}>
|
|
281
|
-
{useTextInput && (<RNTextInput ref={textInputRef} style={[
|
|
282
|
-
styles.inputStyle,
|
|
283
|
-
{
|
|
284
|
-
height: 40,
|
|
285
|
-
borderBottomWidth: 1,
|
|
286
|
-
borderBottomColor: theme.colors.border,
|
|
287
|
-
paddingHorizontal: 12,
|
|
288
|
-
color: theme.colors.text,
|
|
289
|
-
},
|
|
290
|
-
inputStyle,
|
|
291
|
-
]} placeholder={placeholder} placeholderTextColor={theme.colors.placeholderText} defaultValue={searchQueryRef.current} onChangeText={handleSearchChange} onSubmitEditing={(e) => {
|
|
292
|
-
onSubmitEditing === null || onSubmitEditing === void 0 ? void 0 : onSubmitEditing(e);
|
|
293
|
-
Keyboard.dismiss();
|
|
294
|
-
}} returnKeyType="done" {...TextInputProps}/>)}
|
|
295
|
-
|
|
296
|
-
<PopupList data={data} selectedItem={selectedItem} onItemPress={handleItemPress} renderItem={renderItem} keyExtractor={keyExtractor} theme={theme} rootViewsRef={rootViewsRef} fetchData={fetchData} localSearch={localSearch} pageSize={pageSize} onDataUpdate={handleDataUpdate} selectedItemBackgroundColor={selectedItemBackgroundColor}/>
|
|
297
|
-
</View>
|
|
298
|
-
</TouchableOpacity>);
|
|
299
|
-
addRootView({
|
|
300
|
-
id: popupId.current,
|
|
301
|
-
style: {
|
|
302
|
-
position: 'absolute',
|
|
303
|
-
top: 0,
|
|
304
|
-
left: 0,
|
|
305
|
-
right: 0,
|
|
306
|
-
bottom: 0,
|
|
307
|
-
},
|
|
308
|
-
component: popupComponent,
|
|
309
|
-
useModal: true,
|
|
310
|
-
onModalClose: hidePopup,
|
|
311
|
-
centerDisplay: centerDisplay,
|
|
312
|
-
});
|
|
313
|
-
}, 100);
|
|
314
|
-
}, [calculatePosition, loadData, popupPosition, useTextInput, placeholder, theme, inputStyle, TextInputProps, data, selectedItem, renderItem, keyExtractor, centerDisplay, addRootView, hidePopup, handleSearchChange, handleItemPress, LIST_HEIGHT, selectedItemBackgroundColor]);
|
|
315
|
-
// Handle button press - following project implementation logic
|
|
316
|
-
const handleButtonPress = useCallback(() => {
|
|
317
|
-
if (AutoPositionedPopupBtnDisabled)
|
|
318
|
-
return;
|
|
319
|
-
console.log('AutoPositionedPopup onPress tag=', tag);
|
|
320
|
-
console.log('AutoPositionedPopup onPress state.isFocus=', state.isFocus);
|
|
321
|
-
console.log('AutoPositionedPopup onPress useTextInput=', useTextInput);
|
|
322
|
-
console.log('AutoPositionedPopup onPress hasAddedRootView.current=', hasAddedRootView.current);
|
|
323
|
-
console.log('AutoPositionedPopup onPress hasShownRootView.current=', hasShownRootView.current);
|
|
324
|
-
console.log('AutoPositionedPopup onPress hasTriggeredFocus.current=', hasTriggeredFocus.current);
|
|
325
|
-
console.log('AutoPositionedPopup onPress state.selectedItem=', state.selectedItem);
|
|
326
|
-
setState((prevState) => {
|
|
327
|
-
return Object.assign(Object.assign({}, prevState), { isFocus: true });
|
|
328
|
-
});
|
|
329
|
-
if (!hasAddedRootView.current && useTextInput) {
|
|
330
|
-
// TextInput version: hide first, show after keyboard fully appears
|
|
331
|
-
hasAddedRootView.current = true;
|
|
332
|
-
hasShownRootView.current = false;
|
|
333
|
-
addRootView({
|
|
334
|
-
id: tag,
|
|
335
|
-
style: {
|
|
336
|
-
top: 0,
|
|
337
|
-
left: 0,
|
|
338
|
-
width: popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.width,
|
|
339
|
-
height: listLayout.height,
|
|
340
|
-
opacity: 0,
|
|
341
|
-
},
|
|
342
|
-
component: (<PopupList data={data} selectedItem={state.selectedItem} onItemPress={handleItemPress} renderItem={renderItem} keyExtractor={keyExtractor} theme={theme} rootViewsRef={rootViewsRef} fetchData={fetchData} localSearch={localSearch} pageSize={pageSize} onDataUpdate={handleDataUpdate} selectedItemBackgroundColor={selectedItemBackgroundColor}/>),
|
|
343
|
-
useModal: false,
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
}, [AutoPositionedPopupBtnDisabled, useTextInput, state.isFocus, state.selectedItem, tag, hasAddedRootView, hasShownRootView, hasTriggeredFocus, addRootView, popUpViewStyle, data, handleItemPress, renderItem, keyExtractor, theme, rootViewsRef, fetchData, localSearch, pageSize, handleDataUpdate, selectedItemBackgroundColor]);
|
|
347
|
-
// Imperative handle for parent component access
|
|
348
|
-
useImperativeHandle(parentRef, () => ({
|
|
349
|
-
clearSelectedItem: () => {
|
|
350
|
-
// Clear selection logic can be implemented here
|
|
351
|
-
console.log('Clearing selected item for:', tag);
|
|
352
|
-
},
|
|
353
|
-
showPopup,
|
|
354
|
-
hidePopup,
|
|
355
|
-
}), [tag, showPopup, hidePopup]);
|
|
356
|
-
// Component lifecycle management like project implementation
|
|
357
|
-
useEffect(() => {
|
|
358
|
-
console.log(`AutoPositionedPopup componentDidMount tag=`, tag);
|
|
359
|
-
//componentWillUnmount
|
|
360
|
-
return () => {
|
|
361
|
-
console.log(`AutoPositionedPopup componentWillUnmount tag=`, tag);
|
|
362
|
-
removeRootView(tag, forceRemoveAllRootViewOnItemSelected, rootViewsRef.current);
|
|
363
|
-
setContextSearchQuery('');
|
|
364
|
-
if (textInputRef.current) {
|
|
365
|
-
textInputRef.current.blur();
|
|
366
|
-
hasTriggeredFocus.current = false;
|
|
367
|
-
hasAddedRootView.current = false;
|
|
368
|
-
hasShownRootView.current = false;
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
}, [tag, removeRootView, forceRemoveAllRootViewOnItemSelected, setContextSearchQuery]);
|
|
372
|
-
// Cleanup debounce timer
|
|
373
|
-
useEffect(() => {
|
|
374
|
-
return () => {
|
|
375
|
-
if (debounceTimerRef.current) {
|
|
376
|
-
clearTimeout(debounceTimerRef.current);
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
}, []);
|
|
380
282
|
useEffect(() => {
|
|
381
283
|
var _a, _b;
|
|
382
284
|
console.log('AutoPositionedPopup useEffect tag=', tag);
|
|
@@ -391,21 +293,24 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
391
293
|
if (useTextInput) {
|
|
392
294
|
if (isKeyboardFullyShown && hasAddedRootView.current && !hasShownRootView.current && state.isFocus) {
|
|
393
295
|
(_a = refAutoPositionedPopup.current) === null || _a === void 0 ? void 0 : _a.measureInWindow((x, y, width, height) => {
|
|
296
|
+
var _a;
|
|
394
297
|
console.log('AutoPositionedPopup measureInWindow x=', x, ' y=', y, ' width=', width, ' height=', height);
|
|
395
|
-
//
|
|
298
|
+
// SIMPLE CENTER-BASED POSITIONING STRATEGY
|
|
396
299
|
const screenHeight = Dimensions.get('screen').height;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
300
|
+
const screenCenter = screenHeight / 2;
|
|
301
|
+
console.log('AutoPositionedPopup screenHeight=', screenHeight, ' screenCenter=', screenCenter, ' componentY=', y);
|
|
302
|
+
// Simple rule: if component Y > screen center, show popup above; otherwise show below
|
|
303
|
+
if (y > screenCenter) {
|
|
304
|
+
console.log('AutoPositionedPopup with keyboard: showing above (Y > center)');
|
|
305
|
+
ref_listPos.current = { x: x, y: y - listLayout.height, width: width };
|
|
401
306
|
}
|
|
402
307
|
else {
|
|
403
|
-
console.log('AutoPositionedPopup
|
|
404
|
-
ref_listPos.current = { x: x, y: y
|
|
308
|
+
console.log('AutoPositionedPopup with keyboard: showing below (Y <= center)');
|
|
309
|
+
ref_listPos.current = { x: x, y: y + height, width: width };
|
|
405
310
|
}
|
|
406
311
|
console.log('AutoPositionedPopup ref_listPos.current=', ref_listPos.current);
|
|
407
312
|
setRootViewNativeStyle(tag, {
|
|
408
|
-
top: ref_listPos.current.y,
|
|
313
|
+
top: (_a = ref_listPos.current) === null || _a === void 0 ? void 0 : _a.y,
|
|
409
314
|
left: popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.left,
|
|
410
315
|
width: popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.width,
|
|
411
316
|
height: listLayout.height,
|
|
@@ -420,7 +325,7 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
420
325
|
setState((prevState) => {
|
|
421
326
|
return Object.assign(Object.assign({}, prevState), { isFocus: false });
|
|
422
327
|
});
|
|
423
|
-
|
|
328
|
+
setSearchQuery('');
|
|
424
329
|
hasAddedRootView.current = false;
|
|
425
330
|
hasShownRootView.current = false;
|
|
426
331
|
}
|
|
@@ -429,43 +334,81 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
429
334
|
if (state.isFocus) {
|
|
430
335
|
(_b = refAutoPositionedPopup.current) === null || _b === void 0 ? void 0 : _b.measureInWindow((x, y, width, height) => {
|
|
431
336
|
console.log('AutoPositionedPopup measureInWindow x=', x, ' y=', y, ' width=', width, ' height=', height);
|
|
432
|
-
//
|
|
337
|
+
// SIMPLE CENTER-BASED POSITIONING STRATEGY
|
|
433
338
|
const screenHeight = Dimensions.get('screen').height;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
339
|
+
const screenCenter = screenHeight / 2;
|
|
340
|
+
console.log('AutoPositionedPopup screenHeight=', screenHeight, ' screenCenter=', screenCenter, ' componentY=', y);
|
|
341
|
+
// Simple rule: if component Y > screen center, show popup above; otherwise show below
|
|
342
|
+
if (y + insets.top > screenCenter) {
|
|
343
|
+
console.log('AutoPositionedPopup: showing above (Y > center)');
|
|
344
|
+
ref_listPos.current = { x: x, y: y - listLayout.height, width: width };
|
|
438
345
|
}
|
|
439
346
|
else {
|
|
440
|
-
console.log('AutoPositionedPopup
|
|
441
|
-
ref_listPos.current = { x: x, y: y
|
|
347
|
+
console.log('AutoPositionedPopup: showing below (Y <= center)');
|
|
348
|
+
ref_listPos.current = { x: x, y: y + height + insets.top, width: width };
|
|
442
349
|
}
|
|
443
350
|
console.log('AutoPositionedPopup ref_listPos.current=', ref_listPos.current);
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
351
|
+
if (CustomPopView && CustomPopViewStyle) {
|
|
352
|
+
console.log('AutoPositionedPopup CustomPopViewStyle=', CustomPopViewStyle);
|
|
353
|
+
// Ensure CustomPopViewStyle.height is a number before using it in calculations
|
|
354
|
+
const customHeight = typeof CustomPopViewStyle.height === 'number' ? CustomPopViewStyle.height : listLayout.height;
|
|
355
|
+
// Apply same simple center-based strategy for CustomPopView
|
|
356
|
+
console.log('AutoPositionedPopup CustomPopView using center-based positioning, customHeight=', customHeight);
|
|
357
|
+
// Simple rule: if component Y > screen center, show popup above; otherwise show below
|
|
358
|
+
if (y > screenCenter) {
|
|
359
|
+
console.log('AutoPositionedPopup CustomPopView: showing above (Y > center), tag=', tag);
|
|
360
|
+
ref_listPos.current = { x: x, y: y - customHeight, width: width };
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
console.log('AutoPositionedPopup CustomPopView: showing below (Y <= center), tag=', tag);
|
|
364
|
+
ref_listPos.current = { x: x, y: y + height, width: width };
|
|
365
|
+
}
|
|
366
|
+
const PopViewComponent = CustomPopView();
|
|
367
|
+
console.log('AutoPositionedPopup addRootView PopViewComponent=', PopViewComponent);
|
|
368
|
+
console.log('AutoPositionedPopup addRootView state.selectedItem=', state.selectedItem);
|
|
369
|
+
addRootView({
|
|
370
|
+
id: tag,
|
|
371
|
+
style: !centerDisplay
|
|
372
|
+
? Object.assign({ top: ref_listPos.current.y, left: popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.left, width: popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.width, height: listLayout.height, opacity: 1 }, CustomPopViewStyle) : Object.assign({ width: popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.width, height: listLayout.height }, CustomPopViewStyle),
|
|
373
|
+
component: <PopViewComponent selectedItem={state.selectedItem}></PopViewComponent>,
|
|
374
|
+
useModal: true,
|
|
375
|
+
onModalClose: () => {
|
|
376
|
+
console.log('AutoPositionedPopup onModalClose');
|
|
377
|
+
removeRootView(tag, forceRemoveAllRootViewOnItemSelected, rootViewsRef.current);
|
|
378
|
+
setState((prevState) => {
|
|
379
|
+
return Object.assign(Object.assign({}, prevState), { isFocus: false });
|
|
380
|
+
});
|
|
381
|
+
hasAddedRootView.current = false;
|
|
382
|
+
hasShownRootView.current = false;
|
|
383
|
+
hasTriggeredFocus.current = false;
|
|
384
|
+
setSearchQuery('');
|
|
385
|
+
},
|
|
386
|
+
centerDisplay,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
console.log('AutoPositionedPopup addRootView tag=', tag);
|
|
391
|
+
addRootView({
|
|
392
|
+
id: tag,
|
|
393
|
+
style: {
|
|
394
|
+
top: ref_listPos.current.y,
|
|
395
|
+
left: popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.left,
|
|
396
|
+
width: popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.width,
|
|
397
|
+
height: listLayout.height,
|
|
398
|
+
opacity: 1,
|
|
399
|
+
},
|
|
400
|
+
component: (<AutoPositionedPopupList tag={tag} updateState={updateState} fetchData={fetchData} pageSize={pageSize} renderItem={renderItem} selectedItem={state.selectedItem} localSearch={localSearch}/>),
|
|
401
|
+
useModal: true,
|
|
402
|
+
onModalClose: () => {
|
|
403
|
+
console.log('AutoPositionedPopup onModalClose tag=', tag);
|
|
404
|
+
removeRootView(tag, forceRemoveAllRootViewOnItemSelected, rootViewsRef.current);
|
|
405
|
+
setState((prevState) => {
|
|
406
|
+
return Object.assign({}, prevState);
|
|
407
|
+
});
|
|
408
|
+
setSearchQuery('');
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
}
|
|
469
412
|
});
|
|
470
413
|
}
|
|
471
414
|
}
|
|
@@ -473,46 +416,82 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
473
416
|
ref_isFocus.current = state.isFocus;
|
|
474
417
|
if (isKeyboardFullyShown !== keyboardVisibleRef.current) {
|
|
475
418
|
keyboardVisibleRef.current = isKeyboardFullyShown;
|
|
476
|
-
// Ensure TextInput has correct focus when keyboard is fully shown
|
|
477
419
|
if (isKeyboardFullyShown && textInputRef.current) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
textInputRef.current.setNativeProps({ text: searchQueryRef.current });
|
|
420
|
+
if (ref_searchQuery.current) {
|
|
421
|
+
textInputRef.current.setNativeProps({ text: ref_searchQuery.current });
|
|
481
422
|
}
|
|
482
423
|
}
|
|
483
424
|
}
|
|
484
425
|
}
|
|
485
|
-
}, [
|
|
426
|
+
}, [insets,
|
|
486
427
|
isKeyboardFullyShown,
|
|
487
428
|
state.isFocus,
|
|
488
429
|
useTextInput,
|
|
430
|
+
CustomPopView,
|
|
431
|
+
CustomPopViewStyle,
|
|
489
432
|
forceRemoveAllRootViewOnItemSelected,
|
|
490
433
|
tag,
|
|
491
434
|
state.selectedItem,
|
|
492
|
-
popUpViewStyle,
|
|
493
|
-
data,
|
|
494
|
-
handleItemPress,
|
|
495
|
-
renderItem,
|
|
496
|
-
keyExtractor,
|
|
497
|
-
theme,
|
|
498
|
-
rootViewsRef,
|
|
499
|
-
fetchData,
|
|
500
|
-
localSearch,
|
|
501
|
-
pageSize,
|
|
502
|
-
handleDataUpdate,
|
|
503
|
-
selectedItemBackgroundColor,
|
|
504
|
-
removeRootView,
|
|
505
|
-
setContextSearchQuery,
|
|
506
|
-
addRootView,
|
|
507
|
-
centerDisplay,
|
|
508
|
-
setRootViewNativeStyle,
|
|
509
435
|
]);
|
|
436
|
+
// Imperative handle for parent component access
|
|
437
|
+
useImperativeHandle(parentRef, () => ({
|
|
438
|
+
clearSelectedItem: () => {
|
|
439
|
+
console.log('AutoPositionedPopup clearSelectedItem tag=', tag);
|
|
440
|
+
setState((prevState) => {
|
|
441
|
+
return Object.assign(Object.assign({}, prevState), { selectedItem: undefined });
|
|
442
|
+
});
|
|
443
|
+
},
|
|
444
|
+
}), []);
|
|
445
|
+
const updateState = (key, value) => {
|
|
446
|
+
console.log('AutoPositionedPopup updateState key=', key, ' value=', value);
|
|
447
|
+
setState((prevState) => (Object.assign(Object.assign({}, prevState), { [key]: value })));
|
|
448
|
+
if (key === 'selectedItem' && onItemSelected) {
|
|
449
|
+
onItemSelected(value);
|
|
450
|
+
console.log('AutoPositionedPopup updateState onItemSelected rootViewsRef.current=', rootViewsRef.current);
|
|
451
|
+
removeRootView(tag, forceRemoveAllRootViewOnItemSelected, rootViewsRef.current);
|
|
452
|
+
hasAddedRootView.current = false;
|
|
453
|
+
hasShownRootView.current = false;
|
|
454
|
+
hasTriggeredFocus.current = false;
|
|
455
|
+
setState((prevState) => {
|
|
456
|
+
return Object.assign(Object.assign({}, prevState), { isFocus: false });
|
|
457
|
+
});
|
|
458
|
+
setSearchQuery('');
|
|
459
|
+
}
|
|
460
|
+
};
|
|
510
461
|
// Render the component following project implementation
|
|
511
462
|
return useMemo(() => {
|
|
512
463
|
var _a;
|
|
464
|
+
console.log('AutoPositionedPopup render tag=', tag); // Now safe - circular dependency fixed
|
|
513
465
|
return (<CustomRow>
|
|
514
466
|
<View style={[styles.contain, style]} ref={refAutoPositionedPopup}>
|
|
515
|
-
{!state.isFocus || !useTextInput ? (<TouchableOpacity style={[styles.AutoPositionedPopupBtn, AutoPositionedPopupBtnStyle]} disabled={AutoPositionedPopupBtnDisabled} onPress={
|
|
467
|
+
{!state.isFocus || !useTextInput ? (<TouchableOpacity style={[styles.AutoPositionedPopupBtn, AutoPositionedPopupBtnStyle]} disabled={AutoPositionedPopupBtnDisabled} onPress={() => {
|
|
468
|
+
console.log('AutoPositionedPopup onPress tag=', tag);
|
|
469
|
+
console.log('AutoPositionedPopup onPress state.isFocus=', state.isFocus);
|
|
470
|
+
console.log('AutoPositionedPopup onPress useTextInput=', useTextInput);
|
|
471
|
+
console.log('AutoPositionedPopup onPress hasAddedRootView.current=', hasAddedRootView.current);
|
|
472
|
+
console.log('AutoPositionedPopup onPress hasShownRootView.current=', hasShownRootView.current);
|
|
473
|
+
console.log('AutoPositionedPopup onPress hasTriggeredFocus.current=', hasTriggeredFocus.current);
|
|
474
|
+
console.log('AutoPositionedPopup onPress state.selectedItem=', state.selectedItem);
|
|
475
|
+
setState((prevState) => {
|
|
476
|
+
return Object.assign(Object.assign({}, prevState), { isFocus: true });
|
|
477
|
+
});
|
|
478
|
+
if (!hasAddedRootView.current && useTextInput) {
|
|
479
|
+
hasAddedRootView.current = true;
|
|
480
|
+
hasShownRootView.current = false;
|
|
481
|
+
addRootView({
|
|
482
|
+
id: tag,
|
|
483
|
+
style: {
|
|
484
|
+
top: 0,
|
|
485
|
+
left: 0,
|
|
486
|
+
width: popUpViewStyle === null || popUpViewStyle === void 0 ? void 0 : popUpViewStyle.width,
|
|
487
|
+
height: listLayout.height,
|
|
488
|
+
opacity: 0,
|
|
489
|
+
},
|
|
490
|
+
component: (<AutoPositionedPopupList tag={tag} updateState={updateState} fetchData={fetchData} pageSize={pageSize} renderItem={renderItem} selectedItem={state.selectedItem} localSearch={localSearch}/>),
|
|
491
|
+
useModal: false,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}}>
|
|
516
495
|
{!btwChildren ? (<Text style={[
|
|
517
496
|
styles.searchQueryTxt,
|
|
518
497
|
state.selectedItem && { color: theme.colors.text },
|
|
@@ -523,18 +502,84 @@ const AutoPositionedPopup = memo(forwardRef((props, parentRef) => {
|
|
|
523
502
|
</TouchableOpacity>) : (useTextInput &&
|
|
524
503
|
state.isFocus && (<RNTextInput ref={textInputRef} key="fixed-textinput-key" style={[
|
|
525
504
|
styles.inputStyle,
|
|
526
|
-
{
|
|
527
|
-
textAlignVertical: 'center',
|
|
528
|
-
paddingVertical: 0,
|
|
529
|
-
paddingHorizontal: 0,
|
|
530
|
-
},
|
|
531
505
|
inputStyle,
|
|
532
|
-
]} textAlign={TextInputProps['textAlign'] || 'left'} multiline={TextInputProps['multiline'] || false} numberOfLines={TextInputProps['numberOfLines'] || 1}
|
|
533
|
-
|
|
506
|
+
]} textAlign={TextInputProps['textAlign'] || 'left'} multiline={TextInputProps['multiline'] || false} numberOfLines={TextInputProps['numberOfLines'] || 1} onChangeText={(searchQuery) => {
|
|
507
|
+
ref_searchQuery.current = searchQuery;
|
|
508
|
+
console.log('AutoPositionedPopup onChangeText rootViews=', rootViews);
|
|
509
|
+
if (!localSearch) {
|
|
510
|
+
if (debounceTimerRef.current) {
|
|
511
|
+
clearTimeout(debounceTimerRef.current);
|
|
512
|
+
}
|
|
513
|
+
debounceTimerRef.current = setTimeout(() => {
|
|
514
|
+
emitQueryChange(ref_searchQuery.current);
|
|
515
|
+
}, 500);
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
emitQueryChange(ref_searchQuery.current);
|
|
519
|
+
}
|
|
520
|
+
}} placeholderTextColor={theme.colors.placeholderText} placeholder={placeholder} onKeyPress={(e) => {
|
|
521
|
+
if (e.nativeEvent.key === 'Enter') {
|
|
522
|
+
Keyboard.dismiss();
|
|
523
|
+
}
|
|
524
|
+
}} 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={() => {
|
|
525
|
+
console.log('AutoPositionedPopup onFocus tag=', tag, ' selectedItem=', state.selectedItem, ' hasTriggeredFocus.current=', hasTriggeredFocus.current, ' textInputRef.current=', textInputRef.current, ' ref_searchQuery.current=', ref_searchQuery.current);
|
|
526
|
+
if (!hasTriggeredFocus.current) {
|
|
527
|
+
hasTriggeredFocus.current = true;
|
|
528
|
+
ref_isFocus.current = true;
|
|
529
|
+
if (state.selectedItem) {
|
|
530
|
+
ref_searchQuery.current = state.selectedItem.title;
|
|
531
|
+
}
|
|
532
|
+
if (textInputRef.current && ref_searchQuery.current) {
|
|
533
|
+
textInputRef.current.setNativeProps({
|
|
534
|
+
text: ref_searchQuery.current,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}} onBlur={() => {
|
|
539
|
+
console.log('AutoPositionedPopup onBlur tag=', tag, 'textInputRef.current=', textInputRef.current);
|
|
540
|
+
hasTriggeredFocus.current = false;
|
|
541
|
+
hasAddedRootView.current = false; // 重置 RootView 狀態
|
|
542
|
+
hasShownRootView.current = false;
|
|
543
|
+
ref_isFocus.current = false;
|
|
544
|
+
setState((prevState) => {
|
|
545
|
+
return Object.assign(Object.assign({}, prevState), { isFocus: false });
|
|
546
|
+
});
|
|
547
|
+
removeRootView(tag, forceRemoveAllRootViewOnItemSelected);
|
|
548
|
+
setSearchQuery('');
|
|
549
|
+
if (textInputRef.current) {
|
|
550
|
+
textInputRef.current.setNativeProps({ text: '' });
|
|
551
|
+
ref_searchQuery.current = '';
|
|
552
|
+
textInputRef.current.blur();
|
|
553
|
+
}
|
|
534
554
|
Keyboard.dismiss();
|
|
535
|
-
}}
|
|
555
|
+
}} selectTextOnFocus={TextInputProps['selectTextOnFocus'] || false} onSubmitEditing={(e) => {
|
|
556
|
+
console.log('AutoPositionedPopup.tsx onSubmitEditing e.nativeEvent.text=', e.nativeEvent.text);
|
|
557
|
+
onSubmitEditing && onSubmitEditing(e);
|
|
558
|
+
}}/>))}
|
|
536
559
|
</View>
|
|
537
560
|
</CustomRow>);
|
|
538
|
-
}, [
|
|
561
|
+
}, [tag,
|
|
562
|
+
fetchData,
|
|
563
|
+
renderItem,
|
|
564
|
+
onItemSelected,
|
|
565
|
+
onSubmitEditing,
|
|
566
|
+
localSearch,
|
|
567
|
+
placeholder,
|
|
568
|
+
textAlign,
|
|
569
|
+
pageSize,
|
|
570
|
+
selectedItem,
|
|
571
|
+
CustomRow,
|
|
572
|
+
useTextInput,
|
|
573
|
+
btwChildren,
|
|
574
|
+
selectedItem,
|
|
575
|
+
keyExtractor,
|
|
576
|
+
AutoPositionedPopupBtnStyle,
|
|
577
|
+
CustomPopView,
|
|
578
|
+
CustomPopViewStyle,
|
|
579
|
+
forceRemoveAllRootViewOnItemSelected,
|
|
580
|
+
inputStyle,
|
|
581
|
+
TextInputProps,
|
|
582
|
+
state.isFocus,]);
|
|
539
583
|
}));
|
|
540
584
|
export default AutoPositionedPopup;
|
|
585
|
+
//# sourceMappingURL=AutoPositionedPopup.js.map
|