@mpxjs/webpack-plugin 2.9.70 → 2.9.71

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 (60) hide show
  1. package/lib/platform/template/wx/component-config/movable-view.js +8 -1
  2. package/lib/platform/template/wx/component-config/picker-view.js +1 -5
  3. package/lib/platform/template/wx/component-config/scroll-view.js +1 -1
  4. package/lib/platform/template/wx/index.js +0 -4
  5. package/lib/runtime/components/react/context.ts +8 -0
  6. package/lib/runtime/components/react/dist/context.js +2 -0
  7. package/lib/runtime/components/react/dist/getInnerListeners.js +34 -31
  8. package/lib/runtime/components/react/dist/mpx-button.jsx +16 -44
  9. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +93 -58
  10. package/lib/runtime/components/react/dist/mpx-navigator.jsx +1 -1
  11. package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +35 -0
  12. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +151 -127
  13. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +38 -34
  14. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -11
  15. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +11 -4
  16. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +31 -8
  17. package/lib/runtime/components/react/dist/mpx-swiper.jsx +670 -0
  18. package/lib/runtime/components/react/dist/mpx-view.jsx +15 -53
  19. package/lib/runtime/components/react/dist/pickerFaces.js +7 -6
  20. package/lib/runtime/components/react/dist/pickerVIewContext.js +14 -0
  21. package/lib/runtime/components/react/dist/pickerViewIndicator.jsx +23 -0
  22. package/lib/runtime/components/react/dist/pickerViewMask.jsx +18 -0
  23. package/lib/runtime/components/react/dist/useAnimationHooks.js +20 -2
  24. package/lib/runtime/components/react/dist/utils.jsx +74 -11
  25. package/lib/runtime/components/react/getInnerListeners.ts +43 -32
  26. package/lib/runtime/components/react/mpx-button.tsx +20 -57
  27. package/lib/runtime/components/react/mpx-movable-view.tsx +119 -74
  28. package/lib/runtime/components/react/mpx-navigator.tsx +1 -1
  29. package/lib/runtime/components/react/mpx-picker-view-column-item.tsx +76 -0
  30. package/lib/runtime/components/react/mpx-picker-view-column.tsx +206 -183
  31. package/lib/runtime/components/react/mpx-picker-view.tsx +49 -48
  32. package/lib/runtime/components/react/mpx-rich-text/index.tsx +12 -18
  33. package/lib/runtime/components/react/mpx-scroll-view.tsx +21 -10
  34. package/lib/runtime/components/react/mpx-swiper-item.tsx +45 -11
  35. package/lib/runtime/components/react/mpx-swiper.tsx +742 -0
  36. package/lib/runtime/components/react/mpx-view.tsx +18 -65
  37. package/lib/runtime/components/react/pickerFaces.ts +10 -7
  38. package/lib/runtime/components/react/pickerVIewContext.ts +27 -0
  39. package/lib/runtime/components/react/pickerViewIndicator.tsx +34 -0
  40. package/lib/runtime/components/react/pickerViewMask.tsx +30 -0
  41. package/lib/runtime/components/react/types/{getInnerListeners.ts → getInnerListeners.d.ts} +4 -5
  42. package/lib/runtime/components/react/types/global.d.ts +10 -0
  43. package/lib/runtime/components/react/useAnimationHooks.ts +24 -3
  44. package/lib/runtime/components/react/utils.tsx +85 -12
  45. package/lib/runtime/components/web/mpx-checkbox.vue +1 -1
  46. package/lib/runtime/components/web/mpx-picker-view-column.vue +9 -4
  47. package/lib/template-compiler/compiler.js +61 -13
  48. package/lib/wxss/loader.js +15 -2
  49. package/package.json +3 -3
  50. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +0 -480
  51. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +0 -68
  52. package/lib/runtime/components/react/dist/mpx-swiper/type.js +0 -1
  53. package/lib/runtime/components/react/dist/pickerOverlay.jsx +0 -21
  54. package/lib/runtime/components/react/dist/types/common.js +0 -1
  55. package/lib/runtime/components/react/dist/types/getInnerListeners.js +0 -1
  56. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +0 -527
  57. package/lib/runtime/components/react/mpx-swiper/index.tsx +0 -80
  58. package/lib/runtime/components/react/mpx-swiper/type.ts +0 -87
  59. package/lib/runtime/components/react/pickerOverlay.tsx +0 -32
  60. /package/lib/runtime/components/react/types/{common.ts → common.d.ts} +0 -0
