@mpxjs/webpack-plugin 2.9.59 → 2.9.62

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 (106) hide show
  1. package/lib/platform/style/wx/index.js +314 -254
  2. package/lib/platform/template/wx/component-config/checkbox-group.js +8 -0
  3. package/lib/platform/template/wx/component-config/checkbox.js +8 -0
  4. package/lib/platform/template/wx/component-config/cover-image.js +15 -0
  5. package/lib/platform/template/wx/component-config/cover-view.js +9 -0
  6. package/lib/platform/template/wx/component-config/form.js +13 -1
  7. package/lib/platform/template/wx/component-config/icon.js +8 -0
  8. package/lib/platform/template/wx/component-config/index.js +5 -1
  9. package/lib/platform/template/wx/component-config/label.js +15 -0
  10. package/lib/platform/template/wx/component-config/movable-area.js +18 -1
  11. package/lib/platform/template/wx/component-config/movable-view.js +18 -1
  12. package/lib/platform/template/wx/component-config/navigator.js +8 -0
  13. package/lib/platform/template/wx/component-config/picker-view-column.js +8 -0
  14. package/lib/platform/template/wx/component-config/picker-view.js +18 -2
  15. package/lib/platform/template/wx/component-config/picker.js +14 -1
  16. package/lib/platform/template/wx/component-config/radio-group.js +8 -0
  17. package/lib/platform/template/wx/component-config/radio.js +8 -0
  18. package/lib/platform/template/wx/component-config/root-portal.js +15 -0
  19. package/lib/platform/template/wx/component-config/switch.js +8 -0
  20. package/lib/platform/template/wx/component-config/unsupported.js +1 -3
  21. package/lib/react/processScript.js +2 -0
  22. package/lib/runtime/components/react/context.ts +38 -0
  23. package/lib/runtime/components/react/dist/context.js +7 -0
  24. package/lib/runtime/components/react/dist/getInnerListeners.js +22 -11
  25. package/lib/runtime/components/react/dist/mpx-button.jsx +67 -45
  26. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +81 -0
  27. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +152 -0
  28. package/lib/runtime/components/react/dist/mpx-form.jsx +59 -0
  29. package/lib/runtime/components/react/dist/mpx-icon.jsx +51 -0
  30. package/lib/runtime/components/react/dist/mpx-image/index.jsx +17 -22
  31. package/lib/runtime/components/react/dist/mpx-image/svg.jsx +0 -1
  32. package/lib/runtime/components/react/dist/mpx-input.jsx +38 -16
  33. package/lib/runtime/components/react/dist/mpx-label.jsx +63 -0
  34. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +46 -0
  35. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +346 -0
  36. package/lib/runtime/components/react/dist/mpx-navigator.jsx +35 -0
  37. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +69 -0
  38. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +138 -0
  39. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +139 -0
  40. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +90 -0
  41. package/lib/runtime/components/react/dist/mpx-picker/regionData.js +6099 -0
  42. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +76 -0
  43. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +244 -0
  44. package/lib/runtime/components/react/dist/mpx-picker/type.js +1 -0
  45. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +15 -0
  46. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +68 -0
  47. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +79 -0
  48. package/lib/runtime/components/react/dist/mpx-radio.jsx +169 -0
  49. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +11 -0
  50. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +66 -50
  51. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +206 -147
  52. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +9 -7
  53. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +3 -3
  54. package/lib/runtime/components/react/dist/mpx-switch.jsx +76 -0
  55. package/lib/runtime/components/react/dist/mpx-text.jsx +7 -19
  56. package/lib/runtime/components/react/dist/mpx-textarea.jsx +1 -1
  57. package/lib/runtime/components/react/dist/mpx-view.jsx +326 -96
  58. package/lib/runtime/components/react/dist/mpx-web-view.jsx +9 -15
  59. package/lib/runtime/components/react/dist/types/common.js +1 -0
  60. package/lib/runtime/components/react/dist/useNodesRef.js +3 -8
  61. package/lib/runtime/components/react/dist/utils.js +82 -14
  62. package/lib/runtime/components/react/getInnerListeners.ts +25 -13
  63. package/lib/runtime/components/react/mpx-button.tsx +87 -67
  64. package/lib/runtime/components/react/mpx-checkbox-group.tsx +147 -0
  65. package/lib/runtime/components/react/mpx-checkbox.tsx +245 -0
  66. package/lib/runtime/components/react/mpx-form.tsx +89 -0
  67. package/lib/runtime/components/react/mpx-icon.tsx +103 -0
  68. package/lib/runtime/components/react/mpx-image/index.tsx +20 -32
  69. package/lib/runtime/components/react/mpx-image/svg.tsx +2 -2
  70. package/lib/runtime/components/react/mpx-input.tsx +54 -26
  71. package/lib/runtime/components/react/mpx-label.tsx +115 -0
  72. package/lib/runtime/components/react/mpx-movable-area.tsx +67 -0
  73. package/lib/runtime/components/react/mpx-movable-view.tsx +425 -0
  74. package/lib/runtime/components/react/mpx-navigator.tsx +67 -0
  75. package/lib/runtime/components/react/mpx-picker/date.tsx +83 -0
  76. package/lib/runtime/components/react/mpx-picker/index.tsx +155 -0
  77. package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +153 -0
  78. package/lib/runtime/components/react/mpx-picker/region.tsx +104 -0
  79. package/lib/runtime/components/react/mpx-picker/regionData.ts +6101 -0
  80. package/lib/runtime/components/react/mpx-picker/selector.tsx +92 -0
  81. package/lib/runtime/components/react/mpx-picker/time.tsx +274 -0
  82. package/lib/runtime/components/react/mpx-picker/type.ts +107 -0
  83. package/lib/runtime/components/react/mpx-picker-view-column.tsx +28 -0
  84. package/lib/runtime/components/react/mpx-picker-view.tsx +104 -0
  85. package/lib/runtime/components/react/mpx-radio-group.tsx +147 -0
  86. package/lib/runtime/components/react/mpx-radio.tsx +246 -0
  87. package/lib/runtime/components/react/mpx-root-portal.tsx +25 -0
  88. package/lib/runtime/components/react/mpx-scroll-view.tsx +82 -58
  89. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +203 -156
  90. package/lib/runtime/components/react/mpx-swiper/index.tsx +12 -13
  91. package/lib/runtime/components/react/mpx-swiper/type.ts +11 -4
  92. package/lib/runtime/components/react/mpx-swiper-item.tsx +5 -3
  93. package/lib/runtime/components/react/mpx-switch.tsx +127 -0
  94. package/lib/runtime/components/react/mpx-text.tsx +52 -68
  95. package/lib/runtime/components/react/mpx-textarea.tsx +2 -2
  96. package/lib/runtime/components/react/mpx-view.tsx +373 -140
  97. package/lib/runtime/components/react/mpx-web-view.tsx +24 -28
  98. package/lib/runtime/components/react/types/common.ts +12 -0
  99. package/lib/runtime/components/react/types/getInnerListeners.ts +2 -1
  100. package/lib/runtime/components/react/types/global.d.ts +4 -0
  101. package/lib/runtime/components/react/useNodesRef.ts +3 -8
  102. package/lib/runtime/components/react/utils.ts +93 -15
  103. package/lib/runtime/optionProcessor.js +19 -17
  104. package/lib/template-compiler/compiler.js +56 -41
  105. package/lib/template-compiler/gen-node-react.js +7 -7
  106. package/package.json +6 -3
