@mpxjs/webpack-plugin 2.10.2 → 2.10.3

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.
Files changed (34) hide show
  1. package/lib/dependencies/RecordPageConfigsMapDependency.js +45 -0
  2. package/lib/index.js +12 -0
  3. package/lib/platform/style/wx/index.js +6 -4
  4. package/lib/platform/template/wx/component-config/view.js +12 -2
  5. package/lib/react/index.js +0 -1
  6. package/lib/react/processJSON.js +13 -2
  7. package/lib/react/processScript.js +4 -2
  8. package/lib/react/processTemplate.js +18 -3
  9. package/lib/react/script-helper.js +18 -4
  10. package/lib/runtime/components/react/dist/mpx-input.jsx +1 -11
  11. package/lib/runtime/components/react/dist/{KeyboardAvoidingView.jsx → mpx-keyboard-avoiding-view.jsx} +4 -3
  12. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +1 -2
  13. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +10 -5
  14. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +22 -0
  15. package/lib/runtime/components/react/dist/mpx-view.jsx +10 -5
  16. package/lib/runtime/components/react/dist/mpx-web-view.jsx +2 -2
  17. package/lib/runtime/components/react/dist/useAnimationHooks.js +46 -48
  18. package/lib/runtime/components/react/dist/utils.jsx +17 -7
  19. package/lib/runtime/components/react/mpx-input.tsx +1 -19
  20. package/lib/runtime/components/react/{KeyboardAvoidingView.tsx → mpx-keyboard-avoiding-view.tsx} +4 -2
  21. package/lib/runtime/components/react/mpx-portal/portal-manager.tsx +1 -2
  22. package/lib/runtime/components/react/mpx-scroll-view.tsx +13 -4
  23. package/lib/runtime/components/react/mpx-simple-view.tsx +32 -0
  24. package/lib/runtime/components/react/mpx-view.tsx +17 -10
  25. package/lib/runtime/components/react/mpx-web-view.tsx +7 -7
  26. package/lib/runtime/components/react/types/getInnerListeners.d.ts +1 -1
  27. package/lib/runtime/components/react/useAnimationHooks.ts +46 -48
  28. package/lib/runtime/components/react/utils.tsx +21 -10
  29. package/lib/runtime/optionProcessor.js +3 -2
  30. package/lib/style-compiler/index.js +8 -6
  31. package/lib/template-compiler/compiler.js +20 -12
  32. package/lib/utils/match-condition.js +14 -8
  33. package/lib/web/processJSON.js +1 -3
  34. package/package.json +3 -3
@@ -0,0 +1,45 @@
1
+ const NullDependency = require('webpack/lib/dependencies/NullDependency')
2
+ const makeSerializable = require('webpack/lib/util/makeSerializable')
3
+ const isEmptyObject = require('../utils/is-empty-object')
4
+
5
+ class RecordPageConfigMapDependency extends NullDependency {
6
+ constructor (resourcePath, jsonObj) {
7
+ super()
8
+ this.resourcePath = resourcePath || ''
9
+ this.jsonObj = jsonObj || {}
10
+ }
11
+
12
+ get type () {
13
+ return 'mpx record pageConfigsMap'
14
+ }
15
+
16
+ mpxAction (module, compilation, callback) {
17
+ const mpx = compilation.__mpx__
18
+ const pagePath = mpx.pagesMap[this.resourcePath]
19
+ if (pagePath && !isEmptyObject(this.jsonObj)) mpx.pageConfigsMap[pagePath] = this.jsonObj
20
+ return callback()
21
+ }
22
+
23
+ serialize (context) {
24
+ const { write } = context
25
+ write(this.resourcePath)
26
+ write(this.jsonObj)
27
+ super.serialize(context)
28
+ }
29
+
30
+ deserialize (context) {
31
+ const { read } = context
32
+ this.pagePath = read()
33
+ this.jsonObj = read()
34
+ super.deserialize(context)
35
+ }
36
+ }
37
+
38
+ RecordPageConfigMapDependency.Template = class RecordPageConfigMapDependencyTemplate {
39
+ apply () {
40
+ }
41
+ }
42
+
43
+ makeSerializable(RecordPageConfigMapDependency, '@mpxjs/webpack-plugin/lib/dependencies/RecordPageConfigMapDependency')
44
+
45
+ module.exports = RecordPageConfigMapDependency
package/lib/index.js CHANGED
@@ -34,6 +34,7 @@ const FixDescriptionInfoPlugin = require('./resolver/FixDescriptionInfoPlugin')
34
34
  // const RequireHeaderDependency = require('webpack/lib/dependencies/RequireHeaderDependency')