@@ -0,0 +1,35 @@
1
+ import React, { useEffect } from 'react';
2
+ import Reanimated, { Extrapolation, interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated';
3
+ import { extendObject } from './utils';
4
+ import { createFaces } from './pickerFaces';
5
+ import { usePickerViewColumnAnimationContext, usePickerViewStyleContext } from './pickerVIewContext';
6
+ const PickerViewColumnItem = ({ item, index, itemHeight, itemWidth = '100%', textStyle, textProps, visibleCount, onItemLayout }) => {
7
+ const textStyleFromAncestor = usePickerViewStyleContext();
8
+ const offsetYShared = usePickerViewColumnAnimationContext();
9
+ const facesShared = useSharedValue(createFaces(itemHeight, visibleCount));
10
+ useEffect(() => {
11
+ facesShared.value = createFaces(itemHeight, visibleCount);
12
+ }, [itemHeight]);
13
+ const animatedStyles = useAnimatedStyle(() => {
14
+ const inputRange = facesShared.value.map((f) => itemHeight * (index + f.index));
15
+ return {
16
+ opacity: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.opacity), Extrapolation.CLAMP),
17
+ transform: [
18
+ { translateY: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.offsetY), Extrapolation.EXTEND) },
19
+ { rotateX: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.deg), Extrapolation.CLAMP) + 'deg' },
20
+ { scale: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.scale), Extrapolation.EXTEND) }
21
+ ]
22
+ };
23
+ });
24
+ const strKey = `picker-column-item-${index}`;
25
+ const restProps = index === 0 ? { onLayout: onItemLayout } : {};
26
+ const itemProps = extendObject({
27
+ style: extendObject({ height: itemHeight, width: '100%' }, textStyleFromAncestor, textStyle, item.props.style)
28
+ }, textProps, restProps);
29
+ const realItem = React.cloneElement(item, itemProps);
30
+ return (<Reanimated.View key={strKey} style={[{ height: itemHeight, width: itemWidth }, animatedStyles]}>
31
+ {realItem}
32
+ </Reanimated.View>);
33
+ };
34
+ PickerViewColumnItem.displayName = 'MpxPickerViewColumnItem';
35
+ export default PickerViewColumnItem;
@@ -1,42 +1,68 @@
1
- import { Animated, SafeAreaView } from 'react-native';
2
- import React, { forwardRef, useRef, useState, useMemo, useCallback, useEffect } from 'react';
3
- import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, usePrevious } from './utils';
1
+ import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react';
2
+ import { StyleSheet, View } from 'react-native';
3
+ import Reanimated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
4
+ import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS } from './utils';
4
5
  import useNodesRef from './useNodesRef';
5
- import { createFaces } from './pickerFaces';
6
- import PickerOverlay from './pickerOverlay';
7
- // 默认的单个选项高度
8
- const DefaultPickerItemH = 36;
9
- // 默认一屏可见选项个数
6
+ import PickerIndicator from './pickerViewIndicator';
7
+ import PickerMask from './pickerViewMask';
8
+ import MpxPickerVIewColumnItem from './mpx-picker-view-column-item';
9
+ import { PickerViewColumnAnimationContext } from './pickerVIewContext';
10
10
  const visibleCount = 5;