@@ -0,0 +1,147 @@
1
+ /**
2
+ * ✔ bindchange
3
+ */
4
+ import {
5
+ JSX,
6
+ useRef,
7
+ forwardRef,
8
+ ReactNode,
9
+ useContext
10
+ } from 'react'
11
+ import {
12
+ View,
13
+ NativeSyntheticEvent,
14
+ ViewStyle
15
+ } from 'react-native'
16
+ import { FormContext, FormFieldValue, RadioGroupContext, GroupValue } from './context'
17
+ import useInnerProps, { getCustomEvent } from './getInnerListeners'
18
+ import useNodesRef, { HandlerRef } from './useNodesRef'
19
+ import { throwReactWarning } from './utils'
20
+
21
+ export interface radioGroupProps {
22
+ name: string
23
+ style?: ViewStyle & Record<string, any>
24
+ 'enable-offset'?: boolean
25
+ children: ReactNode
26
+ bindchange?: (evt: NativeSyntheticEvent<TouchEvent> | unknown) => void
27
+ }
28
+
29
+ const radioGroup = forwardRef<
30
+ HandlerRef<View, radioGroupProps>,
31
+ radioGroupProps
32
+ >((props, ref): JSX.Element => {
33
+ const {
34
+ style = {},
35
+ 'enable-offset': enableOffset,
36
+ children,
37
+ bindchange,
38
+ } = props
39
+
40
+ const layoutRef = useRef({})
41
+
42
+ const formContext = useContext(FormContext)
43
+
44
+ let formValuesMap: Map<string, FormFieldValue> | undefined;
45
+
46
+ if (formContext) {
47
+ formValuesMap = formContext.formValuesMap
48
+ }
49
+
50
+ const groupValue: GroupValue = useRef({}).current
51
+
52
+ const defaultStyle = {
53
+ flexDirection: 'row',
54
+ flexWrap: 'wrap',
55
+ ...style
56
+ }
57
+
58
+ const { nodeRef } = useNodesRef(props, ref, {
59
+ defaultStyle
60
+ })
61
+
62
+ const onLayout = () => {
63
+ nodeRef.current?.measure(
64
+ (
65
+ x: number,
66
+ y: number,
67
+ width: number,
68
+ height: number,
69
+ offsetLeft: number,
70
+ offsetTop: number
71
+ ) => {
72
+ layoutRef.current = { x, y, width, height, offsetLeft, offsetTop }
73
+ }
74
+ )
75
+ }
76
+
77
+ const getSelectionValue = (): string | undefined => {
78
+ for (let key in groupValue) {
79
+ if (groupValue[key].checked) {
80
+ return key
81
+ }
82
+ }
83
+ }
84
+
85
+ const getValue = () => {
86
+ return getSelectionValue()
87
+ }
88
+
89
+ const resetValue = () => {
90
+ Object.keys(groupValue).forEach((key) => {
91
+ groupValue[key].checked = false
92
+ groupValue[key].setValue(false)
93
+ })
94
+ }
95
+
96
+ if (formValuesMap) {
97
+ if (!props.name) {
98
+ throwReactWarning('[Mpx runtime warn]: If a form component is used, the name attribute is required.')
99
+ } else {
100
+ formValuesMap.set(props.name, { getValue, resetValue })
101
+ }
102
+ }
103
+
104
+ const notifyChange = (
105
+ evt: NativeSyntheticEvent<TouchEvent>
106
+ ) => {
107
+ bindchange &&
108
+ bindchange(
109
+ getCustomEvent(
110
+ 'tap',
111
+ evt,
112
+ {
113
+ layoutRef,
114
+ detail: {
115
+ value: getSelectionValue()
116
+ }
117
+ },
118
+ props
119
+ )
120
+ )
121
+ }
122
+
123
+ const innerProps = useInnerProps(
124
+ props,
125
+ {
126
+ ref: nodeRef,
127
+ style: defaultStyle,
128
+ ...(enableOffset ? { onLayout } : {})
129
+ },
130
+ ['enable-offset'],
131
+ {
132
+ layoutRef
133
+ }
134
+ )
135
+
136
+ return (
137
+ <View {...innerProps}>
138
+ <RadioGroupContext.Provider value={{ groupValue, notifyChange }}>
139
+ {children}
140
+ </RadioGroupContext.Provider>
141
+ </View>
142
+ )
143
+ })
144
+
145
+ radioGroup.displayName = 'mpx-radio-group'
146
+
147
+ export default radioGroup
@@ -0,0 +1,246 @@
1
+ /**
2
+ * ✔ value
3
+ * ✔ disabled
4
+ * ✔ checked
5
+ * ✔ color
6
+ */
7
+ import { JSX, useRef, useState, forwardRef, useEffect, ReactNode, useContext, Dispatch, SetStateAction } from 'react'
8
+ import {
9
+ View,
10
+ Text,
11
+ StyleSheet,
12
+ ViewStyle,
13
+ NativeSyntheticEvent,
14
+ TextStyle
15
+ } from 'react-native'
16
+ import { LabelContext, RadioGroupContext } from './context'
17
+ import useInnerProps, { getCustomEvent } from './getInnerListeners'
18
+ import useNodesRef, { HandlerRef } from './useNodesRef'
19
+ import { every, splitStyle, splitProps, isText, throwReactWarning } from './utils'
20
+ import Icon from './mpx-icon'
21
+
22
+ export interface RadioProps {
23
+ value?: string
24
+ checked?: boolean
25
+ disabled?: boolean
26
+ color?: string
27
+ style?: ViewStyle & Record<string, any>
28
+ 'enable-offset'?: boolean
29
+ children: ReactNode
30
+ bindtap?: (evt: NativeSyntheticEvent<TouchEvent> | unknown) => void
31
+ catchtap?: (evt: NativeSyntheticEvent<TouchEvent> | unknown) => void
32
+ }
33
+
34
+ const styles = StyleSheet.create({
35
+ container: {
36
+ flexDirection: 'row',
37
+ alignItems: 'center'
38
+ },
39
+ wrapper: {
40
+ alignItems: 'center',
41
+ justifyContent: 'center',
42
+ width: 24,
43
+ height: 24,
44
+ borderColor: '#D1D1D1',
45
+ borderWidth: 1,
46
+ borderRadius: 12,
47
+ backgroundColor: '#ffffff',
48
+ marginRight: 5,
49
+ overflow: 'hidden'
50
+ },
51
+ wrapperChecked: {
52
+ borderWidth: 0
53
+ },
54
+ wrapperDisabled: {
55
+ backgroundColor: '#E1E1E1'
56
+ },
57
+ icon: {
58
+ opacity: 0
59
+ },
60
+ iconDisabled: {
61
+ backgroundColor: '#ADADAD'
62
+ },
63
+ iconChecked: {
64
+ opacity: 1
65
+ }
66
+ })
67
+
68
+ const Radio = forwardRef<HandlerRef<View, RadioProps>, RadioProps>(
69
+ (props, ref): JSX.Element => {
70
+ const {
71
+ value = '',
72
+ disabled = false,
73
+ checked = false,
74
+ color = '#09BB07',
75
+ style = [],
76
+ 'enable-offset': enableOffset,
77
+ children,
78
+ bindtap,
79
+ catchtap,
80
+ } = props
81
+
82
+ const layoutRef = useRef({})
83
+
84
+ const [isChecked, setIsChecked] = useState<boolean>(!!checked)
85
+
86
+ const groupContext = useContext(RadioGroupContext)
87
+ let groupValue: { [key: string]: { checked: boolean; setValue: Dispatch<SetStateAction<boolean>>; } } | undefined;
88
+ let notifyChange: (evt: NativeSyntheticEvent<TouchEvent>) => void | undefined;
89
+
90
+ const labelContext = useContext(LabelContext)
91
+
92
+ const { textStyle, imageStyle, innerStyle } = splitStyle(style)
93
+
94
+ if (imageStyle) {
95
+ throwReactWarning('[Mpx runtime warn]: Radio does not support background image-related styles!')
96
+ }
97
+
98
+ const defaultStyle = {
99
+ ...styles.wrapper,
100
+ ...(isChecked && styles.wrapperChecked),
101
+ ...(disabled && styles.wrapperDisabled),
102
+ }
103
+
104
+ const viewStyle = {
105
+ ...defaultStyle,
106
+ ...innerStyle
107
+ }
108
+
109
+ const onChange = (evt: NativeSyntheticEvent<TouchEvent>) => {
110
+ if (disabled || isChecked) return
111
+ setIsChecked(!isChecked)
112
+ if (groupValue) {
113
+ for (const [key, radio] of Object.entries(groupValue)) {
114
+ if (!radio) continue
115
+ radio.setValue(key === value)
116
+ radio.checked = key === value
117
+ }
118
+ }
119
+ notifyChange && notifyChange(evt)
120
+ }
121
+
122
+ const onTap = (evt: NativeSyntheticEvent<TouchEvent>) => {
123
+ if (disabled) return
124
+ bindtap && bindtap(getCustomEvent('tap', evt, { layoutRef }, props))
125
+ onChange(evt)
126
+ }
127
+
128
+ const catchTap = (evt: NativeSyntheticEvent<TouchEvent>) => {
129
+ if (disabled) return
130
+ catchtap && catchtap(getCustomEvent('tap', evt, { layoutRef }, props))
131
+ }
132
+
133
+ const { nodeRef } = useNodesRef(props, ref, {
134
+ defaultStyle,
135
+ change: onChange
136
+ })
137
+
138
+ const onLayout = () => {
139
+ nodeRef.current?.measure(
140
+ (
141
+ x: number,
142
+ y: number,
143
+ width: number,
144
+ height: number,
145
+ offsetLeft: number,
146
+ offsetTop: number
147
+ ) => {
148
+ layoutRef.current = { x, y, width, height, offsetLeft, offsetTop }
149
+ }
150
+ )
151
+ }
152
+
153
+ const wrapChildren = (
154
+ children: ReactNode,
155
+ textStyle?: TextStyle
156
+ ) => {
157
+ if (!children) return children
158
+ const { textProps } = splitProps(props)
159
+
160
+ if (every(children, (child) => isText(child))) {
161
+ if (textStyle || textProps) {
162
+ children = <Text key='radioTextWrap' style={textStyle || {}} {...(textProps || {})}>
163
+ {children}
164
+ </Text>
165
+ }
166
+ } else {
167
+ if (textStyle) {
168
+ throwReactWarning(
169
+ '[Mpx runtime warn]: Text style will be ignored unless every child of the Radio is Text node!'
170
+ )
171
+ }
172
+ }
173
+
174
+ return children
175
+ }
176
+
177
+ if (groupContext) {
178
+ groupValue = groupContext.groupValue
179
+ notifyChange = groupContext.notifyChange
180
+ }
181
+
182
+ if (labelContext) {
183
+ labelContext.current.triggerChange = onChange
184
+ }
185
+
186
+ const innerProps = useInnerProps(
187
+ props,
188
+ {
189
+ ref: nodeRef,
190
+ style: styles.container,
191
+ bindtap: onTap,
192
+ catchtap: catchTap,
193
+ ...(enableOffset ? { onLayout } : {})
194
+ },
195
+ ['enable-offset'],
196
+ {
197
+ layoutRef
198
+ }
199
+ )
200
+
201
+ useEffect(() => {
202
+ if (groupValue) {
203
+ groupValue[value] = {
204
+ checked: checked,
205
+ setValue: setIsChecked
206
+ }
207
+ }
208
+ return () => {
209
+ if (groupValue) {
210
+ delete groupValue[value]
211
+ }
212
+ }
213
+ }, [])
214
+
215
+ useEffect(() => {
216
+ if (checked !== isChecked) {
217
+ setIsChecked(checked)
218
+ if (groupValue) {
219
+ groupValue[value].checked = checked
220
+ }
221
+ }
222
+ }, [checked])
223
+
224
+ return (
225
+ <View {...innerProps}>
226
+ <View style={viewStyle}>
227
+ <Icon
228
+ type='success'
229
+ size={24}
230
+ color={disabled ? '#E1E1E1' : color}
231
+ style={{
232
+ ...styles.icon,
233
+ ...(isChecked && styles.iconChecked),
234
+ ...(disabled && styles.iconDisabled)
235
+ }}
236
+ />
237
+ </View>
238
+ {wrapChildren(children, textStyle)}
239
+ </View>
240
+ )
241
+ }
242
+ )
243
+
244
+ Radio.displayName = 'mpx-radio'
245
+
246
+ export default Radio
@@ -0,0 +1,25 @@
1
+ /**
2
+ * ✔ enable
3
+ */
4
+ import { ReactNode } from 'react'
5
+ import { Portal } from '@ant-design/react-native'
6
+
7
+ interface RootPortalProps {
8
+ enable?: boolean
9
+ children: ReactNode
10
+ }
11
+ const _RootPortal = (props: RootPortalProps) => {
12
+ const { children, enable = true } = props
13
+ return enable ? (
14
+ // @ts-ignore
15
+ <Portal>
16
+ {children}
17
+ </Portal>
18
+ ) : (
19
+ <>{children}</>
20
+ );
21
+ }
22
+
23
+ _RootPortal.displayName = 'mpx-root-portal'
24
+
25
+ export default _RootPortal
@@ -31,14 +31,15 @@
31
31
  * ✔ bindscrolltolower
