react-native-auto-positioned-popup 1.0.2

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.
@@ -0,0 +1,186 @@
1
+ import React, { ReactNode, createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { Pressable, View, ViewStyle } from 'react-native';
3
+
4
+ interface DynamicViewBase {
5
+ id: string;
6
+ style: ViewStyle;
7
+ component: ReactNode;
8
+ useModal?: boolean;
9
+ onModalClose?: () => void;
10
+ centerDisplay?: boolean;
11
+ }
12
+
13
+ interface RootViewContextType {
14
+ addRootView: (view: DynamicViewBase) => void;
15
+ setRootViewNativeStyle: (id: string, style: ViewStyle) => void;
16
+ updateRootView: (id: string, update: Partial<DynamicViewBase>) => void;
17
+ removeRootView: (id?: string, force?: boolean, _rootViews?: DynamicViewBase[]) => void;
18
+ rootViews: DynamicViewBase[];
19
+ searchQuery: string;
20
+ setSearchQuery: (searchQuery: string) => void;
21
+ }
22
+
23
+ interface RootViewProviderProps {
24
+ children: ReactNode;
25
+ }
26
+
27
+ const RootViewContext = createContext<RootViewContextType | undefined>(undefined);
28
+ /**
29
+ * Dynamically add or remove views on the root view.
30
+ * @param children
31
+ * @constructor
32
+ */
33
+ export const RootViewProvider: React.FC<RootViewProviderProps> = ({ children }) => {
34
+ const [rootViews, setRootViews] = useState<DynamicViewBase[]>([]);
35
+ const [searchQuery, setSearchQuery] = useState<string>('');
36
+ const viewRefs = useRef<Record<string, View>>({});
37
+ //監聽 rootViews
38
+ useEffect(() => {
39
+ console.log('RootViewProvider rootViews changed:', rootViews);
40
+ }, [rootViews]);
41
+ const addRootView = (view: DynamicViewBase): void => {
42
+ // const id = `dynamic-view-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
43
+ const newView: DynamicViewBase = { ...view };
44
+ console.log('RootViewProvider addRootView rootViews=', rootViews);
45
+ console.log('RootViewProvider addRootView newView=', newView);
46
+ setRootViews((prev) => [...prev, newView]);
47
+ };
48
+
49
+ const updateRootView = (id: string, update: Partial<DynamicViewBase>): void => {
50
+ setRootViews((prev) => prev.map((view) => (view.id === id ? { ...view, ...update } : view)));
51
+ };
52
+
53
+ const removeRootView = (id?: string, force?: boolean, _rootViews?: DynamicViewBase[]): void => {
54
+ console.log('RootViewProvider removeRootView id=', id);
55
+ console.log('RootViewProvider removeRootView force=', force);
56
+ console.log('RootViewProvider removeRootView rootViews=', rootViews);
57
+ console.log('RootViewProvider removeRootView _rootViews=', _rootViews);
58
+
59
+ // Ensure keyboard is dismissed when force removing all root views
60
+ if (force) {
61
+ // Dismiss keyboard first
62
+ if ((global as any).dismissKeyboard) {
63
+ (global as any).dismissKeyboard();
64
+ }
65
+
66
+ // Small delay to ensure keyboard is dismissed before removing views
67
+ setTimeout(() => {
68
+ setRootViews((prev) => []);
69
+ console.log('RootViewProvider removeRootView setRootViews(prev => []) force=true');
70
+ }, 50);
71
+ return;
72
+ }
73
+ if (rootViews.length > 0 && id) {
74
+ setRootViews((prev) => prev.filter((view) => view.id !== id));
75
+ // else {
76
+ // console.log('RootViewProvider removeRootView setRootViews(prev => [])')
77
+ // setRootViews(prev => [])
78
+ // }
79
+ } else if (_rootViews && _rootViews.length > 0 && id) {
80
+ setRootViews((prev) => prev.filter((view) => view.id !== id));
81
+ }
82
+ };
83
+
84
+ const setRootViewNativeStyle = (id: string, style: ViewStyle): void => {
85
+ const target = viewRefs.current[id];
86
+ if (target) {
87
+ // @ts-ignore - React Native setNativeProps
88
+ target.setNativeProps({ style });
89
+ }
90
+ };
91
+
92
+ const contextValue = useMemo(
93
+ () => ({
94
+ addRootView,
95
+ setRootViewNativeStyle,
96
+ updateRootView,
97
+ removeRootView,
98
+ rootViews,
99
+ searchQuery,
100
+ setSearchQuery,
101
+ }),
102
+ [addRootView, setRootViewNativeStyle, updateRootView, removeRootView, rootViews, searchQuery, setSearchQuery]
103
+ );
104
+
105
+ return (
106
+ <RootViewContext.Provider value={contextValue}>
107
+ <>
108
+ {children}
109
+ {rootViews.map(
110
+ ({ id, style, component, useModal, onModalClose, centerDisplay }: DynamicViewBase): React.JSX.Element => {
111
+ console.log('RootViewProvider rootViews.map id=', id);
112
+ console.log('RootViewProvider rootViews.map style=', style);
113
+ console.log('RootViewProvider rootViews.map component=', component);
114
+ console.log('RootViewProvider rootViews.map useModal=', useModal);
115
+ console.log('RootViewProvider rootViews.map centerDisplay=', centerDisplay);
116
+ return !useModal ? (
117
+ <View
118
+ key={id}
119
+ ref={(r) => {
120
+ if (r) viewRefs.current[id] = r;
121
+ }}
122
+ style={[style, { position: 'absolute' }]}
123
+ >
124
+ {component}
125
+ </View>
126
+ ) : (
127
+ <Pressable
128
+ key={id}
129
+ style={[
130
+ {
131
+ flex: 1,
132
+ position: 'absolute',
133
+ width: '100%',
134
+ left: 0,
135
+ right: 0,
136
+ top: 0,
137
+ bottom: 0,
138
+ zIndex: 99999999999,
139
+ backgroundColor: 'rgba(0,0,0,0.5)',
140
+ },
141
+ centerDisplay && { justifyContent: 'center', alignItems: 'center' },
142
+ ]}
143
+ onPress={() => {
144
+ console.log('RootViewProvider Pressable onPress rootViews=', rootViews);
145
+ removeRootView(id, true);
146
+ onModalClose && onModalClose();
147
+ }}
148
+ >
149
+ <View
150
+ ref={(r) => {
151
+ if (r) viewRefs.current[id] = r;
152
+ }}
153
+ style={[{ position: 'absolute' }, style]}
154
+ >
155
+ {component}
156
+ </View>
157
+ </Pressable>
158
+ );
159
+ // (<Modal
160
+ // animationType="none"
161
+ // transparent={false}
162
+ // visible={true}
163
+ // presentationStyle="overFullScreen" // iOS特定属性
164
+ // onRequestClose={() => {
165
+ // // Android 返回鍵按下時的回調
166
+ // onModalClose && onModalClose()
167
+ // }}
168
+ // key={id}
169
+ // >
170
+ // </Modal>)
171
+ }
172
+ )}
173
+ </>
174
+ </RootViewContext.Provider>
175
+ );
176
+ };
177
+ /*
178
+ const { addRootView, updateRootView, removeRootView ,searchQuery } = useRootView();
179
+ */
180
+ export const useRootView = (): RootViewContextType => {
181
+ const context = useContext(RootViewContext);
182
+ if (!context) {
183
+ throw new Error('useRootView must be used within a RootViewProvider');
184
+ }
185
+ return context;
186
+ };
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ // Main component export
2
+ export { default as AutoPositionedPopup } from './AutoPositionedPopup';
3
+ export { default } from './AutoPositionedPopup';
4
+
5
+ // Type exports
6
+ export type {
7
+ AutoPositionedPopupProps,
8
+ SelectedItem,
9
+ Data,
10
+ } from './AutoPositionedPopupProps';
11
+
12
+ // Style exports (optional, for customization)
13
+ export { default as AutoPositionedPopupStyles } from './AutoPositionedPopup.style';
14
+
15
+ // RootView exports
16
+ export { RootViewProvider, useRootView } from './RootViewContext';