11
11
  const _PickerViewColumn = forwardRef((props, ref) => {
12
- const { columnData, columnIndex, initialIndex, onSelectChange, onColumnItemRawHChange, getInnerLayout, style, wrapperStyle, pickerOverlayStyle, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
13
- const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
14
- const { textStyle } = splitStyle(normalStyle);
15
- const { textProps } = splitProps(props);
16
- const scrollViewRef = useRef(null);
12
+ const { columnData, columnIndex, initialIndex, onSelectChange, style, wrapperStyle, pickerMaskStyle, pickerIndicatorStyle, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
13
+ const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
14
+ const { textStyle = {} } = splitStyle(normalStyle);
15
+ const { textProps = {} } = splitProps(props);
16
+ const scrollViewRef = useAnimatedRef();
17
+ const offsetYShared = useScrollViewOffset(scrollViewRef);
17
18
  useNodesRef(props, ref, scrollViewRef, {
18
19
  style: normalStyle
19
20
  });
20
- const { height: pickerH, itemHeight = DefaultPickerItemH } = wrapperStyle;
21
- const [itemRawH, setItemRawH] = useState(0); // 单个选项真实渲染高度
21
+ const { height: pickerH, itemHeight } = wrapperStyle;
22
+ const [itemRawH, setItemRawH] = useState(itemHeight);
22
23
  const maxIndex = useMemo(() => columnData.length - 1, [columnData]);
24
+ const prevScrollingInfo = useRef({ index: initialIndex, y: 0 });
23
25
  const touching = useRef(false);
24
26
  const scrolling = useRef(false);
27
+ const timerResetPosition = useRef(null);
28
+ const timerScrollTo = useRef(null);
25
29
  const activeIndex = useRef(initialIndex);
26
30
  const prevIndex = usePrevious(initialIndex);
27
31
  const prevMaxIndex = usePrevious(maxIndex);
28
- const initialOffset = useMemo(() => ({
29
- x: 0,
30
- y: itemRawH * initialIndex
31
- }), [itemRawH]);
32
- const snapToOffsets = useMemo(() => columnData.map((_, i) => i * itemRawH), [columnData, itemRawH]);
32
+ const { layoutProps } = useLayout({
33
+ props,
34
+ hasSelfPercent,
35
+ setWidth,
36
+ setHeight,
37
+ nodeRef: scrollViewRef
38
+ });
39
+ const paddingHeight = useMemo(() => Math.round((pickerH - itemHeight) / 2), [pickerH, itemHeight]);
40
+ const snapToOffsets = useMemo(() => Array.from({ length: maxIndex + 1 }, (_, i) => i * itemRawH), [maxIndex, itemRawH]);
33
41
  const contentContainerStyle = useMemo(() => {
34
- return [
35
- {
36
- paddingVertical: Math.round(pickerH - itemRawH) / 2
37
- }
38
- ];
39
- }, [pickerH, itemRawH]);
42
+ return [{ paddingVertical: paddingHeight }];
43
+ }, [paddingHeight]);
44
+ const getIndex = useCallback((y) => {
45
+ const calc = Math.round(y / itemRawH);
46
+ return Math.max(0, Math.min(calc, maxIndex));
47
+ }, [itemRawH, maxIndex]);
48
+ const clearTimerResetPosition = useCallback(() => {
49
+ if (timerResetPosition.current) {
50
+ clearTimeout(timerResetPosition.current);
51
+ timerResetPosition.current = null;
52
+ }
53
+ }, []);
54
+ const clearTimerScrollTo = useCallback(() => {
55
+ if (timerScrollTo.current) {
56
+ clearTimeout(timerScrollTo.current);
57
+ timerScrollTo.current = null;
58
+ }
59
+ }, []);
60
+ useEffect(() => {
61
+ return () => {
62
+ clearTimerResetPosition();
63
+ clearTimerScrollTo();
64
+ };
65
+ }, []);
40
66
  useEffect(() => {
41
67
  if (!scrollViewRef.current ||
42
68
  !itemRawH ||
@@ -48,122 +74,120 @@ const _PickerViewColumn = forwardRef((props, ref) => {
48
74
  maxIndex !== prevMaxIndex) {
49
75
  return;
50
76
  }
77
+ clearTimerScrollTo();
78
+ timerScrollTo.current = setTimeout(() => {
79
+ scrollViewRef.current?.scrollTo({
80
+ x: 0,
81
+ y: initialIndex * itemRawH,
82
+ animated: false
83
+ });
84
+ }, isAndroid ? 200 : 0);
51
85
  activeIndex.current = initialIndex;
52
- scrollViewRef.current.scrollTo({
53
- x: 0,
54
- y: itemRawH * initialIndex,
55
- animated: false
56
- });
86
+ }, [itemRawH, maxIndex, initialIndex]);
87
+ const onContentSizeChange = useCallback((_w, h) => {
88
+ const y = initialIndex * itemRawH;
89
+ if (y <= h) {
90
+ clearTimerScrollTo();
91
+ timerScrollTo.current = setTimeout(() => {
92
+ scrollViewRef.current?.scrollTo({ x: 0, y, animated: false });
93
+ }, 0);
94
+ }
57
95
  }, [itemRawH, initialIndex]);
58
- const onScrollViewLayout = () => {
59
- getInnerLayout && getInnerLayout(layoutRef);
60
- };
61
- const { layoutRef, layoutProps } = useLayout({
62
- props,
63
- hasSelfPercent,
64
- setWidth,
65
- setHeight,
66
- nodeRef: scrollViewRef,
67
- onLayout: onScrollViewLayout
68
- });
69
- const onContentSizeChange = (w, h) => {
70
- scrollViewRef.current?.scrollTo({
71
- x: 0,
72
- y: itemRawH * initialIndex,
73
- animated: false
74
- });
75
- };
76
- const onItemLayout = (e) => {
96
+ const onItemLayout = useCallback((e) => {
77
97
  const { height: rawH } = e.nativeEvent.layout;
78
- if (rawH && itemRawH !== rawH) {
79
- setItemRawH(rawH);
80
- onColumnItemRawHChange(rawH);
98
+ const roundedH = Math.round(rawH);
99
+ if (roundedH && roundedH !== itemRawH) {
100
+ setItemRawH(roundedH);
81
101
  }
82
- };
83
- const onTouchStart = () => {
84
- touching.current = true;
85
- };
86
- const onTouchEnd = () => {
87
- touching.current = false;
88
- };
89
- const onTouchCancel = () => {
90
- touching.current = false;
91
- };
92
- const onMomentumScrollBegin = () => {
93
- scrolling.current = true;
94
- };
95
- const onMomentumScrollEnd = (e) => {
96
- scrolling.current = false;
97
- if (!itemRawH) {
102
+ }, [itemRawH]);
103
+ const resetScrollPosition = useCallback((y) => {
104
+ if (touching.current || scrolling.current) {
98
105
  return;
99
106
  }
107
+ scrolling.current = true;
108
+ const targetIndex = getIndex(y);
109
+ scrollViewRef.current?.scrollTo({ x: 0, y: targetIndex * itemRawH, animated: false });
110
+ }, [itemRawH, getIndex]);
111
+ const onMomentumScrollBegin = useCallback(() => {
112
+ isIOS && clearTimerResetPosition();
113
+ scrolling.current = true;
114
+ }, []);
115
+ const onMomentumScrollEnd = useCallback((e) => {
116
+ scrolling.current = false;
100
117
  const { y: scrollY } = e.nativeEvent.contentOffset;
101
- let calcIndex = Math.round(scrollY / itemRawH);
102
- activeIndex.current = calcIndex;
103
- if (calcIndex !== initialIndex) {
104
- calcIndex = Math.max(0, Math.min(calcIndex, maxIndex)) || 0;
118
+ if (isIOS && scrollY % itemRawH !== 0) {
119
+ return resetScrollPosition(scrollY);
120
+ }
121
+ const calcIndex = getIndex(scrollY);
122
+ if (calcIndex !== activeIndex.current) {
123
+ activeIndex.current = calcIndex;
105
124
  onSelectChange(calcIndex);
106
125
  }
107
- };
108
- const offsetY = useRef(new Animated.Value(0)).current;
109
- const onScroll = useMemo(() => Animated.event([{ nativeEvent: { contentOffset: { y: offsetY } } }], {
110
- useNativeDriver: true
111
- }), [offsetY]);
112
- const faces = useMemo(() => createFaces(itemRawH, visibleCount), [itemRawH]);
113
- const getTransform = useCallback((index) => {
114
- const inputRange = faces.map((f) => itemRawH * (index + f.index));
115
- return {
116
- opacity: offsetY.interpolate({
117
- inputRange: inputRange,
118
- outputRange: faces.map((x) => x.opacity),
119
- extrapolate: 'clamp'
120
- }),
121
- rotateX: offsetY.interpolate({
122
- inputRange: inputRange,
123
- outputRange: faces.map((x) => `${x.deg}deg`),
124
- extrapolate: 'extend'
125
- }),
126
- translateY: offsetY.interpolate({
127
- inputRange: inputRange,
128
- outputRange: faces.map((x) => x.offsetY),
129
- extrapolate: 'extend'
130
- })
126
+ }, [itemRawH, getIndex, onSelectChange, resetScrollPosition]);
127
+ const onScrollBeginDrag = useCallback(() => {
128
+ isIOS && clearTimerResetPosition();
129
+ touching.current = true;
130
+ prevScrollingInfo.current = {
131
+ index: activeIndex.current,
132
+ y: activeIndex.current * itemRawH
131
133
  };
132
- }, [offsetY, faces, itemRawH]);
133
- const renderInnerchild = () => columnData.map((item, index) => {
134
- const InnerProps = index === 0 ? { onLayout: onItemLayout } : {};
135
- const strKey = `picker-column-${columnIndex}-${index}`;
136
- const { opacity, rotateX, translateY } = getTransform(index);
137
- return (<Animated.View key={strKey} {...InnerProps} style={[
138
- {
139
- height: itemHeight || DefaultPickerItemH,
140
- width: '100%',
141
- opacity,
142
- transform: [
143
- { translateY },
144
- { rotateX },
145
- { perspective: 1000 } // 适配 Android
146
- ]
134
+ }, [itemRawH]);
135
+ const onScrollEndDrag = useCallback((e) => {
136
+ touching.current = false;
137
+ if (isIOS) {
138
+ const { y } = e.nativeEvent.contentOffset;
139
+ if (y % itemRawH === 0) {
140
+ onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } });
141
+ }
142
+ else if (y > 0 && y < snapToOffsets[maxIndex]) {
143
+ timerResetPosition.current = setTimeout(() => {
144
+ resetScrollPosition(y);
145
+ }, 10);
146
+ }
147
+ }
148
+ }, [itemRawH, maxIndex, snapToOffsets, onMomentumScrollEnd, resetScrollPosition]);
149
+ const onScroll = useCallback((e) => {
150
+ // 全局注册的振动触感 hook
151
+ const pickerVibrate = global.__mpx?.config?.rnConfig?.pickerVibrate;
152
+ if (typeof pickerVibrate !== 'function') {
153
+ return;
154
+ }
155
+ const { y } = e.nativeEvent.contentOffset;
156
+ const { index: prevIndex, y: _y } = prevScrollingInfo.current;
157
+ if (touching.current || scrolling.current) {
158
+ if (Math.abs(y - _y) >= itemRawH) {
159
+ const currentId = getIndex(y);
160
+ if (currentId !== prevIndex) {
161
+ prevScrollingInfo.current = {
162
+ index: currentId,
163
+ y: currentId * itemRawH
164
+ };
165
+ // vibrateShort({ type: 'selection' })
166
+ pickerVibrate();
147
167
  }
148
- ]}>
149
- {wrapChildren({ children: item }, {
150
- hasVarDec,
151
- varContext: varContextRef.current,
152
- textStyle,
153
- textProps
154
- })}
155
- </Animated.View>);
168
+ }
169
+ }
170
+ }, [itemRawH, getIndex]);
171
+ const renderInnerchild = () => columnData.map((item, index) => {
172
+ return (<MpxPickerVIewColumnItem key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout}/>);
156
173
  });
157
174
  const renderScollView = () => {
158
- return (<Animated.ScrollView ref={scrollViewRef} bounces={true} horizontal={false} pagingEnabled={false} nestedScrollEnabled={true} removeClippedSubviews={true} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} {...layoutProps} scrollEventThrottle={16} contentContainerStyle={contentContainerStyle} contentOffset={initialOffset} snapToOffsets={snapToOffsets} onContentSizeChange={onContentSizeChange} onScroll={onScroll} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd} onTouchCancel={onTouchCancel} onMomentumScrollBegin={onMomentumScrollBegin} onMomentumScrollEnd={onMomentumScrollEnd}>
159
- {renderInnerchild()}
160
- </Animated.ScrollView>);
175
+ return (<PickerViewColumnAnimationContext.Provider value={offsetYShared}>
176
+ <Reanimated.ScrollView ref={scrollViewRef} bounces={true} horizontal={false} nestedScrollEnabled={true} removeClippedSubviews={false} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} scrollEventThrottle={16} {...layoutProps} style={[{ width: '100%' }]} decelerationRate="fast" snapToOffsets={snapToOffsets} onScroll={onScroll} onScrollBeginDrag={onScrollBeginDrag} onScrollEndDrag={onScrollEndDrag} onMomentumScrollBegin={onMomentumScrollBegin} onMomentumScrollEnd={onMomentumScrollEnd} onContentSizeChange={onContentSizeChange} contentContainerStyle={contentContainerStyle}>
177
+ {renderInnerchild()}
178
+ </Reanimated.ScrollView>
179
+ </PickerViewColumnAnimationContext.Provider>);
161
180
  };
162
- const renderOverlay = () => (<PickerOverlay itemHeight={itemHeight} overlayItemStyle={pickerOverlayStyle}/>);
163
- return (<SafeAreaView style={[{ display: 'flex', flex: 1 }]}>
181
+ const renderIndicator = () => (<PickerIndicator itemHeight={itemHeight} indicatorItemStyle={pickerIndicatorStyle}/>);
182
+ const renderMask = () => (<PickerMask itemHeight={itemHeight} maskContainerStyle={pickerMaskStyle}/>);
183
+ return (<View style={[styles.wrapper, normalStyle]}>
164
184
  {renderScollView()}
165
- {renderOverlay()}
166
- </SafeAreaView>);
185
+ {renderMask()}
186
+ {renderIndicator()}
187
+ </View>);
188
+ });
189
+ const styles = StyleSheet.create({
190
+ wrapper: { display: 'flex', flex: 1 }
167
191
  });
168
192
  _PickerViewColumn.displayName = 'MpxPickerViewColumn';
169
193
  export default _PickerViewColumn;
@@ -1,8 +1,9 @@
1
1
  import { View } from 'react-native';
2
- import React, { forwardRef, useState, useRef } from 'react';
2
+ import React, { forwardRef, useRef } from 'react';
3
3
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
4
4
  import useNodesRef from './useNodesRef';
5
- import { useLayout, splitProps, splitStyle, wrapChildren, parseInlineStyle, useTransformStyle, useDebounceCallback, useStableCallback, extendObject } from './utils';
5
+ import { useLayout, splitProps, splitStyle, wrapChildren, useTransformStyle, extendObject } from './utils';
6
+ import { PickerViewStyleContext } from './pickerVIewContext';
6
7
  const styles = {
7
8
  wrapper: {
8
9
  display: 'flex',
@@ -13,42 +14,38 @@ const styles = {
13
14
  alignItems: 'center'
14
15
  }
15
16
  };
17
+ const DefaultPickerItemH = 36;
16
18
  const _PickerView = forwardRef((props, ref) => {
17
- const { children, value = [], bindchange, style, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
18
- // indicatorStyle 需要转换为rn的style
19
- // 微信设置到pick-view上上设置的normalStyle如border等需要转换成RN的style然后进行透传
20
- const indicatorStyle = parseInlineStyle(props['indicator-style']);
21
- const { height: indicatorH, ...pickerOverlayStyle } = indicatorStyle;
22
- const [pickMaxH, setPickMaxH] = useState(0);
19
+ const { children, value = [], bindchange, style, 'indicator-style': indicatorStyle = {}, 'mask-style': pickerMaskStyle = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
20
+ const { height: indicatorH, ...pickerIndicatorStyle } = indicatorStyle;
23
21
  const nodeRef = useRef(null);
24
22
  const cloneRef = useRef(null);
25
23
  const activeValueRef = useRef(value);
26
24
  activeValueRef.current = value.slice();
25
+ const snapActiveValueRef = useRef(null);
27
26
  const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
28
27
  useNodesRef(props, ref, nodeRef, {
29
28
  style: normalStyle
30
29
  });
31
- const {
32
- // 存储layout布局信息
33
- layoutRef, layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: nodeRef });
30
+ const { layoutRef, layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: nodeRef });
34
31
  const { textProps } = splitProps(props);
35
32
  const { textStyle } = splitStyle(normalStyle);
36
- const onColumnItemRawHChange = (height) => {
37
- if (height > pickMaxH) {
38
- setPickMaxH(height);
39
- }
40
- };
41
- const bindchangeDebounce = useDebounceCallback(useStableCallback(bindchange), 300);
42
33
  const onSelectChange = (columnIndex, selectedIndex) => {
43
- bindchangeDebounce.clear();
44
34
  const activeValue = activeValueRef.current;
45
35
  activeValue[columnIndex] = selectedIndex;
46
- const eventData = getCustomEvent('change', {}, { detail: { value: activeValue, source: 'change' }, layoutRef });
47
- bindchangeDebounce(eventData);
36
+ const eventData = getCustomEvent('change', {}, { detail: { value: activeValue.slice(), source: 'change' }, layoutRef });
37
+ bindchange?.(eventData);
38
+ snapActiveValueRef.current = activeValueRef.current;
48
39
  };
49
- const onInitialChange = (value) => {
50
- const eventData = getCustomEvent('change', {}, { detail: { value, source: 'change' }, layoutRef });
51
- bindchange?.(eventData); // immediate
40
+ const hasDiff = (a = [], b) => {
41
+ return a.some((v, i) => v !== b[i]);
42
+ };
43
+ const onInitialChange = (isInvalid, value) => {
44
+ if (isInvalid || !snapActiveValueRef.current || hasDiff(snapActiveValueRef.current, value)) {
45
+ const eventData = getCustomEvent('change', {}, { detail: { value: value.slice(), source: 'change' }, layoutRef });
46
+ bindchange?.(eventData);
47
+ snapActiveValueRef.current = value.slice();
48
+ }
52
49
  };
53
50
  const innerProps = useInnerProps(props, extendObject({
54
51
  ref: nodeRef,
@@ -57,9 +54,14 @@ const _PickerView = forwardRef((props, ref) => {
57
54
  overflow: 'hidden'
58
55
  }),
59
56
  layoutProps
60
- }), ['enable-offset'], { layoutRef });
57
+ }), [
58
+ 'enable-offset',
59
+ 'indicator-style',
60
+ 'indicator-class',
61
+ 'mask-style',
62
+ 'mask-class'
63
+ ], { layoutRef });
61
64
  const renderColumn = (child, index, columnData, initialIndex) => {
62
- const extraProps = {};
63
65
  const childProps = child?.props || {};
64
66
  const wrappedProps = extendObject({}, childProps, {
65
67
  columnData,
@@ -67,14 +69,14 @@ const _PickerView = forwardRef((props, ref) => {
67
69
  columnIndex: index,
68
70
  key: `pick-view-${index}`,
69
71
  wrapperStyle: {
70
- height: normalStyle?.height || 0,
71
- itemHeight: indicatorH || 0
72
+ height: normalStyle?.height || DefaultPickerItemH,
73
+ itemHeight: indicatorH || DefaultPickerItemH
72
74
  },
73
- onColumnItemRawHChange,
74
75
  onSelectChange: onSelectChange.bind(null, index),
75
76
  initialIndex,
76
- pickerOverlayStyle
77
- }, extraProps);
77
+ pickerIndicatorStyle,
78
+ pickerMaskStyle
79
+ });
78
80
  const realElement = React.cloneElement(child, wrappedProps);
79
81
  return wrapChildren({
80
82
  children: realElement
@@ -110,12 +112,14 @@ const _PickerView = forwardRef((props, ref) => {
110
112
  validValue.push(validIndex);
111
113
  renderColumns.push(renderColumn(item, index, columnData, validIndex));
112
114
  });
113
- isInvalid && onInitialChange(validValue);
115
+ onInitialChange(isInvalid, validValue);
114
116
  return renderColumns;
115
117
  };
116
- return (<View {...innerProps}>
117
- <View style={[styles.wrapper]}>{renderPickerColumns()}</View>
118
- </View>);
118
+ return (<PickerViewStyleContext.Provider value={textStyle}>
119
+ <View {...innerProps}>
120
+ <View style={[styles.wrapper]}>{renderPickerColumns()}</View>
121
+ </View>
122
+ </PickerViewStyleContext.Provider>);
119
123
  });
120
124
  _PickerView.displayName = 'MpxPickerView';
121
125
  export default _PickerView;
@@ -2,10 +2,10 @@
2
2
  * ✔ nodes
3
3
  */
4
4
  import { View } from 'react-native';
5
- import { useRef, forwardRef, useState } from 'react';
5
+ import { useRef, forwardRef, useState, createElement } from 'react';
6
6
  import useInnerProps from '../getInnerListeners';
7
7
  import useNodesRef from '../useNodesRef'; // 引入辅助函数
8
- import { useTransformStyle, useLayout } from '../utils';
8
+ import { useTransformStyle, useLayout, extendObject } from '../utils';
9
9
  import { WebView } from 'react-native-webview';
10
10
  import { generateHTML } from './html';
11
11
  function jsonToHtmlStr(elements) {
@@ -44,20 +44,19 @@ const _RichText = forwardRef((props, ref) => {
44
44
  useNodesRef(props, ref, nodeRef, {
45
45
  layoutRef
46
46
  });
47
- const innerProps = useInnerProps(props, {
47
+ const innerProps = useInnerProps(props, extendObject({
48
48
  ref: nodeRef,
49
- style: { ...normalStyle, ...layoutStyle },
50
- ...layoutProps
51
- }, [], {
49
+ style: extendObject(normalStyle, layoutStyle)
50
+ }, layoutProps), [], {
52
51
  layoutRef
53
52
  });
54
53
  const html = typeof nodes === 'string' ? nodes : jsonToHtmlStr(nodes);
55
- return (<View {...innerProps}>
56
- <WebView source={{ html: generateHTML(html) }} onMessage={(event) => {
54
+ return createElement(View, innerProps, createElement(WebView, {
55
+ source: { html: generateHTML(html) },
56
+ onMessage: (event) => {
57
57
  setWebViewHeight(+event.nativeEvent.data);
58
- }}>
59
- </WebView>
60
- </View>);
58
+ }
59
+ }));
61
60
  });
62
61
  _RichText.displayName = 'mpx-rich-text';
63
62
  export default _RichText;
@@ -33,13 +33,13 @@
33
33
  */
34
34
  import { ScrollView } from 'react-native-gesture-handler';
35
35
  import { RefreshControl } from 'react-native';
36
- import { useRef, useState, useEffect, forwardRef, useContext, createElement } from 'react';
36
+ import { useRef, useState, useEffect, forwardRef, useContext, createElement, useMemo } from 'react';
37
37
  import { useAnimatedRef } from 'react-native-reanimated';
38
38
  import { warn } from '@mpxjs/utils';
39
39
  import useInnerProps, { getCustomEvent } from './getInnerListeners';
40
40
  import useNodesRef from './useNodesRef';
41
41
  import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture } from './utils';
42
- import { IntersectionObserverContext } from './context';
42
+ import { IntersectionObserverContext, ScrollViewContext } from './context';
43
43
  const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
44
44
  const { textProps, innerProps: props = {} } = splitProps(scrollViewProps);
45
45
  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, '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;
@@ -78,6 +78,11 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
78
78
  },
79
79
  gestureRef: scrollViewRef
80
80
  });
81
+ const contextValue = useMemo(() => {
82
+ return {
83
+ gestureRef: scrollViewRef
84
+ };
85
+ }, []);
81
86
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
82
87
  if (scrollX && scrollY) {
83
88
  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');
@@ -196,6 +201,8 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
196
201
  layoutRef
197
202
  }, props));
