@mpxjs/webpack-plugin 2.10.18-beta.11 → 2.10.18-beta.13

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 (29) hide show
  1. package/lib/platform/template/wx/component-config/index.js +1 -5
  2. package/lib/react/style-helper.js +7 -16
  3. package/lib/runtime/components/ali/mpx-section-list.mpx +1 -1
  4. package/lib/runtime/components/extends/section-list.mpx +1 -0
  5. package/lib/runtime/components/extends/sticky-heder.mpx +1 -0
  6. package/lib/runtime/components/extends/sticky-section.mpx +1 -0
  7. package/lib/runtime/components/react/animationHooks/useTransitionHooks.ts +48 -6
  8. package/lib/runtime/components/react/dist/animationHooks/useTransitionHooks.js +44 -6
  9. package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
  10. package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
  11. package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
  12. package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
  13. package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
  14. package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
  15. package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
  16. package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
  17. package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
  18. package/lib/runtime/components/react/dist/mpx-input.jsx +3 -8
  19. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +9 -0
  20. package/lib/runtime/components/react/dist/mpx-section-list.d.ts +5 -2
  21. package/lib/runtime/components/react/dist/mpx-section-list.jsx +31 -15
  22. package/lib/runtime/components/react/dist/mpx-swiper.d.ts +1 -0
  23. package/lib/runtime/components/react/dist/mpx-swiper.jsx +15 -3
  24. package/lib/runtime/components/react/dist/utils.jsx +1 -1
  25. package/lib/runtime/components/react/mpx-section-list.tsx +56 -30
  26. package/lib/runtime/components/web/mpx-section-list.vue +1 -1
  27. package/package.json +1 -1
  28. package/lib/platform/template/wx/component-config/sticky-header.js +0 -23
  29. package/lib/platform/template/wx/component-config/sticky-section.js +0 -23
@@ -42,8 +42,6 @@ const wxs = require('./wxs')
42
42
  const component = require('./component')
43
43
  const fixComponentName = require('./fix-component-name')
44
44
  const rootPortal = require('./root-portal')
45
- const stickyHeader = require('./sticky-header')
46
- const stickySection = require('./sticky-section')
47
45
 