32
32
  * ✔ bindscroll
33
33
  */
34
-
35
- import { View, ScrollView, RefreshControl, NativeSyntheticEvent, NativeScrollEvent, LayoutChangeEvent, ViewStyle } from 'react-native';
36
- import React, { useRef, useState, useEffect, forwardRef } from 'react';
34
+ import { ScrollView } from 'react-native-gesture-handler'
35
+ import { View, RefreshControl, NativeSyntheticEvent, NativeScrollEvent, LayoutChangeEvent, ViewStyle } from 'react-native';
36
+ import { JSX, ReactNode, RefObject, useRef, useState, useEffect, forwardRef } from 'react';
37
37
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
38
38
  import useNodesRef, { HandlerRef } from './useNodesRef'
39
+ import { throwReactWarning } from './utils'
39
40
 
40
41
  interface ScrollViewProps {
41
- children?: React.ReactNode;
42
+ children?: ReactNode;
42
43
  enhanced?: boolean;
43
44
  bounces?: boolean;
44
45
  style?: ViewStyle;
@@ -67,6 +68,7 @@ interface ScrollViewProps {
67
68
  bindtouchstart?: (event: NativeSyntheticEvent<TouchEvent>) => void;
68
69
  bindtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
69
70
  bindtouchend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
71
+ bindscrollend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
70
72
  }
71
73
  type ScrollAdditionalProps = {
72
74
  pinchGestureEnabled: boolean;
@@ -79,15 +81,17 @@ type ScrollAdditionalProps = {
79
81
  showsHorizontalScrollIndicator: boolean;
80
82
  showsVerticalScrollIndicator: boolean;
81
83
  scrollEnabled: boolean;
82
- ref: React.RefObject<ScrollView>;
84
+ ref: RefObject<ScrollView>;
83
85
  bounces?: boolean;
84
86
  pagingEnabled?: boolean;
85
87
  style?: ViewStyle;
86
88
  bindtouchstart?: (event: NativeSyntheticEvent<TouchEvent>) => void;
87
89
  bindtouchmove?: (event: NativeSyntheticEvent<TouchEvent>) => void;
88
90
  bindtouchend?: (event: NativeSyntheticEvent<TouchEvent>) => void;
91
+ onScrollEndDrag?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
92
+ onMomentumScrollEnd?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
89
93
  };
90
- const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, ScrollViewProps>((props: ScrollViewProps = {}, ref): React.JSX.Element => {
94
+ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, ScrollViewProps>((props: ScrollViewProps = {}, ref): JSX.Element => {
91
95
  const {
92
96
  children,
93
97
  enhanced = false,
@@ -105,10 +109,12 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
105
109
  'enable-offset': enableOffset,
106
110
  'show-scrollbar': showScrollbar = true
107
111
  } = props;
108
- const [snapScrollTop, setSnapScrollTop] = useState(0);
109
- const [snapScrollLeft, setSnapScrollLeft] = useState(0);
112
+
110
113
  const [refreshing, setRefreshing] = useState(true);
111
- const [scrollEnabled, setScrollEnabled] = useState(true);
114
+
115
+ const snapScrollTop = useRef(0);
116
+ const snapScrollLeft = useRef(0);
117
+
112
118
  const layoutRef = useRef({})
113
119
  const scrollOptions = useRef({
114
120
  contentLength: 0,
@@ -126,7 +132,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
126
132
  const { nodeRef: scrollViewRef } = useNodesRef(props, ref, {
127
133
  scrollOffset: scrollOptions,
128
134
  node: {
129
- scrollEnabled,
135
+ scrollEnabled: scrollX || scrollY,
130
136
  bounces,
131
137
  showScrollbar,
132
138
  pagingEnabled,
@@ -135,14 +141,25 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
135
141
  scrollTo: scrollToOffset
136
142
  }
137
143
  })
138
-
144
+ if (scrollX && scrollY) {
145
+ throwReactWarning('[Mpx runtime 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')
146
+ }
139
147
  useEffect(() => {
140
148
  if (
141
- snapScrollTop !== props['scroll-top'] ||
142
- snapScrollLeft !== props['scroll-left']
149
+ snapScrollTop.current !== props['scroll-top'] ||
150
+ snapScrollLeft.current !== props['scroll-left']
143
151
  ) {
144
- setSnapScrollTop(props['scroll-top'] || 0);
145
- setSnapScrollLeft(props['scroll-left'] || 0);
152
+
153
+ snapScrollTop.current = props['scroll-top'] || 0
154
+ snapScrollLeft.current = props['scroll-left'] || 0
155
+
156
+ initialTimeout.current = setTimeout(() => {
157
+ scrollToOffset(snapScrollLeft.current, snapScrollTop.current);
158
+ }, 0);
159
+
160
+ return () => {
161
+ initialTimeout.current && clearTimeout(initialTimeout.current);
162
+ };
146
163
  }
147
164
  }, [props['scroll-top'], props['scroll-left']]);
148
165
 
@@ -152,26 +169,6 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
152
169
  }
153
170
  }, [props['refresher-triggered']]);
154
171
 
155
- useEffect(() => {
156
- if (!props['scroll-x'] && !props['scroll-y']) {
157
- setScrollEnabled(false);
158
- } else {
159
- setScrollEnabled(true);
160
- }
161
- }, [props['scroll-x'], props['scroll-y']]);
162
-
163
- useEffect(() => {
164
- if (snapScrollTop || snapScrollLeft) {
165
- initialTimeout.current = setTimeout(() => {
166
- scrollToOffset(snapScrollLeft, snapScrollTop);
167
- }, 0);
168
- }
169
-
170
- return () => {
171
- initialTimeout.current && clearTimeout(initialTimeout.current);
172
- };
173
- }, [snapScrollTop, snapScrollLeft]);
174
-
175
172
  function selectLength(size: { height: number; width: number }) {
176
173
  return !scrollX ? size.height : size.width;
177
174
  }
@@ -234,6 +231,20 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
234
231
  }
235
232
  }
236
233
 
234
+ function updateScrollOptions(e: NativeSyntheticEvent<NativeScrollEvent>, position: Record<string, any>) {
235
+ const visibleLength = selectLength(e.nativeEvent.layoutMeasurement);
236
+ const contentLength = selectLength(e.nativeEvent.contentSize);
237
+ const offset = selectOffset(e.nativeEvent.contentOffset);
238
+ scrollOptions.current = {
239
+ ...scrollOptions.current,
240
+ contentLength,
241
+ offset,
242
+ scrollLeft: position.scrollLeft,
243
+ scrollTop: position.scrollTop,
244
+ visibleLength,
245
+ }
246
+ }
247
+
237
248
  function onScroll(e: NativeSyntheticEvent<NativeScrollEvent>) {
238
249
  const { bindscroll } = props;
239
250
  const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
@@ -252,18 +263,27 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
252
263
  layoutRef
253
264
  }, props)
254
265
  );
266
+ updateScrollOptions(e, { scrollLeft, scrollTop })
255
267
 
256
- const visibleLength = selectLength(e.nativeEvent.layoutMeasurement);
257
- const contentLength = selectLength(e.nativeEvent.contentSize);
258
- const offset = selectOffset(e.nativeEvent.contentOffset);
259
- scrollOptions.current = {
260
- ...scrollOptions.current,
261
- contentLength,
262
- offset,
263
- scrollLeft,
264
- scrollTop,
265
- visibleLength,
266
- };
268
+ }
269
+
270
+ function onScrollEnd(e: NativeSyntheticEvent<NativeScrollEvent>) {
271
+ const { bindscrollend } = props;
272
+ const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
273
+ const { width: scrollWidth, height: scrollHeight } = e.nativeEvent.contentSize
274
+ bindscrollend &&
275
+ bindscrollend(
276
+ getCustomEvent('scrollend', e, {
277
+ detail: {
278
+ scrollLeft,
279
+ scrollTop,
280
+ scrollHeight,
281
+ scrollWidth
282
+ },
283
+ layoutRef
284
+ }, props)
285
+ );
286
+ updateScrollOptions(e, { scrollLeft, scrollTop })
267
287
  onStartReached(e);
268
288
  onEndReached(e);
269
289
  }