198
203
  updateScrollOptions(e, { scrollLeft, scrollTop });
204
+ onStartReached(e);
205
+ onEndReached(e);
199
206
  if (enableTriggerIntersectionObserver && intersectionObservers) {
200
207
  for (const key in intersectionObservers) {
201
208
  intersectionObservers[key].throttleMeasure();
@@ -342,12 +349,12 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
342
349
  onRefresh: onRefresh
343
350
  }, (refresherDefaultStyle && refresherDefaultStyle !== 'none' ? { colors: refreshColor[refresherDefaultStyle] } : null)))
344
351
  : undefined
345
- }), wrapChildren(props, {
352
+ }), createElement(ScrollViewContext.Provider, { value: contextValue }, wrapChildren(props, {
346
353
  hasVarDec,
347
354
  varContext: varContextRef.current,
348
355
  textStyle,
349
356
  textProps
350
- }));
357
+ })));
351
358
  });
352
359
  _ScrollView.displayName = 'MpxScrollView';
353
360
  export default _ScrollView;
@@ -1,10 +1,16 @@
1
- import { View } from 'react-native';
2
- import { forwardRef, useRef } from 'react';
1
+ import Animated, { useAnimatedStyle, interpolate } from 'react-native-reanimated';
2
+ import { forwardRef, useRef, useContext } from 'react';
3
3
  import useInnerProps from './getInnerListeners';