48
46
  module.exports = function getComponentConfigs ({ warn, error }) {
49
47
  /**
@@ -127,8 +125,6 @@ module.exports = function getComponentConfigs ({ warn, error }) {
127
125
  hyphenTagName({ print }),
128
126
  label({ print }),
129
127
  component(),
130
- rootPortal({ print }),
131
- stickyHeader({ print }),
132
- stickySection({ print })
128
+ rootPortal({ print })
133
129
  ]
134
130
  }
@@ -135,27 +135,18 @@ function getClassMap ({ content, filename, mode, srcMode, ctorType, formatValueN
135
135
  if (classMapKeys.length) {
136
136
  classMapKeys.forEach((key) => {
137
137
  if (Object.keys(classMapValue).length) {
138
- let _default = classMap[key]?._default
139
- let _media = classMap[key]?._media
138
+ // set css defalut value
139
+ const val = classMap[key] || {}
140
+ classMap[key] = Object.assign(val, classMapValue)
141
+
142
+ // set css media
140
143
  if (isMedia) {
141
- // 当前是媒体查询
142
- _default = _default || {}
143
- _media = _media || []
144
+ const _media = classMap[key]?._media || []
144
145
  _media.push({
145
146
  options,
146
147
  value: classMapValue
147
148
  })
148
- classMap[key] = {
149
- _media,
150
- _default
151
- }
152
- } else if (_default) {
153
- // 已有媒体查询数据,此次非媒体查询
154
- Object.assign(_default, classMapValue)
155
- } else {
156
- // 无媒体查询
157
- const val = classMap[key] || {}
158
- classMap[key] = Object.assign(val, classMapValue)
149
+ classMap[key]._media = _media
159
150
  }
160
151
  }
161
152
  })
@@ -152,7 +152,7 @@
152
152
  },
153
153
  pollingDuration: {
154
154
  type: Number,
155
- value: 3000
155
+ value: 300
156
156
  },
157
157
  useListHeader: {
158
158
  type: Boolean,
@@ -0,0 +1 @@
1
+ <!-- section-list 占位文件-->
@@ -0,0 +1 @@
1
+ <!-- sticky-header 占位文件-->
@@ -0,0 +1 @@
1
+ <!-- sticky-section 占位文件-->
@@ -144,7 +144,7 @@ function parseTransitionStyle (originalStyle: ExtendedViewStyle) {
144
144
  const transitionMap = transitionData.reduce((acc, cur) => {
145
145
  // hasOwn(transitionSupportedProperty, dash2hump(val)) || val === Transform
146
146
  const { property = '', duration = 0, delay = 0, easing = Easing.inOut(Easing.ease) } = cur
147
- if ((hasOwn(transitionSupportedProperty, dash2hump(property)) || property === 'transform') && duration > 0) {
147
+ if ((hasOwn(transitionSupportedProperty, dash2hump(property)) || property === 'transform') && duration >= 0) {
148
148
  acc[property] = {
149
149
  duration,
150
150
  delay,
@@ -157,17 +157,59 @@ function parseTransitionStyle (originalStyle: ExtendedViewStyle) {
157
157
  return transitionMap
158
158
  }
159
159
 
160
+ const transitionKeys = ['transition', 'transitionDuration', 'transitionTimingFunction', 'transitionDelay', 'transitionProperty'] as const
161
+
162
+ function getTransitionPropertyKeys (map: TransitionMap): string {
163
+ return Object.keys(map).sort().join(',')
164
+ }
165
+
160
166
  export default function useTransitionHooks<T, P> (props: AnimationHooksPropsType) {
161
167
  // console.log(`useTransitionHooks, props=`, props)
162
168
  const { style: originalStyle = {}, transitionend } = props
163
169
  // style变更标识(首次render不执行),初始值为0,首次渲染后为1
164
170
  const animationDeps = useRef(0)
165
- // 记录上次style map
166
- // const lastStyleRef = useRef({} as {[propName: keyof ExtendedViewStyle]: number|string})
167
- // ** style 中获取动画数据
171
+ // transition 时序属性动态更新追踪
172
+ const transitionMapVersionRef = useRef(0)
173
+ const lastTransitionStyleRef = useRef<Record<string, any>>(
174
+ transitionKeys.reduce((acc, key) => { acc[key] = originalStyle[key]; return acc }, {} as Record<string, any>)
175
+ )
176
+ const initialPropertyKeysRef = useRef('')
177
+ const prevTransitionMapRef = useRef<TransitionMap>({})
178
+ // 检测 transition 时序属性变化,返回版本号驱动 transitionMap 重新计算
179
+ const transitionMapVersion = useMemo(() => {
180
+ const prevStyle = lastTransitionStyleRef.current
181
+ const hasChanged = transitionKeys.some(key => prevStyle[key] !== originalStyle[key])
182
+ if (hasChanged) {
183
+ transitionKeys.forEach(key => { lastTransitionStyleRef.current[key] = originalStyle[key] })
184
+ transitionMapVersionRef.current++
185
+ }
186
+ return transitionMapVersionRef.current
187
+ }, [originalStyle])
188
+ // ** 从 style 中获取动画数据(支持动态更新 transitionDuration/transitionDelay/transitionTimingFunction)
168
189
  const transitionMap = useMemo(() => {
169
- return parseTransitionStyle(originalStyle)
170
- }, [])
190
+ const newTransitionMap = parseTransitionStyle(originalStyle)
191
+ const newPropertyKeys = getTransitionPropertyKeys(newTransitionMap)
192
+ if (!initialPropertyKeysRef.current) {
193
+ // 首次计算,记录初始属性集合
194
+ initialPropertyKeysRef.current = newPropertyKeys
195
+ prevTransitionMapRef.current = newTransitionMap
196
+ return newTransitionMap
197
+ }
198
+ // 检测 transitionProperty 是否变化
199
+ if (newPropertyKeys !== initialPropertyKeysRef.current) {
200
+ error('[Mpx runtime error]: dynamic setting transitionProperty is not supported')
201
+ // 保留初始属性集合,仅更新已有属性的时序
202
+ const prevMap = prevTransitionMapRef.current
203
+ const result = Object.keys(prevMap).reduce((map, property) => {
204
+ map[property] = newTransitionMap[property] || prevMap[property]
205
+ return map
206
+ }, {} as TransitionMap)
207
+ prevTransitionMapRef.current = result
208
+ return result
209
+ }
210
+ prevTransitionMapRef.current = newTransitionMap
211
+ return newTransitionMap
212
+ }, [transitionMapVersion])
171
213
  // ** style prop sharedValue interpolateOutput: SharedValue<InterpolateOutput>
172
214
  const { shareValMap, animatedKeys, animatedStyleKeys } = useMemo(() => {
173
215
  // 记录需要执行动画的 propName
@@ -117,7 +117,7 @@ function parseTransitionStyle(originalStyle) {
117
117
  const transitionMap = transitionData.reduce((acc, cur) => {
118
118
  // hasOwn(transitionSupportedProperty, dash2hump(val)) || val === Transform
119
119
  const { property = '', duration = 0, delay = 0, easing = Easing.inOut(Easing.ease) } = cur;
120
- if ((hasOwn(transitionSupportedProperty, dash2hump(property)) || property === 'transform') && duration > 0) {
120
+ if ((hasOwn(transitionSupportedProperty, dash2hump(property)) || property === 'transform') && duration >= 0) {
121
121
  acc[property] = {
122
122
  duration,
123
123
  delay,
@@ -129,17 +129,55 @@ function parseTransitionStyle(originalStyle) {
129
129
  // console.log(`parseTransitionStyle transitionMap=`, transitionMap)
130
130
  return transitionMap;
131
131
  }
132
+ const transitionKeys = ['transition', 'transitionDuration', 'transitionTimingFunction', 'transitionDelay', 'transitionProperty'];
133
+ function getTransitionPropertyKeys(map) {
134
+ return Object.keys(map).sort().join(',');
135
+ }
132
136
  export default function useTransitionHooks(props) {
133
137
  // console.log(`useTransitionHooks, props=`, props)
134
138
  const { style: originalStyle = {}, transitionend } = props;
135
139
  // style变更标识(首次render不执行),初始值为0,首次渲染后为1
136
140
  const animationDeps = useRef(0);
137
- // 记录上次style map
138
- // const lastStyleRef = useRef({} as {[propName: keyof ExtendedViewStyle]: number|string})
139
- // ** style 中获取动画数据
141
+ // transition 时序属性动态更新追踪
142
+ const transitionMapVersionRef = useRef(0);
143
+ const lastTransitionStyleRef = useRef(transitionKeys.reduce((acc, key) => { acc[key] = originalStyle[key]; return acc; }, {}));
144
+ const initialPropertyKeysRef = useRef('');
145
+ const prevTransitionMapRef = useRef({});
146
+ // 检测 transition 时序属性变化,返回版本号驱动 transitionMap 重新计算
147
+ const transitionMapVersion = useMemo(() => {
148
+ const prevStyle = lastTransitionStyleRef.current;
149
+ const hasChanged = transitionKeys.some(key => prevStyle[key] !== originalStyle[key]);
150
+ if (hasChanged) {
151
+ transitionKeys.forEach(key => { lastTransitionStyleRef.current[key] = originalStyle[key]; });
152
+ transitionMapVersionRef.current++;
153
+ }
154
+ return transitionMapVersionRef.current;
155
+ }, [originalStyle]);
156
+ // ** 从 style 中获取动画数据(支持动态更新 transitionDuration/transitionDelay/transitionTimingFunction)
140
157
  const transitionMap = useMemo(() => {
141
- return parseTransitionStyle(originalStyle);
142
- }, []);
158
+ const newTransitionMap = parseTransitionStyle(originalStyle);
159
+ const newPropertyKeys = getTransitionPropertyKeys(newTransitionMap);
160
+ if (!initialPropertyKeysRef.current) {
161
+ // 首次计算,记录初始属性集合
162
+ initialPropertyKeysRef.current = newPropertyKeys;
163
+ prevTransitionMapRef.current = newTransitionMap;
164
+ return newTransitionMap;
165
+ }
166
+ // 检测 transitionProperty 是否变化
167
+ if (newPropertyKeys !== initialPropertyKeysRef.current) {
168
+ error('[Mpx runtime error]: dynamic setting transitionProperty is not supported');
169
+ // 保留初始属性集合,仅更新已有属性的时序
170
+ const prevMap = prevTransitionMapRef.current;
171
+ const result = Object.keys(prevMap).reduce((map, property) => {
172
+ map[property] = newTransitionMap[property] || prevMap[property];
173
+ return map;
174
+ }, {});
175
+ prevTransitionMapRef.current = result;
176
+ return result;
177
+ }
178
+ prevTransitionMapRef.current = newTransitionMap;
179
+ return newTransitionMap;
180
+ }, [transitionMapVersion]);
143
181
  // ** style prop sharedValue interpolateOutput: SharedValue<InterpolateOutput>
144
182
  const { shareValMap, animatedKeys, animatedStyleKeys } = useMemo(() => {
145
183
  // 记录需要执行动画的 propName
@@ -40,7 +40,7 @@
40
40
  import { forwardRef, useRef, useState, useContext, useEffect, createElement } from 'react';
41
41
  import { TextInput } from 'react-native';
42
42
  import { warn } from '@mpxjs/utils';
43
- import { useUpdateEffect, useTransformStyle, useLayout, extendObject, isAndroid } from './utils';
43
+ import { useUpdateEffect, useTransformStyle, useLayout, extendObject } from './utils';
44
44
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
45
45
  import useNodesRef from './useNodesRef';
46
46
  import { FormContext, KeyboardAvoidContext } from './context';
@@ -285,11 +285,6 @@ const Input = forwardRef((props, ref) => {
285
285
  ? nodeRef.current?.focus()
286
286
  : nodeRef.current?.blur();
287
287
  }, [isAutoFocus]);
288
- // 使用 multiline 来修复光标位置问题
289
- // React Native 的 TextInput 在 textAlign center + placeholder 时光标会跑到右边
290
- // 这个问题只在 Android 上出现
291
- // 参考:https://github.com/facebook/react-native/issues/28794 (Android only)
292
- const needMultilineFix = isAndroid && !multiline;
293
288
  const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
294
289
  ref: nodeRef,
295
290
  style: extendObject({}, normalStyle, layoutStyle),
@@ -308,7 +303,7 @@ const Input = forwardRef((props, ref) => {
308
303
  underlineColorAndroid: 'rgba(0,0,0,0)',
309
304
  textAlignVertical: textAlignVertical,
310
305
  placeholderTextColor: placeholderStyle?.color,
311
- multiline: multiline || needMultilineFix,
306
+ multiline: !!multiline,
312
307
  onTouchStart,
313
308
  onTouchEnd,
314
309
  onFocus,
@@ -317,7 +312,7 @@ const Input = forwardRef((props, ref) => {
317
312
  onSelectionChange,
318
313
  onContentSizeChange,
319
314
  onSubmitEditing: bindconfirm && onSubmitEditing
320
- }, needMultilineFix ? { numberOfLines: 1 } : {}, !!multiline && confirmType === 'return' ? {} : { enterKeyHint: confirmType }), [
315
+ }, !!multiline && confirmType === 'return' ? {} : { enterKeyHint: confirmType }), [
321
316
  'type',
322
317
  'password',
323
318
  'placeholder-style',
@@ -73,6 +73,15 @@ const _PickerViewColumn = forwardRef((props, ref) => {
73
73
  clearTimerScrollTo();
74
74
  };
75
75
  }, []);
76
+ // `contentOffset` prop sets visual position but does not fire scroll events,
77
+ // so `offsetYShared` (from `useScrollViewOffset`) stays at 0 until the user scrolls.
78
+ // Directly sync it whenever `itemRawH` is established so wheel animation renders correctly.
79
+ useEffect(() => {
80
+ if (!itemRawH || dragging.current || scrolling.current) {
81
+ return;
82
+ }
83
+ offsetYShared.value = activeIndex.current * itemRawH;
84
+ }, [itemRawH]);
76
85
  useEffect(() => {
77
86
  if (!scrollViewRef.current ||
78
87
  !itemRawH ||
@@ -1,4 +1,5 @@
1
- import React from 'react';
1
+ /// <reference types="react" />
2
+ import { GestureHandler } from './utils';
2
3
  interface ListItem {
3
4
  isSectionHeader?: boolean;
4
5
  _originalItemIndex?: number;
@@ -39,10 +40,12 @@ interface SectionListProps {
39
40
  'refresher-enabled'?: boolean;
40
41
  'show-scrollbar'?: boolean;
41
42
  'refresher-triggered'?: boolean;
43
+ 'wait-for'?: Array<GestureHandler>;
44
+ 'simultaneous-handlers'?: Array<GestureHandler>;
42
45
  bindrefresherrefresh?: (event: any) => void;
43
46
  bindscrolltolower?: (event: any) => void;
44
47
  bindscroll?: (event: any) => void;
45
48
  [key: string]: any;
46
49
  }
47
- declare const _SectionList: React.ForwardRefExoticComponent<Omit<SectionListProps, "ref"> & React.RefAttributes<any>>;
50
+ declare const _SectionList: import("react").ForwardRefExoticComponent<Omit<SectionListProps, "ref"> & import("react").RefAttributes<any>>;
48
51
  export default _SectionList;
@@ -1,7 +1,8 @@
1
- import React, { forwardRef, useRef, useState, useEffect, useMemo, createElement, useImperativeHandle, memo } from 'react';
1
+ import { forwardRef, useRef, useState, useEffect, useMemo, createElement, useImperativeHandle, memo } from 'react';
2
2
  import { SectionList, RefreshControl } from 'react-native';
3
+ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
3
4
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
4
- import { extendObject, useLayout, useTransformStyle } from './utils';
5
+ import { extendObject, useLayout, useTransformStyle, flatGesture } from './utils';
5
6
  const getGeneric = (generichash, generickey) => {
6
7
  if (!generichash || !generickey)
7
8
  return null;
@@ -15,9 +16,10 @@ const getGeneric = (generichash, generickey) => {
15
16
  }));
16
17
  };
17
18
  const _SectionList = forwardRef((props = {}, ref) => {
18
- const { enhanced = false, bounces = true, scrollEventThrottle = 0, height, width, listData, generichash, style = {}, itemHeight = {}, sectionHeaderHeight = {}, listHeaderHeight = {}, listHeaderData = null, useListHeader = false, listFooterData = null, useListFooter = false, 'genericrecycle-item': genericrecycleItem, 'genericsection-header': genericsectionHeader, 'genericlist-header': genericListHeader, 'genericlist-footer': genericListFooter, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'enable-sticky': enableSticky = false, 'enable-back-to-top': enableBackToTop = false, 'end-reached-threshold': onEndReachedThreshold = 0.1, 'refresher-enabled': refresherEnabled, 'show-scrollbar': showScrollbar = true, 'refresher-triggered': refresherTriggered } = props;
19
+ const { enhanced = false, bounces = true, scrollEventThrottle = 0, height, width, listData, generichash, style = {}, itemHeight = {}, sectionHeaderHeight = {}, listHeaderHeight = {}, listHeaderData = null, useListHeader = false, listFooterData = null, useListFooter = false, 'genericrecycle-item': genericrecycleItem, 'genericsection-header': genericsectionHeader, 'genericlist-header': genericListHeader, 'genericlist-footer': genericListFooter, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'enable-sticky': enableSticky = false, 'enable-back-to-top': enableBackToTop = false, 'end-reached-threshold': onEndReachedThreshold = 0.1, 'refresher-enabled': refresherEnabled, 'show-scrollbar': showScrollbar = true, 'refresher-triggered': refresherTriggered, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor } = props;
19
20
  const [refreshing, setRefreshing] = useState(!!refresherTriggered);
20
21
  const scrollViewRef = useRef(null);
22
+ const sectionListGestureRef = useRef();
21
23
  const indexMap = useRef({});
22
24
  const reverseIndexMap = useRef({});
23
25
  const { hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight });
@@ -176,13 +178,13 @@ const _SectionList = forwardRef((props = {}, ref) => {
176
178
  offset += headerHeight;
177
179
  // 添加该 section 中所有 items 的位置信息
178
180
  section.data.forEach((item, itemIndex) => {
179
- const contenteight = getItemHeight({ sectionIndex, rowIndex: itemIndex });
181
+ const contentHeight = getItemHeight({ sectionIndex, rowIndex: itemIndex });
180
182
  layouts.push({
181
- length: contenteight,
183
+ length: contentHeight,
182
184
  offset,
183
185
  index: layouts.length
184
186
  });
185
- offset += contenteight;
187
+ offset += contentHeight;
186
188
  });
187
189
  // 添加该 section 尾部位置信息
188
190
  // 因为即使 sectionList 没传 renderSectionFooter,getItemLayout 中的 index 的计算也会包含尾部节点
@@ -210,21 +212,33 @@ const _SectionList = forwardRef((props = {}, ref) => {
210
212
  onScroll: onScroll,
211
213
  onEndReached: onEndReached
212
214
  }, layoutProps);
215
+ const nativeGesture = useMemo(() => {
216
+ const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
217
+ const waitForHandlers = flatGesture(waitFor);
218
+ const gesture = Gesture.Native().withRef(sectionListGestureRef);
219
+ if (simultaneousHandlers && simultaneousHandlers.length) {
220
+ gesture.simultaneousWithExternalGesture(...simultaneousHandlers);
221
+ }
222
+ if (waitForHandlers && waitForHandlers.length) {
223
+ gesture.requireExternalGestureToFail(...waitForHandlers);
224
+ }
225
+ return gesture;
226
+ }, [originSimultaneousHandlers, waitFor]);
213
227
  if (enhanced) {
214
- Object.assign(scrollAdditionalProps, {
228
+ extendObject(scrollAdditionalProps, {
215
229
  bounces
216
230
  });
217
231
  }
218
232
  if (refresherEnabled) {
219
- Object.assign(scrollAdditionalProps, {
233
+ extendObject(scrollAdditionalProps, {
220
234
  refreshing: refreshing
221
235
  });
222
236
  }
223
237
  useImperativeHandle(ref, () => {
224
- return {
225
- ...props,
238
+ return extendObject({}, props, {
239
+ gestureRef: sectionListGestureRef,
226
240
  scrollToIndex
227
- };
241
+ });
228
242
  });
229
243
  const innerProps = useInnerProps(extendObject({}, props, scrollAdditionalProps), [
230
244
  'id',
@@ -232,7 +246,9 @@ const _SectionList = forwardRef((props = {}, ref) => {
232
246
  'lower-threshold',
233
247
  'refresher-triggered',
234
248
  'refresher-enabled',
235
- 'bindrefresherrefresh'
249
+ 'bindrefresherrefresh',
250
+ 'simultaneous-handlers',
251
+ 'wait-for'
236
252
  ], { layoutRef });
237
253
  // 使用 ref 保存最新的数据,避免数据变化时组件销毁重建
238
254
  const listHeaderDataRef = useRef(listHeaderData);
@@ -272,7 +288,7 @@ const _SectionList = forwardRef((props = {}, ref) => {
272
288
  return null;
273
289
  return () => createElement(ListFooterGenericComponent, { listFooterData: listFooterDataRef.current });
274
290
  }, [useListFooter, generichash, genericListFooter]);
275
- return createElement(SectionList, extendObject({
291
+ return createElement(GestureDetector, { gesture: nativeGesture }, createElement(SectionList, extendObject({
276
292
  style: [{ height, width }, style, layoutStyle],
277
293
  sections: convertedListData,
278
294
  renderItem: renderItem,
@@ -281,12 +297,12 @@ const _SectionList = forwardRef((props = {}, ref) => {
281
297
  ListFooterComponent: useListFooter ? ListFooterComponent : null,
282
298
  renderSectionHeader: renderSectionHeader,
283
299
  refreshControl: refresherEnabled
284
- ? React.createElement(RefreshControl, {
300
+ ? createElement(RefreshControl, {
285
301
  onRefresh: onRefresh,
286
302
  refreshing: refreshing
287
303
  })
288
304
  : undefined
289
- }, innerProps));
305
+ }, innerProps)));
290
306
  });
291
307
  _SectionList.displayName = 'MpxSectionList';
292
308
  export default _SectionList;
@@ -58,6 +58,7 @@ interface SwiperProps {
58
58
  disableGesture?: boolean;
59
59
  'display-multiple-items'?: number;
60
60
  bindchange?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void;
61
+ bindchangestart?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void;
61
62
  }
62
63
  declare const SwiperWrapper: React.ForwardRefExoticComponent<SwiperProps & React.RefAttributes<HandlerRef<View, SwiperProps>>>;
63
64
  export default SwiperWrapper;
@@ -61,7 +61,7 @@ const easeMap = {
61
61
  easeInOutCubic: Easing.inOut(Easing.cubic)
62
62
  };
63
63
  const SwiperWrapper = forwardRef((props, ref) => {
64
- const { 'indicator-dots': showPagination, 'indicator-color': dotColor = 'rgba(0, 0, 0, .3)', 'indicator-width': dotWidth = 8, 'indicator-height': dotHeight = 8, 'indicator-radius': dotRadius = 4, 'indicator-spacing': dotSpacing = 4, 'indicator-margin': paginationMargin = 10, 'indicator-active-color': activeDotColor = '#000000', 'enable-var': enableVar = false, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'external-var-context': externalVarContext, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, autoplay = false, circular = false, disableGesture = false, current: propCurrent = 0, bindchange } = props;
64
+ const { 'indicator-dots': showPagination, 'indicator-color': dotColor = 'rgba(0, 0, 0, .3)', 'indicator-width': dotWidth = 8, 'indicator-height': dotHeight = 8, 'indicator-radius': dotRadius = 4, 'indicator-spacing': dotSpacing = 4, 'indicator-margin': paginationMargin = 10, 'indicator-active-color': activeDotColor = '#000000', 'enable-var': enableVar = false, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'external-var-context': externalVarContext, 'simultaneous-handlers': originSimultaneousHandlers = [], 'wait-for': waitFor = [], style = {}, autoplay = false, circular = false, disableGesture = false, current: propCurrent = 0, bindchange, bindchangestart } = props;
65
65
  const dotCommonStyle = {
66
66
  width: dotWidth,
67
67
  height: dotHeight,
@@ -300,6 +300,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
300
300
  nextIndex += 1;
301
301
  // targetOffset = -nextIndex * step.value - preMarginShared.value
302
302
  targetOffset = -nextIndex * step.value;
303
+ runOnJSCallback('handleSwiperChangeStart', nextIndex);
303
304
  offset.value = withTiming(targetOffset, {
304
305
  duration: easeDuration,
305
306
  easing: easeMap[easeingFunc]
@@ -314,6 +315,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
314
315
  nextIndex = 0;
315
316
  targetOffset = -(childrenLength.value + patchElmNumShared.value) * step.value + preMarginShared.value;
316
317
  // 执行动画到下一帧
318
+ runOnJSCallback('handleSwiperChangeStart', nextIndex);
317
319
  offset.value = withTiming(targetOffset, {
318
320
  duration: easeDuration
319
321
  }, () => {
@@ -328,6 +330,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
328
330
  nextIndex = currentIndex.value + 1;
329
331
  targetOffset = -(nextIndex + patchElmNumShared.value) * step.value + preMarginShared.value;
330
332
  // 执行动画到下一帧
333
+ runOnJSCallback('handleSwiperChangeStart', nextIndex);
331
334
  offset.value = withTiming(targetOffset, {
332
335
  duration: easeDuration,
333
336
  easing: easeMap[easeingFunc]
@@ -362,11 +365,16 @@ const SwiperWrapper = forwardRef((props, ref) => {
362
365
  const eventData = getCustomEvent('change', {}, { detail: { current, source: 'touch' }, layoutRef: layoutRef });
363
366
  bindchange && bindchange(eventData);
364
367
  }
368
+ function handleSwiperChangeStart(current) {
369
+ const eventData = getCustomEvent('changestart', {}, { detail: { current }, layoutRef: layoutRef });
370
+ bindchangestart && bindchangestart(eventData);
371
+ }
365
372
  const runOnJSCallbackRef = useRef({
366
373
  loop,
367
374
  pauseLoop,
368
375
  resumeLoop,
369
- handleSwiperChange
376
+ handleSwiperChange,
377
+ handleSwiperChangeStart
370
378
  });
371
379
  const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef);
372
380
  function getOffset(index, stepValue) {
@@ -387,6 +395,7 @@ const SwiperWrapper = forwardRef((props, ref) => {
387
395
  if (targetOffset !== offset.value) {
388
396
  // 内部基于props.current!==currentIndex.value决定是否使用动画及更新currentIndex.value
389
397
  if (propCurrent !== undefined && propCurrent !== currentIndex.value) {
398
+ runOnJSCallback('handleSwiperChangeStart', propCurrent);
390
399
  offset.value = withTiming(targetOffset, {
391
400
  duration: easeDuration,
392
401
  easing: easeMap[easeingFunc]
@@ -686,7 +695,10 @@ const SwiperWrapper = forwardRef((props, ref) => {
686
695
  const offsetHalf = computeHalf();
687
696
  if (childrenLength.value > 1 && offsetHalf) {
688
697
  const { selectedIndex } = getTargetPosition({ transdir: moveDistance });
689
- currentIndex.value = selectedIndex;
698
+ if (selectedIndex !== currentIndex.value) {
699
+ currentIndex.value = selectedIndex;
700
+ runOnJS(runOnJSCallback)('handleSwiperChangeStart', selectedIndex);
701
+ }
690
702
  }
691
703
  // 2. 非循环: 处理用户一直拖拽到临界点的场景,如果放到onFinalize无法阻止offset.value更新为越界的值
692
704
  if (!circularShared.value) {
@@ -285,7 +285,7 @@ export function parseValues(str, char = ' ') {
285
285
  function parseTransform(transformStr) {
286
286
  const values = parseValues(transformStr);
287
287
  // Todo transform 排序不一致时,transform动画会闪烁,故这里同样的排序输出 transform
288
- values.sort();
288
+ // values.sort()
289
289
  const transform = [];
290
290
  values.forEach(item => {
291
291
  const match = item.match(/([/\w]+)\((.+)\)/);
@@ -1,7 +1,8 @@
1
- import React, { forwardRef, useRef, useState, useEffect, useMemo, createElement, useImperativeHandle, useCallback, memo } from 'react'
1
+ import { forwardRef, useRef, useState, useEffect, useMemo, createElement, useImperativeHandle, memo } from 'react'
2
2
  import { SectionList, RefreshControl, NativeSyntheticEvent, NativeScrollEvent } from 'react-native'
3
+ import { Gesture, GestureDetector } from 'react-native-gesture-handler'
3
4
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
4
- import { extendObject, useLayout, useTransformStyle } from './utils'
5
+ import { extendObject, useLayout, useTransformStyle, GestureHandler, flatGesture } from './utils'
5
6
  interface ListItem {
6
7
  isSectionHeader?: boolean;
7
8
  _originalItemIndex?: number;
@@ -51,6 +52,8 @@ interface SectionListProps {
51
52
  'refresher-enabled'?: boolean;
52
53
  'show-scrollbar'?: boolean;
53
54
  'refresher-triggered'?: boolean;
55
+ 'wait-for'?: Array<GestureHandler>;
56
+ 'simultaneous-handlers'?: Array<GestureHandler>;
54
57
  bindrefresherrefresh?: (event: any) => void;
55
58
  bindscrolltolower?: (event: any) => void;
56
59
  bindscroll?: (event: any) => void;
@@ -107,12 +110,16 @@ const _SectionList = forwardRef<any, SectionListProps>((props = {}, ref) => {
107
110
  'end-reached-threshold': onEndReachedThreshold = 0.1,
108
111
  'refresher-enabled': refresherEnabled,
109
112
  'show-scrollbar': showScrollbar = true,
110
- 'refresher-triggered': refresherTriggered
113
+ 'refresher-triggered': refresherTriggered,
114
+ 'simultaneous-handlers': originSimultaneousHandlers,
115
+ 'wait-for': waitFor
111
116
  } = props
112
117
 
113
118
  const [refreshing, setRefreshing] = useState(!!refresherTriggered)
114
119
 
115
120
  const scrollViewRef = useRef<any>(null)
121
+ const sectionListGestureRef = useRef<any>()
122
+
116
123
 
117
124
  const indexMap = useRef<{ [key: string]: string | number }>({})
118
125
 
@@ -295,13 +302,13 @@ const _SectionList = forwardRef<any, SectionListProps>((props = {}, ref) => {
295
302
 
296
303
  // 添加该 section 中所有 items 的位置信息
297
304
  section.data.forEach((item: ListItem, itemIndex: number) => {
298
- const contenteight = getItemHeight({ sectionIndex, rowIndex: itemIndex })
305
+ const contentHeight = getItemHeight({ sectionIndex, rowIndex: itemIndex })
299
306
  layouts.push({
300
- length: contenteight,
307
+ length: contentHeight,
301
308
  offset,
302
309
  index: layouts.length
303
310
  })
304
- offset += contenteight
311
+ offset += contentHeight
305
312
  })
306
313
 
307
314
  // 添加该 section 尾部位置信息
@@ -335,22 +342,35 @@ const _SectionList = forwardRef<any, SectionListProps>((props = {}, ref) => {
335
342
  layoutProps
336
343
  )
337
344
 
345
+ const nativeGesture = useMemo(() => {
346
+ const simultaneousHandlers = flatGesture(originSimultaneousHandlers)
347
+ const waitForHandlers = flatGesture(waitFor)
348
+ const gesture = Gesture.Native().withRef(sectionListGestureRef as any)
349
+ if (simultaneousHandlers && simultaneousHandlers.length) {
350
+ gesture.simultaneousWithExternalGesture(...simultaneousHandlers)
351
+ }
352
+ if (waitForHandlers && waitForHandlers.length) {
353
+ gesture.requireExternalGestureToFail(...waitForHandlers)
354
+ }
355
+ return gesture
356
+ }, [originSimultaneousHandlers, waitFor])
357
+
338
358
  if (enhanced) {
339
- Object.assign(scrollAdditionalProps, {
359
+ extendObject(scrollAdditionalProps, {
340
360
  bounces
341
361
  })
342
362
  }
343
363
  if (refresherEnabled) {
344
- Object.assign(scrollAdditionalProps, {
364
+ extendObject(scrollAdditionalProps, {
345
365
  refreshing: refreshing
346
366
  })
347
367
  }
348
368
 
349
369
  useImperativeHandle(ref, () => {
350
- return {
351
- ...props,
370
+ return extendObject({}, props, {
371
+ gestureRef: sectionListGestureRef,
352
372
  scrollToIndex
353
- }
373
+ })
354
374
  })
355
375
 
356
376
  const innerProps = useInnerProps(extendObject({}, props, scrollAdditionalProps), [
@@ -359,7 +379,9 @@ const _SectionList = forwardRef<any, SectionListProps>((props = {}, ref) => {
359
379
  'lower-threshold',
360
380
  'refresher-triggered',
361
381
  'refresher-enabled',
362
- 'bindrefresherrefresh'
382
+ 'bindrefresherrefresh',
383
+ 'simultaneous-handlers',
384
+ 'wait-for'
363
385
  ], { layoutRef })
364
386
 
365
387
  // 使用 ref 保存最新的数据,避免数据变化时组件销毁重建
@@ -412,24 +434,28 @@ const _SectionList = forwardRef<any, SectionListProps>((props = {}, ref) => {
412
434
  )
413
435
 
414
436
  return createElement(
415
- SectionList,
416
- extendObject(
417
- {
418
- style: [{ height, width }, style, layoutStyle],
419
- sections: convertedListData,
420
- renderItem: renderItem,
421
- getItemLayout: getItemLayout,
422
- ListHeaderComponent: useListHeader ? ListHeaderComponent : null,
423
- ListFooterComponent: useListFooter ? ListFooterComponent : null,
424
- renderSectionHeader: renderSectionHeader,
425
- refreshControl: refresherEnabled
426
- ? React.createElement(RefreshControl, {
427
- onRefresh: onRefresh,
428
- refreshing: refreshing
429
- })
430
- : undefined
431
- },
432
- innerProps
437
+ GestureDetector,
438
+ { gesture: nativeGesture },
439
+ createElement(
440
+ SectionList,
441
+ extendObject(
442
+ {
443
+ style: [{ height, width }, style, layoutStyle],
444
+ sections: convertedListData,
445
+ renderItem: renderItem,
446
+ getItemLayout: getItemLayout,
447
+ ListHeaderComponent: useListHeader ? ListHeaderComponent : null,
448
+ ListFooterComponent: useListFooter ? ListFooterComponent : null,
449
+ renderSectionHeader: renderSectionHeader,
450
+ refreshControl: refresherEnabled
451
+ ? createElement(RefreshControl, {
452
+ onRefresh: onRefresh,
453
+ refreshing: refreshing
454
+ })
455
+ : undefined
456
+ },
457
+ innerProps
458
+ )
433
459
  )
434
460
  )
435
461
  })
@@ -140,7 +140,7 @@ export default {
140
140
  },
141
141
  useListHeader: {
142
142
  type: Boolean,
143
- default: true
143
+ default: false
144
144
  },
145
145
  listFooterData: {
146
146
  type: Object,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/webpack-plugin",
3
- "version": "2.10.18-beta.11",
3
+ "version": "2.10.18-beta.13",
4
4
  "description": "mpx compile core",
5
5
  "keywords": [
6
6
  "mpx"
@@ -1,23 +0,0 @@
1
- const TAG_NAME = 'sticky-header'
2
-
3
- module.exports = function ({ print }) {
4
- return {
5
- test: TAG_NAME,
6
- android (tag, { el }) {
7
- el.isBuiltIn = true
8
- return 'mpx-sticky-header'
9
- },
10
- ios (tag, { el }) {
11
- el.isBuiltIn = true
12
- return 'mpx-sticky-header'
13
- },
14
- harmony (tag, { el }) {
15
- el.isBuiltIn = true
16
- return 'mpx-sticky-header'
17
- },
18
- web (tag, { el }) {
19
- el.isBuiltIn = true
20
- return 'mpx-sticky-header'
21
- }
22
- }
23
- }
@@ -1,23 +0,0 @@
1
- const TAG_NAME = 'sticky-section'
2
-
3
- module.exports = function ({ print }) {
4
- return {
5
- test: TAG_NAME,
6
- android (tag, { el }) {
7
- el.isBuiltIn = true
8
- return 'mpx-sticky-section'
9
- },
10
- ios (tag, { el }) {
11
- el.isBuiltIn = true
12
- return 'mpx-sticky-section'
13
- },
14
- harmony (tag, { el }) {
15
- el.isBuiltIn = true
16
- return 'mpx-sticky-section'
17
- },
18
- web (tag, { el }) {
19
- el.isBuiltIn = true
20
- return 'mpx-sticky-section'
21
- }
22
- }
23
- }