@@ -292,15 +312,14 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
292
312
  binddragstart(
293
313
  getCustomEvent('dragstart', e, {
294
314
  detail: {
295
- scrollLeft: scrollOptions.current.scrollLeft || 0,
296
- scrollTop: scrollOptions.current.scrollTop || 0,
315
+ scrollLeft: scrollOptions.current.scrollLeft,
316
+ scrollTop: scrollOptions.current.scrollTop,
297
317
  },
298
318
  layoutRef
299
319
  }, props)
300
320
  )
301
321
  }
302
322
  }
303
-
304
323
  function onScrollTouchMove(e: NativeSyntheticEvent<TouchEvent>) {
305
324
  const { binddragging, bindtouchmove, enhanced } = props;
306
325
  bindtouchmove && bindtouchmove(e)
@@ -318,38 +337,43 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
318
337
  }
319
338
  }
320
339
 
321
- function onScrollTouchEnd(e: NativeSyntheticEvent<TouchEvent>) {
322
- const { binddragend, bindtouchend, enhanced } = props;
323
- bindtouchend && bindtouchend(e);
340
+ function onScrollEndDrag(e: NativeSyntheticEvent<NativeScrollEvent>) {
341
+ const { binddragend, enhanced } = props;
324
342
  if (enhanced) {
343
+ const { x: scrollLeft, y: scrollTop } = e.nativeEvent.contentOffset;
344
+ const { width: scrollWidth, height: scrollHeight } = e.nativeEvent.contentSize
325
345
  binddragend &&
326
346
  binddragend(
327
347
  getCustomEvent('dragend', e, {
328
348
  detail: {
329
- scrollLeft: scrollOptions.current.scrollLeft || 0,
330
- scrollTop: scrollOptions.current.scrollTop || 0,
349
+ scrollLeft: scrollLeft,
350
+ scrollTop: scrollTop,
351
+ scrollHeight,
352
+ scrollWidth
331
353
  },
332
354
  layoutRef
333
355
  }, props)
334
356
  )
357
+ updateScrollOptions(e, { scrollLeft, scrollTop })
335
358
  }
336
359
  }
337
360
 
338
361
  let scrollAdditionalProps: ScrollAdditionalProps = {
339
362
  pinchGestureEnabled: false,
340
- horizontal: scrollX || !scrollY,
363
+ horizontal: scrollX && !scrollY,
341
364
  scrollEventThrottle: scrollEventThrottle,
342
365
  scrollsToTop: enableBackToTop,
343
366
  showsHorizontalScrollIndicator: scrollX && showScrollbar,
344
367
  showsVerticalScrollIndicator: scrollY && showScrollbar,
345
- scrollEnabled: scrollEnabled,
368
+ scrollEnabled: scrollX || scrollY,
346
369
  ref: scrollViewRef,
347
370
  onScroll: onScroll,
348
371
  onContentSizeChange: onContentSizeChange,
349
372
  bindtouchstart: onScrollTouchStart,
350
- bindtouchend: onScrollTouchEnd,
351
373
  bindtouchmove: onScrollTouchMove,
352
- onLayout
374
+ onLayout,
375
+ onScrollEndDrag,
376
+ onMomentumScrollEnd: onScrollEnd
353
377
  };
354
378
  if (enhanced) {
355
379
  scrollAdditionalProps = {