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.
- package/LICENSE +21 -0
- package/README.md +425 -0
- package/README_zh.md +425 -0
- package/lib/AutoPositionedPopup.d.ts +5 -0
- package/lib/AutoPositionedPopup.d.ts.map +1 -0
- package/lib/AutoPositionedPopup.js +306 -0
- package/lib/AutoPositionedPopup.style.d.ts +80 -0
- package/lib/AutoPositionedPopup.style.d.ts.map +1 -0
- package/lib/AutoPositionedPopup.style.js +79 -0
- package/lib/AutoPositionedPopupProps.d.ts +58 -0
- package/lib/AutoPositionedPopupProps.d.ts.map +1 -0
- package/lib/AutoPositionedPopupProps.js +1 -0
- package/lib/RootViewContext.d.ts +31 -0
- package/lib/RootViewContext.d.ts.map +1 -0
- package/lib/RootViewContext.js +136 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +7 -0
- package/package.json +82 -0
- package/src/AutoPositionedPopup.style.ts +80 -0
- package/src/AutoPositionedPopup.tsx +529 -0
- package/src/AutoPositionedPopupProps.ts +61 -0
- package/src/RootViewContext.tsx +186 -0
- package/src/index.ts +16 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { Pressable, View } from 'react-native';
|
|
3
|
+
const RootViewContext = createContext(undefined);
|
|
4
|
+
/**
|
|
5
|
+
* Dynamically add or remove views on the root view.
|
|
6
|
+
* @param children
|
|
7
|
+
* @constructor
|
|
8
|
+
*/
|
|
9
|
+
export const RootViewProvider = ({ children }) => {
|
|
10
|
+
const [rootViews, setRootViews] = useState([]);
|
|
11
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
12
|
+
const viewRefs = useRef({});
|
|
13
|
+
//監聽 rootViews
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
console.log('RootViewProvider rootViews changed:', rootViews);
|
|
16
|
+
}, [rootViews]);
|
|
17
|
+
const addRootView = (view) => {
|
|
18
|
+
// const id = `dynamic-view-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
19
|
+
const newView = Object.assign({}, view);
|
|
20
|
+
console.log('RootViewProvider addRootView rootViews=', rootViews);
|
|
21
|
+
console.log('RootViewProvider addRootView newView=', newView);
|
|
22
|
+
setRootViews((prev) => [...prev, newView]);
|
|
23
|
+
};
|
|
24
|
+
const updateRootView = (id, update) => {
|
|
25
|
+
setRootViews((prev) => prev.map((view) => (view.id === id ? Object.assign(Object.assign({}, view), update) : view)));
|
|
26
|
+
};
|
|
27
|
+
const removeRootView = (id, force, _rootViews) => {
|
|
28
|
+
console.log('RootViewProvider removeRootView id=', id);
|
|
29
|
+
console.log('RootViewProvider removeRootView force=', force);
|
|
30
|
+
console.log('RootViewProvider removeRootView rootViews=', rootViews);
|
|
31
|
+
console.log('RootViewProvider removeRootView _rootViews=', _rootViews);
|
|
32
|
+
// Ensure keyboard is dismissed when force removing all root views
|
|
33
|
+
if (force) {
|
|
34
|
+
// Dismiss keyboard first
|
|
35
|
+
if (global.dismissKeyboard) {
|
|
36
|
+
global.dismissKeyboard();
|
|
37
|
+
}
|
|
38
|
+
// Small delay to ensure keyboard is dismissed before removing views
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
setRootViews((prev) => []);
|
|
41
|
+
console.log('RootViewProvider removeRootView setRootViews(prev => []) force=true');
|
|
42
|
+
}, 50);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (rootViews.length > 0 && id) {
|
|
46
|
+
setRootViews((prev) => prev.filter((view) => view.id !== id));
|
|
47
|
+
// else {
|
|
48
|
+
// console.log('RootViewProvider removeRootView setRootViews(prev => [])')
|
|
49
|
+
// setRootViews(prev => [])
|
|
50
|
+
// }
|
|
51
|
+
}
|
|
52
|
+
else if (_rootViews && _rootViews.length > 0 && id) {
|
|
53
|
+
setRootViews((prev) => prev.filter((view) => view.id !== id));
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const setRootViewNativeStyle = (id, style) => {
|
|
57
|
+
const target = viewRefs.current[id];
|
|
58
|
+
if (target) {
|
|
59
|
+
// @ts-ignore - React Native setNativeProps
|
|
60
|
+
target.setNativeProps({ style });
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const contextValue = useMemo(() => ({
|
|
64
|
+
addRootView,
|
|
65
|
+
setRootViewNativeStyle,
|
|
66
|
+
updateRootView,
|
|
67
|
+
removeRootView,
|
|
68
|
+
rootViews,
|
|
69
|
+
searchQuery,
|
|
70
|
+
setSearchQuery,
|
|
71
|
+
}), [addRootView, setRootViewNativeStyle, updateRootView, removeRootView, rootViews, searchQuery, setSearchQuery]);
|
|
72
|
+
return (<RootViewContext.Provider value={contextValue}>
|
|
73
|
+
<>
|
|
74
|
+
{children}
|
|
75
|
+
{rootViews.map(({ id, style, component, useModal, onModalClose, centerDisplay }) => {
|
|
76
|
+
console.log('RootViewProvider rootViews.map id=', id);
|
|
77
|
+
console.log('RootViewProvider rootViews.map style=', style);
|
|
78
|
+
console.log('RootViewProvider rootViews.map component=', component);
|
|
79
|
+
console.log('RootViewProvider rootViews.map useModal=', useModal);
|
|
80
|
+
console.log('RootViewProvider rootViews.map centerDisplay=', centerDisplay);
|
|
81
|
+
return !useModal ? (<View key={id} ref={(r) => {
|
|
82
|
+
if (r)
|
|
83
|
+
viewRefs.current[id] = r;
|
|
84
|
+
}} style={[style, { position: 'absolute' }]}>
|
|
85
|
+
{component}
|
|
86
|
+
</View>) : (<Pressable key={id} style={[
|
|
87
|
+
{
|
|
88
|
+
flex: 1,
|
|
89
|
+
position: 'absolute',
|
|
90
|
+
width: '100%',
|
|
91
|
+
left: 0,
|
|
92
|
+
right: 0,
|
|
93
|
+
top: 0,
|
|
94
|
+
bottom: 0,
|
|
95
|
+
zIndex: 99999999999,
|
|
96
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
97
|
+
},
|
|
98
|
+
centerDisplay && { justifyContent: 'center', alignItems: 'center' },
|
|
99
|
+
]} onPress={() => {
|
|
100
|
+
console.log('RootViewProvider Pressable onPress rootViews=', rootViews);
|
|
101
|
+
removeRootView(id, true);
|
|
102
|
+
onModalClose && onModalClose();
|
|
103
|
+
}}>
|
|
104
|
+
<View ref={(r) => {
|
|
105
|
+
if (r)
|
|
106
|
+
viewRefs.current[id] = r;
|
|
107
|
+
}} style={[{ position: 'absolute' }, style]}>
|
|
108
|
+
{component}
|
|
109
|
+
</View>
|
|
110
|
+
</Pressable>);
|
|
111
|
+
// (<Modal
|
|
112
|
+
// animationType="none"
|
|
113
|
+
// transparent={false}
|
|
114
|
+
// visible={true}
|
|
115
|
+
// presentationStyle="overFullScreen" // iOS特定属性
|
|
116
|
+
// onRequestClose={() => {
|
|
117
|
+
// // Android 返回鍵按下時的回調
|
|
118
|
+
// onModalClose && onModalClose()
|
|
119
|
+
// }}
|
|
120
|
+
// key={id}
|
|
121
|
+
// >
|
|
122
|
+
// </Modal>)
|
|
123
|
+
})}
|
|
124
|
+
</>
|
|
125
|
+
</RootViewContext.Provider>);
|
|
126
|
+
};
|
|
127
|
+
/*
|
|
128
|
+
const { addRootView, updateRootView, removeRootView ,searchQuery } = useRootView();
|
|
129
|
+
*/
|
|
130
|
+
export const useRootView = () => {
|
|
131
|
+
const context = useContext(RootViewContext);
|
|
132
|
+
if (!context) {
|
|
133
|
+
throw new Error('useRootView must be used within a RootViewProvider');
|
|
134
|
+
}
|
|
135
|
+
return context;
|
|
136
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as AutoPositionedPopup } from './AutoPositionedPopup';
|
|
2
|
+
export { default } from './AutoPositionedPopup';
|
|
3
|
+
export type { AutoPositionedPopupProps, SelectedItem, Data, } from './AutoPositionedPopupProps';
|
|
4
|
+
export { default as AutoPositionedPopupStyles } from './AutoPositionedPopup.style';
|
|
5
|
+
export { RootViewProvider, useRootView } from './RootViewContext';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGhD,YAAY,EACV,wBAAwB,EACxB,YAAY,EACZ,IAAI,GACL,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAGnF,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Main component export
|
|
2
|
+
export { default as AutoPositionedPopup } from './AutoPositionedPopup';
|
|
3
|
+
export { default } from './AutoPositionedPopup';
|
|
4
|
+
// Style exports (optional, for customization)
|
|
5
|
+
export { default as AutoPositionedPopupStyles } from './AutoPositionedPopup.style';
|
|
6
|
+
// RootView exports
|
|
7
|
+
export { RootViewProvider, useRootView } from './RootViewContext';
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-auto-positioned-popup",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "A highly customizable React Native auto-positioned popup component with search functionality and flexible styling options",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib/",
|
|
9
|
+
"src/",
|
|
10
|
+
"README.md",
|
|
11
|
+
"README_zh.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "npm run clean && tsc",
|
|
16
|
+
"watch": "tsc --watch",
|
|
17
|
+
"clean": "rimraf lib",
|
|
18
|
+
"prepare": "npm run build",
|
|
19
|
+
"prepublishOnly": "npm run build && npm run lint",
|
|
20
|
+
"postpublish": "echo \"Package published successfully! 🎉\"",
|
|
21
|
+
"test": "echo \"No tests specified\" && exit 0",
|
|
22
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
23
|
+
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
24
|
+
"type-check": "tsc --noEmit",
|
|
25
|
+
"pack-test": "npm pack --dry-run",
|
|
26
|
+
"release:patch": "npm version patch && npm publish",
|
|
27
|
+
"release:minor": "npm version minor && npm publish",
|
|
28
|
+
"release:major": "npm version major && npm publish",
|
|
29
|
+
"release": "node scripts/release.js",
|
|
30
|
+
"release:dry": "node scripts/release.js --dry-run",
|
|
31
|
+
"release:patch-auto": "node scripts/release.js --version patch",
|
|
32
|
+
"release:minor-auto": "node scripts/release.js --version minor",
|
|
33
|
+
"release:major-auto": "node scripts/release.js --version major",
|
|
34
|
+
"dev": "npm run watch"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"react-native",
|
|
38
|
+
"popup",
|
|
39
|
+
"dropdown",
|
|
40
|
+
"auto-positioned",
|
|
41
|
+
"search",
|
|
42
|
+
"flatlist",
|
|
43
|
+
"autocomplete",
|
|
44
|
+
"typescript",
|
|
45
|
+
"ios",
|
|
46
|
+
"android"
|
|
47
|
+
],
|
|
48
|
+
"author": "Stark <stark@example.com>",
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"homepage": "https://github.com/your-username/react-native-auto-positioned-popup#readme",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/your-username/react-native-auto-positioned-popup.git"
|
|
54
|
+
},
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/your-username/react-native-auto-positioned-popup/issues"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"react": ">=16.8.0",
|
|
60
|
+
"react-native": ">=0.60.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^24.2.1",
|
|
64
|
+
"@types/react": "^18.3.23",
|
|
65
|
+
"@types/react-native": "^0.72.8",
|
|
66
|
+
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
67
|
+
"@typescript-eslint/parser": "^6.21.0",
|
|
68
|
+
"eslint": "^8.57.1",
|
|
69
|
+
"eslint-plugin-react": "^7.37.5",
|
|
70
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
71
|
+
"eslint-plugin-react-native": "^4.1.0",
|
|
72
|
+
"rimraf": "^6.0.1",
|
|
73
|
+
"typescript": "^5.9.2"
|
|
74
|
+
},
|
|
75
|
+
"engines": {
|
|
76
|
+
"node": ">=14.0.0"
|
|
77
|
+
},
|
|
78
|
+
"react-native": "src/index.tsx",
|
|
79
|
+
"dependencies": {
|
|
80
|
+
"react-native-advanced-flatlist": "^1.0.4"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export default StyleSheet.create({
|
|
4
|
+
des: {
|
|
5
|
+
fontSize: 12,
|
|
6
|
+
lineHeight: 20,
|
|
7
|
+
fontWeight: '400',
|
|
8
|
+
color: '#666666',
|
|
9
|
+
marginLeft: 4,
|
|
10
|
+
},
|
|
11
|
+
ListItemCode: {
|
|
12
|
+
fontSize: 15,
|
|
13
|
+
lineHeight: 20,
|
|
14
|
+
fontWeight: '600',
|
|
15
|
+
color: '#333333',
|
|
16
|
+
},
|
|
17
|
+
commonModalRow: {
|
|
18
|
+
height: 32,
|
|
19
|
+
borderBottomWidth: 0,
|
|
20
|
+
overflow: 'hidden',
|
|
21
|
+
alignItems: 'center',
|
|
22
|
+
justifyContent: 'center',
|
|
23
|
+
width: '100%',
|
|
24
|
+
flexDirection: 'row',
|
|
25
|
+
borderRadius: 8,
|
|
26
|
+
},
|
|
27
|
+
autoPositionedPopupList: {
|
|
28
|
+
flex: 1,
|
|
29
|
+
height: '100%',
|
|
30
|
+
padding: 12,
|
|
31
|
+
},
|
|
32
|
+
inputStyle: {
|
|
33
|
+
fontSize: 15,
|
|
34
|
+
fontWeight: '400',
|
|
35
|
+
lineHeight: 20,
|
|
36
|
+
color: '#333333',
|
|
37
|
+
width: '90%',
|
|
38
|
+
textAlign: 'right',
|
|
39
|
+
},
|
|
40
|
+
AutoPositionedPopupBtn: {
|
|
41
|
+
flex: 1,
|
|
42
|
+
flexDirection: 'row',
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
},
|
|
45
|
+
searchQueryTxt: {
|
|
46
|
+
fontSize: 17,
|
|
47
|
+
lineHeight: 24,
|
|
48
|
+
color: '#999999',
|
|
49
|
+
textAlign: 'right',
|
|
50
|
+
},
|
|
51
|
+
contain: {
|
|
52
|
+
flex: 1,
|
|
53
|
+
height: '100%',
|
|
54
|
+
},
|
|
55
|
+
selectArrow: {
|
|
56
|
+
marginLeft: 6,
|
|
57
|
+
marginRight: 8,
|
|
58
|
+
width: 8,
|
|
59
|
+
height: 14,
|
|
60
|
+
},
|
|
61
|
+
AutoDropdownBtnStyle1: {
|
|
62
|
+
justifyContent: 'flex-start',
|
|
63
|
+
},
|
|
64
|
+
AutoDropdownBtnStyle: {
|
|
65
|
+
justifyContent: 'flex-end',
|
|
66
|
+
},
|
|
67
|
+
usageRowText: {
|
|
68
|
+
fontSize: 17,
|
|
69
|
+
lineHeight: 21,
|
|
70
|
+
fontWeight: '400',
|
|
71
|
+
color: '#333333',
|
|
72
|
+
},
|
|
73
|
+
AutoDropdownRow: {
|
|
74
|
+
flex: 1,
|
|
75
|
+
height: 50,
|
|
76
|
+
flexDirection: 'row',
|
|
77
|
+
alignItems: 'center',
|
|
78
|
+
justifyContent: 'space-between',
|
|
79
|
+
},
|
|
80
|
+
});
|