4
4
  import useNodesRef from './useNodesRef'; // 引入辅助函数
5
5
  import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout } from './utils';
6
+ import { SwiperContext } from './context';
6
7
  const _SwiperItem = forwardRef((props, ref) => {
7
- const { 'enable-var': enableVar, 'external-var-context': externalVarContext, style } = props;
8
+ const { 'enable-var': enableVar, 'external-var-context': externalVarContext, style, customStyle, itemIndex } = props;
9
+ const contextValue = useContext(SwiperContext);
10
+ const offset = contextValue.offset || 0;
11
+ const step = contextValue.step || 0;
12
+ const scale = contextValue.scale || false;
13
+ const dir = contextValue.dir || 'x';
8
14
  const { textProps } = splitProps(props);
9
15
  const nodeRef = useRef(null);
10
16
  const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
@@ -16,21 +22,38 @@ const _SwiperItem = forwardRef((props, ref) => {
16
22
  // 存储layout布局信息
17
23
  layoutRef, layoutProps, layoutStyle } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: nodeRef });
18
24
  const innerProps = useInnerProps(props, {
19
- style: { ...innerStyle, ...layoutStyle },
20
25
  ref: nodeRef,
21
26
  ...layoutProps
22
27
  }, [
23
28
  'children',
24
- 'enable-offset'
29
+ 'enable-offset',
30
+ 'style'
25
31
  ], { layoutRef });
26
- return (<View data-itemId={props['item-id']} {...innerProps}>
27
- {wrapChildren(props, {
32
+ const itemAnimatedStyle = useAnimatedStyle(() => {
33
+ if (!step.value)
34
+ return {};
35
+ const inputRange = [step.value, 0];
36
+ const outputRange = [0.7, 1];
37
+ // 实现元素的宽度跟随step从0到真实宽度,且不能触发重新渲染整个组件,通过AnimatedStyle的方式实现
38
+ const outerLayoutStyle = dir === 'x' ? { width: step.value, height: '100%' } : { width: '100%', height: step.value };
39
+ const transformStyle = [];
40
+ if (scale) {
41
+ transformStyle.push({
42
+ scale: interpolate(Math.abs(Math.abs(offset.value) - itemIndex * step.value), inputRange, outputRange)
43
+ });
44
+ }
45
+ return Object.assign(outerLayoutStyle, {
46
+ transform: transformStyle
47
+ });
48
+ });
49
+ return (<Animated.View {...innerProps} style={[innerStyle, layoutStyle, itemAnimatedStyle, customStyle]} data-itemId={props['item-id']}>
50
+ {wrapChildren(props, {
28
51
  hasVarDec,
29
52
  varContext: varContextRef.current,
30
53
  textStyle,
31
54
  textProps
32
55
  })}
33
- </View>);
56
+ </Animated.View>);
34
57
  });
35
58
  _SwiperItem.displayName = 'MpxSwiperItem';
36
59
  export default _SwiperItem;