35
35
  // const RemovedModuleDependency = require('./dependencies/RemovedModuleDependency')
36
36
  const AppEntryDependency = require('./dependencies/AppEntryDependency')
37
+ const RecordPageConfigMapDependency = require('./dependencies/RecordPageConfigsMapDependency')
37
38
  const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDependency')
38
39
  const RecordGlobalComponentsDependency = require('./dependencies/RecordGlobalComponentsDependency')
39
40
  const RecordIndependentDependency = require('./dependencies/RecordIndependentDependency')
@@ -617,6 +618,9 @@ class MpxWebpackPlugin {
617
618
  compilation.dependencyFactories.set(RemoveEntryDependency, new NullFactory())
618
619
  compilation.dependencyTemplates.set(RemoveEntryDependency, new RemoveEntryDependency.Template())
619
620
 
621
+ compilation.dependencyFactories.set(RecordPageConfigMapDependency, new NullFactory())
622
+ compilation.dependencyTemplates.set(RecordPageConfigMapDependency, new RecordPageConfigMapDependency.Template())
623
+
620
624
  compilation.dependencyFactories.set(RecordResourceMapDependency, new NullFactory())
621
625
  compilation.dependencyTemplates.set(RecordResourceMapDependency, new RecordResourceMapDependency.Template())
622
626
 
@@ -656,6 +660,8 @@ class MpxWebpackPlugin {
656
660
  __vfs,
657
661
  // app信息,便于获取appName
658
662
  appInfo: {},
663
+ // pageConfig信息
664
+ pageConfigsMap: {},
659
665
  // pages全局记录,无需区分主包分包
660
666
  pagesMap: {},
661
667
  // 组件资源记录,依照所属包进行记录
@@ -1641,6 +1647,12 @@ class MpxWebpackPlugin {
1641
1647
  if (isReact(mpx.mode)) {
1642
1648
  // 添加 @refresh reset 注释用于在 React HMR 时刷新组件
1643
1649
  source.add('/* @refresh reset */\n')
1650
+ // 注入页面的配置,供screen前置设置导航情况
1651
+ if (isRuntime) {
1652
+ source.add('// inject pageconfigmap for screen\n' +
1653
+ 'var context = (function() { return this })() || Function("return this")();\n')
1654
+ source.add(`context.__mpxPageConfigsMap = ${JSON.stringify(mpx.pageConfigsMap)};\n`)
1655
+ }
1644
1656
  source.add(originalSource)
1645
1657
  compilation.assets[chunkFile] = source
1646
1658
  processedChunk.add(chunk)
@@ -47,7 +47,7 @@ module.exports = function getSpec ({ warn, error }) {
47
47
  'flex-wrap': ['wrap', 'nowrap', 'wrap-reverse'],
48
48
  'pointer-events': ['auto', 'box-none', 'box-only', 'none'],
49
49
  'vertical-align': ['auto', 'top', 'bottom', 'center'],
50
- position: ['relative', 'absolute'],
50
+ position: ['relative', 'absolute', 'fixed'],
51
51
  'font-variant': ['small-caps', 'oldstyle-nums', 'lining-nums', 'tabular-nums', 'proportional-nums'],
52
52
  'text-align': ['left', 'right', 'center', 'justify'],
53
53
  'font-style': ['normal', 'italic'],
@@ -395,22 +395,22 @@ module.exports = function getSpec ({ warn, error }) {
395
395
  case 'skewY':
396
396
  case 'perspective':
397
397
  // 单个值处理
398
+ // rotate 处理成 rotateZ
399
+ key = key === 'rotate' ? 'rotateZ' : key
398
400
  transform.push({ [key]: val })
399
401
  break
400
402
  case 'matrix':
401
- case 'matrix3d':
402
403
  transform.push({ [key]: parseValues(val, ',').map(val => +val) })
403
404
  break
404
405
  case 'translate':
405
406
  case 'scale':
406
407
  case 'skew':
407
- case 'rotate3d': // x y z angle
408
408
  case 'translate3d': // x y 支持 z不支持
409
409
  case 'scale3d': // x y 支持 z不支持
410
410
  {
411
411
  // 2 个以上的值处理
412
412
  key = key.replace('3d', '')
413
- const vals = parseValues(val, ',').splice(0, key === 'rotate' ? 4 : 3)
413
+ const vals = parseValues(val, ',').splice(0, 3)
414
414
  // scale(.5) === scaleX(.5) scaleY(.5)
415
415
  if (vals.length === 1 && key === 'scale') {
416
416
  vals.push(vals[0])
@@ -426,6 +426,8 @@ module.exports = function getSpec ({ warn, error }) {
426
426
  }
427
427
  case 'translateZ':
428
428
  case 'scaleZ':
429
+ case 'rotate3d': // x y z angle
430
+ case 'matrix3d':
429
431
  default:
430
432
  // 不支持的属性处理
431
433
  unsupportedPropError({ prop, value, selector }, { mode })
@@ -25,11 +25,11 @@ module.exports = function ({ print }) {
25
25
  },
26
26
  ios (tag, { el }) {
27
27
  el.isBuiltIn = true
28
- return 'mpx-view'
28
+ return el.isSimple ? 'mpx-simple-view' : 'mpx-view'
29
29
  },
30
30
  android (tag, { el }) {
31
31
  el.isBuiltIn = true
32
- return 'mpx-view'
32
+ return el.isSimple ? 'mpx-simple-view' : 'mpx-view'
33
33
  },
34
34
  qa (tag) {
35
35
  return 'div'
@@ -47,6 +47,16 @@ module.exports = function ({ print }) {
47
47
  test: /^(hover-stop-propagation)$/,
48
48
  android: androidPropLog,
49
49
  ios: iosPropLog
50
+ }, {
51
+ test: /^(is-simple)$/,
52
+ android (prop, { el }) {
53
+ el.isSimple = true
54
+ return false
55
+ },
56
+ ios (prop, { el }) {
57
+ el.isSimple = true
58
+ return false
59
+ }
50
60
  }
51
61
  ],
52
62
  // 组件事件中的差异部分
@@ -85,7 +85,6 @@ module.exports = function ({
85
85
  srcMode,
86
86
  moduleId,
87
87
  isProduction,
88
- componentGenerics,
89
88
  jsonConfig: jsonRes.jsonObj,
90
89
  outputPath: queryObj.outputPath || '',
91
90
  builtInComponentsMap: templateRes.builtInComponentsMap,
@@ -12,6 +12,7 @@ const createJSONHelper = require('../json-compiler/helper')
12
12
  const getRulesRunner = require('../platform/index')
13
13
  const { RESOLVE_IGNORED_ERR } = require('../utils/const')
14
14
  const RecordResourceMapDependency = require('../dependencies/RecordResourceMapDependency')
15
+ const RecordPageConfigsMapDependency = require('../dependencies/RecordPageConfigsMapDependency')
15
16
 
16
17
  module.exports = function (jsonContent, {
17
18
  loaderContext,
@@ -79,7 +80,6 @@ module.exports = function (jsonContent, {
79
80
  })
80
81
  }
81
82
 
82
- const isApp = ctorType === 'app'
83
83
  if (!jsonContent) {
84
84
  return callback()
85
85
  }
@@ -99,7 +99,7 @@ module.exports = function (jsonContent, {
99
99
  }
100
100
  }
101
101
 
102
- if (!isApp) {
102
+ if (ctorType !== 'app') {
103
103
  rulesRunnerOptions.mainKey = ctorType
104
104
  }
105
105
 
@@ -112,6 +112,17 @@ module.exports = function (jsonContent, {
112
112
  return callback(e)
113
113
  }
114
114
 
115
+ if (ctorType === 'page') {
116
+ const keysToExtract = ['navigationStyle']
117
+ const configObj = {}
118
+ keysToExtract.forEach(key => {
119
+ if (jsonObj[key]) {
120
+ configObj[key] = jsonObj[key]
121
+ }
122
+ })
123
+ loaderContext._module.addPresentationalDependency(new RecordPageConfigsMapDependency(parseRequest(loaderContext.resource).resourcePath, configObj))
124
+ }
125
+
115
126
  const fs = loaderContext._compiler.inputFileSystem
116
127
 
117
128
  const defaultTabbar = {
@@ -12,7 +12,9 @@ module.exports = function (script, {
12
12
  outputPath,
13
13
  builtInComponentsMap,
14
14
  localComponentsMap,
15
- localPagesMap
15
+ localPagesMap,
16
+ componentGenerics,
17
+ genericsInfo
16
18
  }, callback) {
17
19
  let scriptSrcMode = srcMode
18
20
  const mode = loaderContext.getMpx().mode
@@ -68,7 +70,7 @@ global.__navigationHelper = {
68
70
  jsonConfig
69
71
  })
70
72
 
71
- output += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, ctorType, jsonConfig, componentsMap, outputPath })
73
+ output += buildGlobalParams({ moduleId, scriptSrcMode, loaderContext, isProduction, ctorType, jsonConfig, componentsMap, outputPath, genericsInfo, componentGenerics })
72
74
  output += getRequireScript({ ctorType, script, loaderContext })
73
75
  output += `export default global.__mpxOptionsMap[${JSON.stringify(moduleId)}]\n`
74
76
  }
@@ -5,6 +5,8 @@ const loaderUtils = require('loader-utils')
5
5
  const templateCompiler = require('../template-compiler/compiler')
6
6
  const genNode = require('../template-compiler/gen-node-react')
7
7
  const bindThis = require('../template-compiler/bind-this')
8
+ const isEmptyObject = require('../utils/is-empty-object')
9
+ const dash2hump = require('../utils/hump-dash').dash2hump
8
10
 
9
11
  module.exports = function (template, {
10
12
  loaderContext,
@@ -75,16 +77,15 @@ module.exports = function (template, {
75
77
  defs,
76
78
  decodeHTMLText,
77
79
  externalClasses,
78
- // todo 后续输出web也采用mpx的scoped处理
79
80
  hasScoped: false,
80
81
  moduleId,
81
82
  filePath: rawResourcePath,
82
83
  // react中模版i18n不需要特殊处理
83
84
  i18n: null,
84
85
  checkUsingComponents,
85
- // web模式下全局组件不会被合入usingComponents中,故globalComponents可以传空
86
+ // rn模式下全局组件不会被合入usingComponents中,故globalComponents可以传空
86
87
  globalComponents: [],
87
- // web模式下实现抽象组件
88
+ // rn模式下实现抽象组件
88
89
  componentGenerics,
89
90
  hasVirtualHost: matchCondition(resourcePath, autoVirtualHostRules),
90
91
  forceProxyEvent: matchCondition(resourcePath, forceProxyEventRules),
@@ -148,6 +149,20 @@ ${e.stack}`)
148
149
  if (meta.options) {
149
150
  output += `global.currentInject.injectOptions = ${JSON.stringify(meta.options)};\n`
150
151
  }
152
+ if (!isEmptyObject(componentGenerics)) {
153
+ output += 'global.currentInject.injectProperties = {\n'
154
+ output += ' generichash: String,\n'
155
+
156
+ Object.keys(componentGenerics).forEach(genericName => {
157
+ const defaultValue = componentGenerics[genericName].default
158
+ if (defaultValue) {
159
+ output += ` generic${dash2hump(genericName)}: { type: String, value: '${genericName}default' },\n`
160
+ } else {
161
+ output += ` generic${dash2hump(genericName)}: String,\n`
162
+ }
163
+ })
164
+ output += '}\n'
165
+ }
151
166
  }
152
167
  }
153
168
 
@@ -89,7 +89,8 @@ function buildGlobalParams ({
89
89
  componentsMap,
90
90
  pagesMap,
91
91
  firstPage,
92
- outputPath
92
+ outputPath,
93
+ genericsInfo
93
94
  }) {
94
95
  let content = ''
95
96
  if (ctorType === 'app') {
@@ -115,9 +116,22 @@ global.currentInject.firstPage = ${JSON.stringify(firstPage)}\n`
115
116
  delete pageConfig.usingComponents
116
117
  content += `global.currentInject.pageConfig = ${JSON.stringify(pageConfig)}\n`
117
118
  }
118
- content += `global.currentInject.getComponents = function () {
119
- return ${shallowStringify(componentsMap)}
120
- }\n`
119
+
120
+ content += `
121
+
122
+ function getComponents() {
123
+ return ${shallowStringify(componentsMap)}
124
+ }
125
+
126
+ global.currentInject.getComponents = getComponents\n`
127
+ if (genericsInfo) {
128
+ content += `
129
+ const genericHash = ${JSON.stringify(genericsInfo.hash)}\n
130
+ global.__mpxGenericsMap[genericHash] = function (name) {
131
+ return getComponents()[name]
132
+ }
133
+ \n`
134
+ }
121
135
  if (ctorType === 'component') {
122
136
  content += `global.currentInject.componentPath = '/' + ${JSON.stringify(outputPath)}\n`
123
137
  }
@@ -170,15 +170,6 @@ const Input = forwardRef((props, ref) => {
170
170
  layoutRef
171
171
  }, props));
172
172
  };
173
- const onKeyPress = (evt) => {
174
- evt.nativeEvent.key === 'Enter' &&
175
- bindconfirm(getCustomEvent('confirm', evt, {
176
- detail: {
177
- value: tmpValue.current || ''
178
- },
179
- layoutRef
180
- }, props));
181
- };
182
173
  const onSubmitEditing = (evt) => {
183
174
  bindconfirm(getCustomEvent('confirm', evt, {
184
175
  detail: {
@@ -278,8 +269,7 @@ const Input = forwardRef((props, ref) => {
278
269
  onChange,
279
270
  onSelectionChange,
280
271
  onContentSizeChange,
281
- onKeyPress: bindconfirm && onKeyPress,
282
- onSubmitEditing: bindconfirm && multiline && onSubmitEditing
272
+ onSubmitEditing: bindconfirm && !multiline && onSubmitEditing
283
273
  }), [
284
274
  'type',
285
275
  'password',
@@ -1,6 +1,6 @@
1
1
  import React, { useContext, useEffect, useMemo } from 'react';
2
2
  import { Keyboard, Platform, View } from 'react-native';
3
- import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing, runOnJS } from 'react-native-reanimated';
3
+ import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
4
4
  import { GestureDetector, Gesture } from 'react-native-gesture-handler';
5
5
  import { KeyboardAvoidContext } from './context';
6
6
  const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
@@ -16,8 +16,8 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
16
16
  const gesture = useMemo(() => {
17
17
  return Gesture.Tap()
18
18
  .onEnd(() => {
19
- runOnJS(dismiss)();
20
- });
19
+ dismiss();
20
+ }).runOnJS(true);
21
21
  }, []);
22
22
  const animatedStyle = useAnimatedStyle(() => {
23
23
  return Object.assign({
@@ -96,4 +96,5 @@ const KeyboardAvoidingView = ({ children, style, contentContainerStyle }) => {
96
96
  </View>
97
97
  </GestureDetector>);
98
98
  };
99
+ KeyboardAvoidingView.displayName = 'MpxKeyboardAvoidingView';
99
100
  export default KeyboardAvoidingView;
@@ -1,6 +1,5 @@
1
1
  import { useState, useCallback, forwardRef, useImperativeHandle } from 'react';
2
2
  import { View, StyleSheet } from 'react-native';
3
- import { extendObject } from '../utils';
4
3
  const _PortalManager = forwardRef((props, ref) => {
5
4
  const [state, setState] = useState({
6
5
  portals: []
@@ -14,7 +13,7 @@ const _PortalManager = forwardRef((props, ref) => {
14
13
  setState((prevState) => ({
15
14
  portals: prevState.portals.map((item) => {
16
15
  if (item.key === key) {
17
- return extendObject({}, item, { children });
16
+ return Object.assign({}, item, { children });
18
17
  }
19
18
  return item;
20
19
  })
@@ -41,7 +41,7 @@ import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, ext
41
41
  import { IntersectionObserverContext, ScrollViewContext } from './context';
42
42
  const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
43
43
  const { textProps, innerProps: props = {} } = splitProps(scrollViewProps);
44
- const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, __selectRef } = props;
44
+ const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, 'scroll-event-throttle': scrollEventThrottle = 0, __selectRef } = props;
45
45
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
46
46
  const waitForHandlers = flatGesture(waitFor);
47
47
  const [refreshing, setRefreshing] = useState(true);
@@ -54,7 +54,6 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
54
54
  scrollTop: 0,
55
55
  visibleLength: 0
56
56
  });
57
- const scrollEventThrottle = 50;
58
57
  const hasCallScrollToUpper = useRef(true);
59
58
  const hasCallScrollToLower = useRef(false);
60
59
  const initialTimeout = useRef(null);
@@ -83,6 +82,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
83
82
  };
84
83
  }, []);
85
84
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
85
+ const lastOffset = useRef(0);
86
86
  if (scrollX && scrollY) {
87
87
  warn('scroll-x and scroll-y cannot be set to true at the same time, Mpx will use the value of scroll-y as the criterion');
88
88
  }
@@ -133,7 +133,8 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
133
133
  function onStartReached(e) {
134
134
  const { bindscrolltoupper } = props;
135
135
  const { offset } = scrollOptions.current;
136
- if (bindscrolltoupper && (offset <= upperThreshold)) {
136
+ const isScrollingBackward = offset < lastOffset.current;
137
+ if (bindscrolltoupper && (offset <= upperThreshold) && isScrollingBackward) {
137
138
  if (!hasCallScrollToUpper.current) {
138
139
  bindscrolltoupper(getCustomEvent('scrolltoupper', e, {
139
140
  detail: {
@@ -152,12 +153,13 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
152
153
  const { bindscrolltolower } = props;
153
154
  const { contentLength, visibleLength, offset } = scrollOptions.current;
154
155
  const distanceFromEnd = contentLength - visibleLength - offset;
155
- if (bindscrolltolower && (distanceFromEnd < lowerThreshold)) {
156
+ const isScrollingForward = offset > lastOffset.current;
157
+ if (bindscrolltolower && (distanceFromEnd < lowerThreshold) && isScrollingForward) {
156
158
  if (!hasCallScrollToLower.current) {
157
159
  hasCallScrollToLower.current = true;
158
160
  bindscrolltolower(getCustomEvent('scrolltolower', e, {
159
161
  detail: {
160
- direction: scrollX ? 'right' : 'botttom'
162
+ direction: scrollX ? 'right' : 'bottom'
161
163
  },
162
164
  layoutRef
163
165
  }, props));
@@ -206,6 +208,8 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
206
208
  onStartReached(e);
207
209
  onEndReached(e);
208
210
  updateIntersection();
211
+ // 在 onStartReached、onEndReached 执行完后更新 lastOffset
212
+ lastOffset.current = scrollOptions.current.offset;
209
213
  }
210
214
  function onScrollEnd(e) {
211
215
  const { bindscrollend } = props;
@@ -225,6 +229,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
225
229
  onStartReached(e);
226
230
  onEndReached(e);
227
231
  updateIntersection();
232
+ lastOffset.current = scrollOptions.current.offset;
228
233
  }
229
234
  function updateIntersection() {
230
235
  if (enableTriggerIntersectionObserver && intersectionObservers) {
@@ -0,0 +1,22 @@
1
+ import { View } from 'react-native';
2
+ import { createElement, forwardRef, useRef } from 'react';
3
+ import useNodesRef from './useNodesRef';
4
+ import { extendObject, splitProps, splitStyle, wrapChildren } from './utils';
5
+ const _View2 = forwardRef((simpleViewProps, ref) => {
6
+ const nodeRef = useRef(null);
7
+ const { textProps, innerProps: props = {} } = splitProps(simpleViewProps);
8
+ const { textStyle, innerStyle = {} } = splitStyle(props.style || {});
9
+ useNodesRef(props, ref, nodeRef, {
10
+ style: innerStyle || {}
11
+ });
12
+ return createElement(View, extendObject({}, props, {
13
+ style: innerStyle,
14
+ ref: nodeRef
15
+ }), wrapChildren(props, {
16
+ hasVarDec: false,
17
+ textStyle: textStyle,
18
+ textProps
19
+ }));
20
+ });
21
+ _View2.displayName = 'MpxSimpleView';
22
+ export default _View2;
@@ -14,6 +14,7 @@ import { parseUrl, PERCENT_REGEX, splitStyle, splitProps, useTransformStyle, wra
14
14
  import { error } from '@mpxjs/utils';
15
15
  import LinearGradient from 'react-native-linear-gradient';
16
16
  import { GestureDetector } from 'react-native-gesture-handler';
17
+ import Portal from './mpx-portal';
17
18
  const linearMap = new Map([
18
19
  ['top', 0],
19
20
  ['bottom', 180],
@@ -555,7 +556,7 @@ const _View = forwardRef((viewProps, ref) => {
555
556
  const enableHover = !!hoverStyle;
556
557
  const { isHover, gesture } = useHover({ enableHover, hoverStartTime, hoverStayTime });
557
558
  const styleObj = extendObject({}, defaultStyle, style, isHover ? hoverStyle : {});
558
- const { normalStyle, hasSelfPercent, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, {
559
+ const { normalStyle, hasSelfPercent, hasPositionFixed, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, {
559
560
  enableVar,
560
561
  externalVarContext,
561
562
  parentFontSize,
@@ -600,12 +601,16 @@ const _View = forwardRef((viewProps, ref) => {
600
601
  innerStyle,
601
602
  enableFastImage
602
603
  });
603
- const BaseComponent = enableStyleAnimation
604
+ let finalComponent = enableStyleAnimation
604
605
  ? createElement(Animated.View, innerProps, childNode)
605
606
  : createElement(View, innerProps, childNode);
606
- return enableHover
607
- ? createElement(GestureDetector, { gesture: gesture }, BaseComponent)
608
- : BaseComponent;
607
+ if (enableHover) {
608
+ finalComponent = createElement(GestureDetector, { gesture: gesture }, finalComponent);
609
+ }
610
+ if (hasPositionFixed) {
611
+ finalComponent = createElement(Portal, null, finalComponent);
612
+ }
613
+ return finalComponent;
609
614
  });
610
615
  _View.displayName = 'MpxView';
611
616
  export default _View;
@@ -45,7 +45,7 @@ const _WebView = forwardRef((props, ref) => {
45
45
  button: 'Reload'
46
46
  }
47
47
  };
48
- const currentErrorText = errorText[mpx.i18n.locale || 'zh-CN'];
48
+ const currentErrorText = errorText[mpx.i18n?.locale || 'zh-CN'];
49
49
  if (props.style) {
50
50
  warn('The web-view component does not support the style prop.');
51
51
  }
@@ -277,7 +277,7 @@ const _WebView = forwardRef((props, ref) => {
277
277
  setIsLoaded(false);
278
278
  }
279
279
  };
280
- return (<Portal key={pageLoadErr ? 'error' : 'webview'}>
280
+ return (<Portal>
281
281
  {pageLoadErr
282
282
  ? (<View style={[styles.loadErrorContext, defaultWebViewStyle]}>
283
283
  <View style={styles.loadErrorText}><Text style={{ fontSize: 14, color: '#999999' }}>{currentErrorText.text}</Text></View>