@mpxjs/webpack-plugin 2.9.73 → 2.10.1
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/file-loader.js +5 -0
- package/lib/index.js +21 -3
- package/lib/platform/template/wx/component-config/input.js +1 -1
- package/lib/platform/template/wx/component-config/textarea.js +1 -1
- package/lib/platform/template/wx/component-config/unsupported.js +1 -1
- package/lib/platform/template/wx/component-config/video.js +28 -1
- package/lib/platform/template/wx/index.js +2 -2
- package/lib/react/processScript.js +6 -4
- package/lib/react/processTemplate.js +5 -3
- package/lib/runtime/components/react/KeyboardAvoidingView.tsx +108 -0
- package/lib/runtime/components/react/context.ts +18 -2
- package/lib/runtime/components/react/dist/KeyboardAvoidingView.jsx +89 -0
- package/lib/runtime/components/react/dist/context.js +1 -0
- package/lib/runtime/components/react/dist/event.config.js +1 -0
- package/lib/runtime/components/react/dist/getInnerListeners.js +8 -5
- package/lib/runtime/components/react/dist/mpx-button.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/index.jsx +50 -0
- package/lib/runtime/components/react/dist/mpx-image.jsx +19 -18
- package/lib/runtime/components/react/dist/mpx-input.jsx +48 -19
- package/lib/runtime/components/react/dist/mpx-movable-view.jsx +16 -14
- package/lib/runtime/components/react/dist/mpx-picker/time.jsx +2 -1
- package/lib/runtime/components/react/dist/mpx-portal/index.jsx +30 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-host.jsx +112 -0
- package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +41 -0
- package/lib/runtime/components/react/dist/mpx-root-portal.jsx +1 -1
- package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +20 -9
- package/lib/runtime/components/react/dist/mpx-swiper.jsx +3 -2
- package/lib/runtime/components/react/dist/mpx-textarea.jsx +11 -3
- package/lib/runtime/components/react/dist/mpx-video.jsx +248 -0
- package/lib/runtime/components/react/dist/mpx-web-view.jsx +149 -50
- package/lib/runtime/components/react/dist/useAnimationHooks.js +4 -4
- package/lib/runtime/components/react/dist/utils.jsx +8 -3
- package/lib/runtime/components/react/event.config.ts +2 -0
- package/lib/runtime/components/react/getInnerListeners.ts +8 -5
- package/lib/runtime/components/react/mpx-button.tsx +1 -1
- package/lib/runtime/components/react/mpx-icon/icons/cancel.png +0 -0
- package/lib/runtime/components/react/mpx-icon/icons/clear.png +0 -0
- package/lib/runtime/components/react/mpx-icon/icons/download.png +0 -0
- package/lib/runtime/components/react/mpx-icon/icons/info.png +0 -0
- package/lib/runtime/components/react/mpx-icon/icons/search.png +0 -0
- package/lib/runtime/components/react/mpx-icon/icons/success.png +0 -0
- package/lib/runtime/components/react/mpx-icon/icons/success_no_circle.png +0 -0
- package/lib/runtime/components/react/mpx-icon/icons/waiting.png +0 -0
- package/lib/runtime/components/react/mpx-icon/icons/warn.png +0 -0
- package/lib/runtime/components/react/mpx-icon/index.tsx +111 -0
- package/lib/runtime/components/react/mpx-image.tsx +26 -14
- package/lib/runtime/components/react/mpx-input.tsx +52 -20
- package/lib/runtime/components/react/mpx-movable-view.tsx +19 -16
- package/lib/runtime/components/react/mpx-picker/time.tsx +2 -1
- package/lib/runtime/components/react/mpx-portal/index.tsx +39 -0
- package/lib/runtime/components/react/mpx-portal/portal-host.tsx +141 -0
- package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +64 -0
- package/lib/runtime/components/react/mpx-root-portal.tsx +1 -1
- package/lib/runtime/components/react/mpx-scroll-view.tsx +22 -10
- package/lib/runtime/components/react/mpx-swiper.tsx +3 -2
- package/lib/runtime/components/react/mpx-textarea.tsx +13 -3
- package/lib/runtime/components/react/mpx-video.tsx +388 -0
- package/lib/runtime/components/react/mpx-web-view.tsx +180 -49
- package/lib/runtime/components/react/types/getInnerListeners.d.ts +1 -1
- package/lib/runtime/components/react/types/global.d.ts +8 -0
- package/lib/runtime/components/react/useAnimationHooks.ts +4 -4
- package/lib/runtime/components/react/utils.tsx +12 -6
- package/lib/script-setup-compiler/index.js +6 -2
- package/lib/style-compiler/index.js +3 -4
- package/lib/style-compiler/strip-conditional-loader.js +127 -0
- package/lib/template-compiler/compiler.js +19 -8
- package/lib/template-compiler/index.js +4 -4
- package/lib/web/processTemplate.js +7 -5
- package/lib/wxs/loader.js +2 -2
- package/lib/wxs/pre-loader.js +2 -2
- package/package.json +7 -5
- package/lib/runtime/components/react/dist/mpx-icon.jsx +0 -41
- package/lib/runtime/components/react/mpx-icon.tsx +0 -102
package/lib/file-loader.js
CHANGED
|
@@ -7,6 +7,8 @@ const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDep
|
|
|
7
7
|
module.exports = function loader (content, prevOptions) {
|
|
8
8
|
const options = prevOptions || loaderUtils.getOptions(this) || {}
|
|
9
9
|
const context = options.context || this.rootContext
|
|
10
|
+
const mpx = this.getMpx()
|
|
11
|
+
const isRN = ['ios', 'android', 'harmony'].includes(mpx.mode)
|
|
10
12
|
|
|
11
13
|
let url = loaderUtils.interpolateName(this, options.name, {
|
|
12
14
|
context,
|
|
@@ -33,6 +35,9 @@ module.exports = function loader (content, prevOptions) {
|
|
|
33
35
|
|
|
34
36
|
let publicPath = `__webpack_public_path__ + ${JSON.stringify(url)}`
|
|
35
37
|
|
|
38
|
+
// todo 未来添加分包处理后相对地址不一定是./开头的,需要考虑通过dependency的方式在sourceModule时通过最终的chunkName得到准确的相对路径
|
|
39
|
+
if (isRN) publicPath = `__non_webpack_require__(${JSON.stringify(`./${url}`)})`
|
|
40
|
+
|
|
36
41
|
if (options.publicPath) {
|
|
37
42
|
if (typeof options.publicPath === 'function') {
|
|
38
43
|
publicPath = options.publicPath(url, this.resourcePath, context)
|
package/lib/index.js
CHANGED
|
@@ -54,6 +54,7 @@ const wxssLoaderPath = normalize.lib('wxss/index')
|
|
|
54
54
|
const wxmlLoaderPath = normalize.lib('wxml/loader')
|
|
55
55
|
const wxsLoaderPath = normalize.lib('wxs/loader')
|
|
56
56
|
const styleCompilerPath = normalize.lib('style-compiler/index')
|
|
57
|
+
const styleStripConditionalPath = normalize.lib('style-compiler/strip-conditional-loader')
|
|
57
58
|
const templateCompilerPath = normalize.lib('template-compiler/index')
|
|
58
59
|
const jsonCompilerPath = normalize.lib('json-compiler/index')
|
|
59
60
|
const jsonThemeCompilerPath = normalize.lib('json-compiler/theme')
|
|
@@ -1506,12 +1507,12 @@ class MpxWebpackPlugin {
|
|
|
1506
1507
|
' code = code.replace(/const { (.*?) } = ctx/g, function (match, $1) {\n' +
|
|
1507
1508
|
' var arr = $1.split(", ")\n' +
|
|
1508
1509
|
' var str = ""\n' +
|
|
1509
|
-
' var pattern = /(
|
|
1510
|
+
' var pattern = /(\\w+)(?::\\s*(\\w+))?/\n' +
|
|
1510
1511
|
' for (var i = 0; i < arr.length; i++) {\n' +
|
|
1511
1512
|
' var result = arr[i].match(pattern)\n' +
|
|
1512
1513
|
' var left = result[1]\n' +
|
|
1513
|
-
' var right = result[2]\n' +
|
|
1514
|
-
' str += "var" + right + " = ctx." + left\n' +
|
|
1514
|
+
' var right = result[2] || left\n' +
|
|
1515
|
+
' str += "var " + right + " = ctx." + left + ";\\n "\n' +
|
|
1515
1516
|
' }\n' +
|
|
1516
1517
|
' return str\n' +
|
|
1517
1518
|
' })\n' +
|
|
@@ -1786,6 +1787,23 @@ try {
|
|
|
1786
1787
|
if (queryObj.mpx && queryObj.mpx !== MPX_PROCESSED_FLAG) {
|
|
1787
1788
|
const type = queryObj.type
|
|
1788
1789
|
const extract = queryObj.extract
|
|
1790
|
+
|
|
1791
|
+
if (type === 'styles') {
|
|
1792
|
+
let insertBeforeIndex = -1
|
|
1793
|
+
// 单次遍历收集所有索引
|
|
1794
|
+
loaders.forEach((loader, index) => {
|
|
1795
|
+
const currentLoader = toPosix(loader.loader)
|
|
1796
|
+
if (currentLoader.includes('node_modules/stylus-loader') || currentLoader.includes('node_modules/sass-loader') || currentLoader.includes('node_modules/less-loader')) {
|
|
1797
|
+
insertBeforeIndex = index
|
|
1798
|
+
}
|
|
1799
|
+
})
|
|
1800
|
+
|
|
1801
|
+
if (insertBeforeIndex !== -1) {
|
|
1802
|
+
loaders.splice(insertBeforeIndex, 0, { loader: styleStripConditionalPath })
|
|
1803
|
+
}
|
|
1804
|
+
loaders.push({ loader: styleStripConditionalPath })
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1789
1807
|
switch (type) {
|
|
1790
1808
|
case 'styles':
|
|
1791
1809
|
case 'template': {
|
|
@@ -92,7 +92,7 @@ module.exports = function ({ print }) {
|
|
|
92
92
|
qa: qaPropLog
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
|
-
test: /^(placeholder-style|placeholder-class|
|
|
95
|
+
test: /^(placeholder-style|placeholder-class|always-embed|hold-keyboard|safe-password-.+)$/,
|
|
96
96
|
ios: iosPropLog,
|
|
97
97
|
android: androidPropLog
|
|
98
98
|
}
|
|
@@ -78,7 +78,7 @@ module.exports = function ({ print }) {
|
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
|
-
test: /^(placeholder-style|placeholder-class|
|
|
81
|
+
test: /^(placeholder-style|placeholder-class|always-embed|hold-keyboard|disable-default-padding|adjust-keyboard-to|fixed|show-confirm-bar)$/,
|
|
82
82
|
ios: iosPropLog,
|
|
83
83
|
android: androidPropLog
|
|
84
84
|
}
|
|
@@ -11,7 +11,7 @@ const JD_UNSUPPORTED_TAG_NAME_ARR = ['functional-page-navigator', 'live-pusher',
|
|
|
11
11
|
// 快应用不支持的标签集合
|
|
12
12
|
const QA_UNSUPPORTED_TAG_NAME_ARR = ['movable-view', 'movable-area', 'open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'cover-image']
|
|
13
13
|
// RN不支持的标签集合
|
|
14
|
-
const RN_UNSUPPORTED_TAG_NAME_ARR = ['open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'progress', 'slider', 'audio', 'camera', '
|
|
14
|
+
const RN_UNSUPPORTED_TAG_NAME_ARR = ['open-data', 'official-account', 'editor', 'functional-page-navigator', 'live-player', 'live-pusher', 'ad', 'progress', 'slider', 'audio', 'camera', 'match-media', 'page-container', 'editor', 'keyboard-accessory', 'map']
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* @param {function(object): function} print
|
|
@@ -11,12 +11,24 @@ module.exports = function ({ print }) {
|
|
|
11
11
|
const aliEventLogError = print({ platform: 'ali', tag: TAG_NAME, isError: false, type: 'event' })
|
|
12
12
|
const qaPropLog = print({ platform: 'qa', tag: TAG_NAME, isError: false })
|
|
13
13
|
const qaEventLogError = print({ platform: 'qa', tag: TAG_NAME, isError: false, type: 'event' })
|
|
14
|
+
const iosPropLog = print({ platform: 'ios', tag: TAG_NAME, isError: false })
|
|
15
|
+
const iosEventLogError = print({ platform: 'ios', tag: TAG_NAME, isError: false, type: 'event' })
|
|
16
|
+
const androidPropLog = print({ platform: 'android', tag: TAG_NAME, isError: false })
|
|
17
|
+
const androidEventLogError = print({ platform: 'android', tag: TAG_NAME, isError: false, type: 'event' })
|
|
14
18
|
return {
|
|
15
19
|
test: TAG_NAME,
|
|
16
20
|
web (tag, { el }) {
|
|
17
21
|
el.isBuiltIn = true
|
|
18
22
|
return 'mpx-video'
|
|
19
23
|
},
|
|
24
|
+
android (tag, { el }) {
|
|
25
|
+
el.isBuiltIn = true
|
|
26
|
+
return 'mpx-video'
|
|
27
|
+
},
|
|
28
|
+
ios (tag, { el }) {
|
|
29
|
+
el.isBuiltIn = true
|
|
30
|
+
return 'mpx-video'
|
|
31
|
+
},
|
|
20
32
|
props: [
|
|
21
33
|
{
|
|
22
34
|
test: /^(enable-danmu|danmu-btn|show-progress|play-btn-position|enable-play-gesture|auto-pause-if-navigate|auto-pause-if-open-native|vslide-gesture|vslide-gesture-in-fullscreen|ad-unit-id|poster-for-crawler|show-casting-button|picture-in-picture-mode|picture-in-picture-show-progress|enable-auto-rotation|show-snapshot-button|show-screen-lock-button)$/,
|
|
@@ -41,13 +53,20 @@ module.exports = function ({ print }) {
|
|
|
41
53
|
qq: qqPropLog
|
|
42
54
|
},
|
|
43
55
|
{
|
|
44
|
-
|
|
45
56
|
test: /^(duration|danmu-list|danmu-btn|enable-danmu|muted|initial-time|page-gesture|direction|show-progress|show-center-play-btn|enable-progress-gesture|show-mute-btn|title|enable-play-gesture|auto-pause-if-navigate|auto-pause-if-open-native|vslide-gesture|vslide-gesture-in-fullscreen|ad-unit-id|poster-for-crawler|show-casting-button|picture-in-picture-mode|picture-in-picture-show-progress|enable-auto-rotation|show-screen-lock-button|show-snapshot-button)$/,
|
|
46
57
|
tt: ttPropLog
|
|
47
58
|
},
|
|
48
59
|
{
|
|
49
60
|
test: /^(duration|danmu-list|danmu-btn|enable-danmu|muted|initial-time|page-gesture|direction|show-progress|show-center-play-btn|enable-progress-gesture|show-mute-btn|enable-play-gesture|auto-pause-if-navigate|auto-pause-if-open-native|vslide-gesture|vslide-gesture-in-fullscreen|ad-unit-id|poster-for-crawler|show-casting-button|picture-in-picture-mode|picture-in-picture-show-progress|enable-auto-rotation|show-screen-lock-button|show-snapshot-button)$/,
|
|
50
61
|
qa: qaPropLog
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
test: /^(duration|enable-danmu|danmu-btn|page-gesture|direction|show-progress|show-fullscreen-btn|show-center-play-btn|enable-progress-gesture|show-mute-btn|title|play-btn-position|enable-play-gesture|auto-pause-if-navigate|auto-pause-if-open-native|vslide-gesture|vslide-gesture-in-fullscreen|show-bottom-progress|ad-unit-id|poster-for-crawler|show-casting-button|picture-in-picture-mode|picture-in-picture-show-progress| picture-in-picture-init-position|show-snapshot-button|show-screen-lock-button|show-background-playback-button|background-poster|referrer-policy|is-live)$/,
|
|
65
|
+
ios: iosPropLog
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
test: /^(duration|enable-danmu|danmu-btn|page-gesture|direction|show-progress|show-fullscreen-btn|show-center-play-btn|enable-progress-gesture|show-mute-btn|title|play-btn-position|enable-play-gesture|auto-pause-if-navigate|auto-pause-if-open-native|vslide-gesture|vslide-gesture-in-fullscreen|show-bottom-progress|ad-unit-id|poster-for-crawler|show-casting-button|picture-in-picture-mode|picture-in-picture-show-progress| picture-in-picture-init-position|enable-auto-rotation|show-snapshot-button|show-screen-lock-button|show-background-playback-button|background-poster|referrer-policy|is-live)$/,
|
|
69
|
+
android: androidPropLog
|
|
51
70
|
}
|
|
52
71
|
],
|
|
53
72
|
event: [
|
|
@@ -82,6 +101,14 @@ module.exports = function ({ print }) {
|
|
|
82
101
|
{
|
|
83
102
|
test: /^(progress|enterpictureinpicture|leavepictureinpicture|controlstoggle|loadedmetadata|seekcomplete)$/,
|
|
84
103
|
qa: qaEventLogError
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
test: /^(progress|controlstoggle|enterpictureinpicture|leavepictureinpicture|castinguserselect|castingstatechange|castinginterrupt)$/,
|
|
107
|
+
ios: iosEventLogError
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
test: /^(progress|enterpictureinpicture|leavepictureinpicture|castinguserselect|castingstatechange|castinginterrupt)$/,
|
|
111
|
+
android: androidEventLogError
|
|
85
112
|
}
|
|
86
113
|
]
|
|
87
114
|
}
|
|
@@ -68,7 +68,7 @@ module.exports = function getSpec ({ warn, error }) {
|
|
|
68
68
|
if (el) {
|
|
69
69
|
const injectWxsProp = {
|
|
70
70
|
injectWxsPath: '~' + normalize.lib('runtime/swanHelper.wxs'),
|
|
71
|
-
injectWxsModuleName: '
|
|
71
|
+
injectWxsModuleName: 'mpxSwanHelper'
|
|
72
72
|
}
|
|
73
73
|
if (el.injectWxsProps && Array.isArray(el.injectWxsProps)) {
|
|
74
74
|
el.injectWxsProps.push(injectWxsProp)
|
|
@@ -78,7 +78,7 @@ module.exports = function getSpec ({ warn, error }) {
|
|
|
78
78
|
}
|
|
79
79
|
return {
|
|
80
80
|
name: 's-for',
|
|
81
|
-
value: `${itemName}, ${indexName} in
|
|
81
|
+
value: `${itemName}, ${indexName} in mpxSwanHelper.processFor(${listName})${keyStr}`
|
|
82
82
|
}
|
|
83
83
|
},
|
|
84
84
|
web ({ value }, { el }) {
|
|
@@ -26,17 +26,19 @@ module.exports = function (script, {
|
|
|
26
26
|
output += `
|
|
27
27
|
import { getComponent } from ${stringifyRequest(loaderContext, optionProcessorPath)}
|
|
28
28
|
import { NavigationContainer, StackActions } from '@react-navigation/native'
|
|
29
|
-
import {
|
|
30
|
-
import
|
|
29
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
|
30
|
+
import PortalHost from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/portal-host'
|
|
31
|
+
import { useHeaderHeight } from '@react-navigation/elements';
|
|
31
32
|
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
32
33
|
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
|
33
34
|
|
|
34
35
|
global.__navigationHelper = {
|
|
35
36
|
NavigationContainer: NavigationContainer,
|
|
36
|
-
createStackNavigator:
|
|
37
|
+
createStackNavigator: createNativeStackNavigator,
|
|
38
|
+
useHeaderHeight: useHeaderHeight,
|
|
37
39
|
StackActions: StackActions,
|
|
38
40
|
GestureHandlerRootView: GestureHandlerRootView,
|
|
39
|
-
|
|
41
|
+
PortalHost: PortalHost,
|
|
40
42
|
SafeAreaProvider: SafeAreaProvider,
|
|
41
43
|
useSafeAreaInsets: useSafeAreaInsets
|
|
42
44
|
}\n`
|
|
@@ -28,9 +28,10 @@ module.exports = function (template, {
|
|
|
28
28
|
externalClasses,
|
|
29
29
|
checkUsingComponents,
|
|
30
30
|
autoVirtualHostRules,
|
|
31
|
+
forceProxyEventRules,
|
|
31
32
|
customTextRules
|
|
32
33
|
} = mpx
|
|
33
|
-
const { resourcePath } = parseRequest(loaderContext.resource)
|
|
34
|
+
const { resourcePath, rawResourcePath } = parseRequest(loaderContext.resource)
|
|
34
35
|
const builtInComponentsMap = {}
|
|
35
36
|
|
|
36
37
|
let genericsInfo
|
|
@@ -77,7 +78,7 @@ module.exports = function (template, {
|
|
|
77
78
|
// todo 后续输出web也采用mpx的scoped处理
|
|
78
79
|
hasScoped: false,
|
|
79
80
|
moduleId,
|
|
80
|
-
filePath:
|
|
81
|
+
filePath: rawResourcePath,
|
|
81
82
|
// react中模版i18n不需要特殊处理
|
|
82
83
|
i18n: null,
|
|
83
84
|
checkUsingComponents,
|
|
@@ -86,12 +87,13 @@ module.exports = function (template, {
|
|
|
86
87
|
// web模式下实现抽象组件
|
|
87
88
|
componentGenerics,
|
|
88
89
|
hasVirtualHost: matchCondition(resourcePath, autoVirtualHostRules),
|
|
90
|
+
forceProxyEvent: matchCondition(resourcePath, forceProxyEventRules),
|
|
89
91
|
isCustomText: matchCondition(resourcePath, customTextRules)
|
|
90
92
|
})
|
|
91
93
|
|
|
92
94
|
if (meta.wxsContentMap) {
|
|
93
95
|
for (const module in meta.wxsContentMap) {
|
|
94
|
-
wxsContentMap[`${
|
|
96
|
+
wxsContentMap[`${rawResourcePath}~${module}`] = meta.wxsContentMap[module]
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
99
|
if (meta.builtInComponentsMap) {
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React, { ReactNode, useContext, useEffect } from 'react'
|
|
2
|
+
import { DimensionValue, EmitterSubscription, Keyboard, Platform, View, ViewStyle } from 'react-native'
|
|
3
|
+
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated'
|
|
4
|
+
import { KeyboardAvoidContext } from './context'
|
|
5
|
+
import { extendObject } from './utils'
|
|
6
|
+
|
|
7
|
+
type KeyboardAvoidViewProps = {
|
|
8
|
+
children?: ReactNode
|
|
9
|
+
style?: ViewStyle
|
|
10
|
+
contentContainerStyle?: ViewStyle
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: KeyboardAvoidViewProps) => {
|
|
14
|
+
const isIOS = Platform.OS === 'ios'
|
|
15
|
+
const duration = isIOS ? 250 : 300
|
|
16
|
+
const easing = isIOS ? Easing.inOut(Easing.ease) : Easing.out(Easing.quad)
|
|
17
|
+
|
|
18
|
+
const offset = useSharedValue(0)
|
|
19
|
+
const basic = useSharedValue('auto')
|
|
20
|
+
const keyboardAvoid = useContext(KeyboardAvoidContext)
|
|
21
|
+
|
|
22
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
23
|
+
return Object.assign(
|
|
24
|
+
{
|
|
25
|
+
transform: [{ translateY: -offset.value }]
|
|
26
|
+
},
|
|
27
|
+
isIOS ? {} : { flexBasis: basic.value as DimensionValue }
|
|
28
|
+
)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const resetKeyboard = () => {
|
|
32
|
+
keyboardAvoid?.current && extendObject(keyboardAvoid.current, {
|
|
33
|
+
cursorSpacing: 0,
|
|
34
|
+
ref: null
|
|
35
|
+
})
|
|
36
|
+
offset.value = withTiming(0, { duration, easing })
|
|
37
|
+
basic.value = 'auto'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
let subscriptions: EmitterSubscription[] = []
|
|
42
|
+
|
|
43
|
+
if (isIOS) {
|
|
44
|
+
subscriptions = [
|
|
45
|
+
Keyboard.addListener('keyboardWillShow', (evt: any) => {
|
|
46
|
+
if (!keyboardAvoid?.current) return
|
|
47
|
+
const { endCoordinates } = evt
|
|
48
|
+
const { ref, cursorSpacing = 0 } = keyboardAvoid.current
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
|
|
51
|
+
const aboveOffset = offset.value + pageY + height - endCoordinates.screenY
|
|
52
|
+
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
|
|
53
|
+
const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing)
|
|
54
|
+
const value = aboveOffset > 0 ? belowValue : aboveValue
|
|
55
|
+
offset.value = withTiming(value, { duration, easing })
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
}),
|
|
59
|
+
Keyboard.addListener('keyboardWillHide', resetKeyboard)
|
|
60
|
+
]
|
|
61
|
+
} else {
|
|
62
|
+
subscriptions = [
|
|
63
|
+
Keyboard.addListener('keyboardDidShow', (evt: any) => {
|
|
64
|
+
if (!keyboardAvoid?.current) return
|
|
65
|
+
const { endCoordinates } = evt
|
|
66
|
+
const { ref, cursorSpacing = 0 } = keyboardAvoid.current
|
|
67
|
+
ref?.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
|
|
68
|
+
const aboveOffset = pageY + height - endCoordinates.screenY
|
|
69
|
+
const belowOffset = endCoordinates.height - aboveOffset
|
|
70
|
+
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing
|
|
71
|
+
const belowValue = Math.min(belowOffset, cursorSpacing)
|
|
72
|
+
const value = aboveOffset > 0 ? belowValue : aboveValue
|
|
73
|
+
offset.value = withTiming(value, { duration, easing }, (finished) => {
|
|
74
|
+
if (finished) {
|
|
75
|
+
/**
|
|
76
|
+
* In the Android environment, the layout information is not synchronized after the animation,
|
|
77
|
+
* which results in the inability to correctly trigger element events.
|
|
78
|
+
* Here, we utilize flexBasic to proactively trigger a re-layout
|
|
79
|
+
*/
|
|
80
|
+
basic.value = '99.99%'
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
}),
|
|
85
|
+
Keyboard.addListener('keyboardDidHide', resetKeyboard)
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return () => {
|
|
90
|
+
subscriptions.forEach(subscription => subscription.remove())
|
|
91
|
+
}
|
|
92
|
+
}, [keyboardAvoid])
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<View style={style}>
|
|
96
|
+
<Animated.View
|
|
97
|
+
style={[
|
|
98
|
+
contentContainerStyle,
|
|
99
|
+
animatedStyle
|
|
100
|
+
]}
|
|
101
|
+
>
|
|
102
|
+
{children}
|
|
103
|
+
</Animated.View>
|
|
104
|
+
</View>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export default KeyboardAvoidingView
|
|
@@ -5,7 +5,10 @@ export type LabelContextValue = MutableRefObject<{
|
|
|
5
5
|
triggerChange: (evt: NativeSyntheticEvent<TouchEvent>) => void
|
|
6
6
|
}>
|
|
7
7
|
|
|
8
|
-
export type KeyboardAvoidContextValue =
|
|
8
|
+
export type KeyboardAvoidContextValue = MutableRefObject<{
|
|
9
|
+
cursorSpacing: number
|
|
10
|
+
ref: MutableRefObject<any>
|
|
11
|
+
}>
|
|
9
12
|
|
|
10
13
|
export interface GroupValue {
|
|
11
14
|
[key: string]: { checked: boolean; setValue: Dispatch<SetStateAction<boolean>> }
|
|
@@ -33,10 +36,21 @@ export interface IntersectionObserver {
|
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
|
|
39
|
+
export interface PortalContextValue {
|
|
40
|
+
mount: (children: React.ReactNode, key?: number | null, id?: number| null) => number| undefined
|
|
41
|
+
update: (key: number, children: React.ReactNode) => void
|
|
42
|
+
unmount: (key: number) => void
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
export interface ScrollViewContextValue {
|
|
37
46
|
gestureRef: React.RefObject<any> | null
|
|
38
47
|
}
|
|
39
48
|
|
|
49
|
+
export interface RouteContextValue {
|
|
50
|
+
pageId: number
|
|
51
|
+
navigation: Record<string, any>
|
|
52
|
+
}
|
|
53
|
+
|
|
40
54
|
export const MovableAreaContext = createContext({ width: 0, height: 0 })
|
|
41
55
|
|
|
42
56
|
export const FormContext = createContext<FormContextValue | null>(null)
|
|
@@ -53,10 +67,12 @@ export const VarContext = createContext({})
|
|
|
53
67
|
|
|
54
68
|
export const IntersectionObserverContext = createContext<IntersectionObserver | null>(null)
|
|
55
69
|
|
|
56
|
-
export const RouteContext = createContext<
|
|
70
|
+
export const RouteContext = createContext<RouteContextValue | null>(null)
|
|
57
71
|
|
|
58
72
|
export const SwiperContext = createContext({})
|
|
59
73
|
|
|
60
74
|
export const KeyboardAvoidContext = createContext<KeyboardAvoidContextValue | null>(null)
|
|
61
75
|
|
|
62
76
|
export const ScrollViewContext = createContext<ScrollViewContextValue>({ gestureRef: null })
|
|
77
|
+
|
|
78
|
+
export const PortalContext = createContext<PortalContextValue>(null as any)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React, { useContext, useEffect } from 'react';
|
|
2
|
+
import { Keyboard, Platform, View } from 'react-native';
|
|
3
|
+
import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
|
|
4
|
+
import { KeyboardAvoidContext } from './context';
|
|
5
|
+
import { extendObject } from './utils';
|
|
6
|
+
const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
|
|
7
|
+
const isIOS = Platform.OS === 'ios';
|
|
8
|
+
const duration = isIOS ? 250 : 300;
|
|
9
|
+
const easing = isIOS ? Easing.inOut(Easing.ease) : Easing.out(Easing.quad);
|
|
10
|
+
const offset = useSharedValue(0);
|
|
11
|
+
const basic = useSharedValue('auto');
|
|
12
|
+
const keyboardAvoid = useContext(KeyboardAvoidContext);
|
|
13
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
14
|
+
return Object.assign({
|
|
15
|
+
transform: [{ translateY: -offset.value }]
|
|
16
|
+
}, isIOS ? {} : { flexBasis: basic.value });
|
|
17
|
+
});
|
|
18
|
+
const resetKeyboard = () => {
|
|
19
|
+
keyboardAvoid?.current && extendObject(keyboardAvoid.current, {
|
|
20
|
+
cursorSpacing: 0,
|
|
21
|
+
ref: null
|
|
22
|
+
});
|
|
23
|
+
offset.value = withTiming(0, { duration, easing });
|
|
24
|
+
basic.value = 'auto';
|
|
25
|
+
};
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
let subscriptions = [];
|
|
28
|
+
if (isIOS) {
|
|
29
|
+
subscriptions = [
|
|
30
|
+
Keyboard.addListener('keyboardWillShow', (evt) => {
|
|
31
|
+
if (!keyboardAvoid?.current)
|
|
32
|
+
return;
|
|
33
|
+
const { endCoordinates } = evt;
|
|
34
|
+
const { ref, cursorSpacing = 0 } = keyboardAvoid.current;
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
ref?.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
37
|
+
const aboveOffset = offset.value + pageY + height - endCoordinates.screenY;
|
|
38
|
+
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
|
|
39
|
+
const belowValue = Math.min(endCoordinates.height, aboveOffset + cursorSpacing);
|
|
40
|
+
const value = aboveOffset > 0 ? belowValue : aboveValue;
|
|
41
|
+
offset.value = withTiming(value, { duration, easing });
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}),
|
|
45
|
+
Keyboard.addListener('keyboardWillHide', resetKeyboard)
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
subscriptions = [
|
|
50
|
+
Keyboard.addListener('keyboardDidShow', (evt) => {
|
|
51
|
+
if (!keyboardAvoid?.current)
|
|
52
|
+
return;
|
|
53
|
+
const { endCoordinates } = evt;
|
|
54
|
+
const { ref, cursorSpacing = 0 } = keyboardAvoid.current;
|
|
55
|
+
ref?.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
56
|
+
const aboveOffset = pageY + height - endCoordinates.screenY;
|
|
57
|
+
const belowOffset = endCoordinates.height - aboveOffset;
|
|
58
|
+
const aboveValue = -aboveOffset >= cursorSpacing ? 0 : aboveOffset + cursorSpacing;
|
|
59
|
+
const belowValue = Math.min(belowOffset, cursorSpacing);
|
|
60
|
+
const value = aboveOffset > 0 ? belowValue : aboveValue;
|
|
61
|
+
offset.value = withTiming(value, { duration, easing }, (finished) => {
|
|
62
|
+
if (finished) {
|
|
63
|
+
/**
|
|
64
|
+
* In the Android environment, the layout information is not synchronized after the animation,
|
|
65
|
+
* which results in the inability to correctly trigger element events.
|
|
66
|
+
* Here, we utilize flexBasic to proactively trigger a re-layout
|
|
67
|
+
*/
|
|
68
|
+
basic.value = '99.99%';
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}),
|
|
73
|
+
Keyboard.addListener('keyboardDidHide', resetKeyboard)
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
return () => {
|
|
77
|
+
subscriptions.forEach(subscription => subscription.remove());
|
|
78
|
+
};
|
|
79
|
+
}, [keyboardAvoid]);
|
|
80
|
+
return (<View style={style}>
|
|
81
|
+
<Animated.View style={[
|
|
82
|
+
contentContainerStyle,
|
|
83
|
+
animatedStyle
|
|
84
|
+
]}>
|
|
85
|
+
{children}
|
|
86
|
+
</Animated.View>
|
|
87
|
+
</View>);
|
|
88
|
+
};
|
|
89
|
+
export default KeyboardAvoidingView;
|
|
@@ -11,3 +11,4 @@ export const RouteContext = createContext(null);
|
|
|
11
11
|
export const SwiperContext = createContext({});
|
|
12
12
|
export const KeyboardAvoidContext = createContext(null);
|
|
13
13
|
export const ScrollViewContext = createContext({ gestureRef: null });
|
|
14
|
+
export const PortalContext = createContext(null);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export const TAP_EVENTS = ['bindtap', 'catchtap', 'capture-bindtap', 'capture-catchtap'];
|
|
1
2
|
const eventConfigMap = {
|
|
2
3
|
bindtap: { bitFlag: '0', events: ['onTouchStart', 'onTouchMove', 'onTouchEnd'] },
|
|
3
4
|
bindlongpress: { bitFlag: '1', events: ['onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'] },
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { useRef, useMemo } from 'react';
|
|
2
2
|
import { hasOwn, collectDataset } from '@mpxjs/utils';
|
|
3
|
-
import { useNavigation } from '
|
|
4
|
-
import {
|
|
5
|
-
import eventConfigMap from './event.config';
|
|
3
|
+
import { omit, extendObject, useNavigation } from './utils';
|
|
4
|
+
import eventConfigMap, { TAP_EVENTS } from './event.config';
|
|
6
5
|
const globalEventState = {
|
|
7
6
|
needPress: true
|
|
8
7
|
};
|
|
@@ -137,11 +136,15 @@ function handleTouchmove(e, type, ref, propsRef, config, navigation) {
|
|
|
137
136
|
];
|
|
138
137
|
const currentTouchEvent = type === 'bubble' ? bubbleTouchEvent : captureTouchEvent;
|
|
139
138
|
handleEmitEvent(currentTouchEvent, 'touchmove', e, propsRef, config, navigation);
|
|
140
|
-
|
|
139
|
+
if (TAP_EVENTS.some(eventName => propsRef.current[eventName])) {
|
|
140
|
+
checkIsNeedPress(e, type, ref);
|
|
141
|
+
}
|
|
141
142
|
}
|
|
142
143
|
function handleTouchend(e, type, ref, propsRef, config, navigation) {
|
|
143
144
|
// move event may not be triggered
|
|
144
|
-
|
|
145
|
+
if (TAP_EVENTS.some(eventName => propsRef.current[eventName])) {
|
|
146
|
+
checkIsNeedPress(e, type, ref);
|
|
147
|
+
}
|
|
145
148
|
const bubbleTouchEvent = ['catchtouchend', 'bindtouchend'];
|
|
146
149
|
const bubbleTapEvent = ['catchtap', 'bindtap'];
|
|
147
150
|
const captureTouchEvent = [
|
|
@@ -130,7 +130,7 @@ const Loading = ({ alone = false }) => {
|
|
|
130
130
|
const Button = forwardRef((buttonProps, ref) => {
|
|
131
131
|
const { textProps, innerProps: props = {} } = splitProps(buttonProps);
|
|
132
132
|
const { size = 'default', type = 'default', plain = false, disabled = false, loading = false, 'hover-class': hoverClass, 'hover-style': hoverStyle = {}, 'hover-start-time': hoverStartTime = 20, 'hover-stay-time': hoverStayTime = 70, 'open-type': openType, 'form-type': formType, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, style = {}, children, bindgetuserinfo, bindtap } = props;
|
|
133
|
-
const pageId = useContext(RouteContext);
|
|
133
|
+
const { pageId } = useContext(RouteContext) || {};
|
|
134
134
|
const formContext = useContext(FormContext);
|
|
135
135
|
const enableHover = hoverClass !== 'none';
|
|
136
136
|
const { isHover, gesture } = useHover({ enableHover, hoverStartTime, hoverStayTime, disabled });
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ✔ type
|
|
3
|
+
* ✔ size
|
|
4
|
+
* ✔ color
|
|
5
|
+
*/
|
|
6
|
+
import { forwardRef, useRef, createElement } from 'react';
|
|
7
|
+
import { Image } from 'react-native';
|
|
8
|
+
import useInnerProps from '../getInnerListeners';
|
|
9
|
+
import useNodesRef from '../useNodesRef';
|
|
10
|
+
import { useLayout, useTransformStyle, extendObject } from '../utils';
|
|
11
|
+
import Success from './icons/success.png';
|
|
12
|
+
import SuccessNoCircle from './icons/success_no_circle.png';
|
|
13
|
+
import Info from './icons/info.png';
|
|
14
|
+
import Warn from './icons/warn.png';
|
|
15
|
+
import Waiting from './icons/waiting.png';
|
|
16
|
+
import Cancel from './icons/cancel.png';
|
|
17
|
+
import Download from './icons/download.png';
|
|
18
|
+
import Search from './icons/search.png';
|
|
19
|
+
import Clear from './icons/clear.png';
|
|
20
|
+
const IconTypeMap = new Map([
|
|
21
|
+
['success', Success],
|
|
22
|
+
['success_no_circle', SuccessNoCircle],
|
|
23
|
+
['info', Info],
|
|
24
|
+
['warn', Warn],
|
|
25
|
+
['waiting', Waiting],
|
|
26
|
+
['cancel', Cancel],
|
|
27
|
+
['download', Download],
|
|
28
|
+
['search', Search],
|
|
29
|
+
['clear', Clear]
|
|
30
|
+
]);
|
|
31
|
+
const Icon = forwardRef((props, ref) => {
|
|
32
|
+
const { type, size = 23, color, style = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props;
|
|
33
|
+
const source = IconTypeMap.get(type);
|
|
34
|
+
const defaultStyle = { width: ~~size, height: ~~size };
|
|
35
|
+
const styleObj = extendObject({}, defaultStyle, style);
|
|
36
|
+
const { hasSelfPercent, normalStyle, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
|
|
37
|
+
const nodeRef = useRef(null);
|
|
38
|
+
useNodesRef(props, ref, nodeRef, { style: normalStyle });
|
|
39
|
+
const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef });
|
|
40
|
+
const innerProps = useInnerProps(props, extendObject({
|
|
41
|
+
ref: nodeRef,
|
|
42
|
+
source,
|
|
43
|
+
style: extendObject({}, normalStyle, layoutStyle, { tintColor: color })
|
|
44
|
+
}, layoutProps), [], {
|
|
45
|
+
layoutRef
|
|
46
|
+
});
|
|
47
|
+
return createElement(Image, innerProps);
|
|
48
|
+
});
|
|
49
|
+
Icon.displayName = 'MpxIcon';
|
|
50
|
+
export default Icon;
|