@mpxjs/webpack-plugin 2.10.3-beta.17 → 2.10.3-beta.18

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 (68) hide show
  1. package/lib/runtime/components/react/dist/context.js +5 -1
  2. package/lib/runtime/components/react/dist/event.config.js +0 -1
  3. package/lib/runtime/components/react/dist/getInnerListeners.js +148 -149
  4. package/lib/runtime/components/react/dist/mpx-async-suspense.jsx +145 -0
  5. package/lib/runtime/components/react/dist/mpx-button.jsx +11 -7
  6. package/lib/runtime/components/react/dist/mpx-canvas/Image.js +2 -4
  7. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +23 -21
  8. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +9 -4
  9. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +9 -5
  10. package/lib/runtime/components/react/dist/mpx-form.jsx +2 -2
  11. package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
  12. package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
  13. package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
  14. package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
  15. package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
  16. package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
  17. package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
  18. package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
  19. package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
  20. package/lib/runtime/components/react/dist/mpx-icon/index.jsx +9 -4
  21. package/lib/runtime/components/react/dist/mpx-image.jsx +92 -41
  22. package/lib/runtime/components/react/dist/mpx-inline-text.jsx +11 -0
  23. package/lib/runtime/components/react/dist/mpx-input.jsx +14 -13
  24. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +22 -7
  25. package/lib/runtime/components/react/dist/mpx-label.jsx +9 -5
  26. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +10 -5
  27. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +206 -80
  28. package/lib/runtime/components/react/dist/mpx-navigator.jsx +11 -3
  29. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +194 -68
  30. package/lib/runtime/components/react/dist/mpx-picker/dateData.js +17 -0
  31. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +178 -98
  32. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +79 -139
  33. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +190 -90
  34. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +60 -75
  35. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +100 -228
  36. package/lib/runtime/components/react/dist/{mpx-picker-view.jsx → mpx-picker-view/index.jsx} +16 -15
  37. package/lib/runtime/components/react/dist/{mpx-picker-view-column.jsx → mpx-picker-view-column/index.jsx} +95 -26
  38. package/lib/runtime/components/react/dist/{mpx-picker-view-column-item.jsx → mpx-picker-view-column/pickerViewColumnItem.jsx} +16 -16
  39. package/lib/runtime/components/react/dist/mpx-picker-view-column/pickerViewColumnItemLite.jsx +20 -0
  40. package/lib/runtime/components/react/dist/{pickerFaces.js → mpx-picker-view-column/pickerViewFaces.js} +6 -0
  41. package/lib/runtime/components/react/dist/mpx-popup/index.jsx +61 -0
  42. package/lib/runtime/components/react/dist/mpx-popup/popupBase.jsx +92 -0
  43. package/lib/runtime/components/react/dist/mpx-portal/index.jsx +5 -1
  44. package/lib/runtime/components/react/dist/mpx-portal/portal-manager.jsx +3 -5
  45. package/lib/runtime/components/react/dist/mpx-progress.jsx +163 -0
  46. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +11 -4
  47. package/lib/runtime/components/react/dist/mpx-radio.jsx +9 -5
  48. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +12 -4
  49. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +317 -89
  50. package/lib/runtime/components/react/dist/mpx-simple-text.jsx +7 -5
  51. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +11 -15
  52. package/lib/runtime/components/react/dist/mpx-slider.jsx +321 -0
  53. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +117 -0
  54. package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +45 -0
  55. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +15 -14
  56. package/lib/runtime/components/react/dist/mpx-swiper.jsx +245 -121
  57. package/lib/runtime/components/react/dist/mpx-switch.jsx +10 -7
  58. package/lib/runtime/components/react/dist/mpx-text.jsx +43 -13
  59. package/lib/runtime/components/react/dist/mpx-video.jsx +12 -7
  60. package/lib/runtime/components/react/dist/mpx-view.jsx +34 -18
  61. package/lib/runtime/components/react/dist/mpx-web-view.jsx +40 -35
  62. package/lib/runtime/components/react/dist/useAnimationHooks.js +35 -90
  63. package/lib/runtime/components/react/dist/utils.jsx +215 -109
  64. package/lib/runtime/components/web/mpx-titlebar.vue +21 -18
  65. package/package.json +1 -1
  66. /package/lib/runtime/components/react/dist/{pickerVIewContext.js → mpx-picker-view/pickerVIewContext.js} +0 -0
  67. /package/lib/runtime/components/react/dist/{pickerViewIndicator.jsx → mpx-picker-view-column/pickerViewIndicator.jsx} +0 -0
  68. /package/lib/runtime/components/react/dist/{pickerViewMask.jsx → mpx-picker-view-column/pickerViewMask.jsx} +0 -0
@@ -1,242 +1,114 @@
1
- import { View, Text, Modal, TouchableWithoutFeedback } from 'react-native';
2
- import Portal from '../mpx-portal/index';
3
- import { PickerView } from '@ant-design/react-native';
4
- import React, { forwardRef, useState, useRef, useEffect } from 'react';
5
- import useNodesRef from '../useNodesRef'; // 引入辅助函数
6
- // 可见应用窗口的大小。
7
- // const { height: dHeight, width: dWidth } = Dimensions.get('window');
8
- // modal属性: {"height": 298.33331298828125, "offsetLeft": 0, "offsetTop": 513.6666870117188, "width": 375, "x": 0, "y": 513.6666870117188}
9
- // const { height: sHeight, width: sWidth } = Dimensions.get('screen');
10
- // 设备屏幕的大小。 screen
11
- const styles = {
12
- showModal: {
13
- backgroundColor: 'black',
14
- opacity: 0.5,
15
- width: '100%',
16
- height: '100%'
1
+ import React, { forwardRef, useRef, useState, useEffect, useImperativeHandle } from 'react';
2
+ import { StyleSheet, Text } from 'react-native';
3
+ import { warn } from '@mpxjs/utils';
4
+ import MpxPickerView from '../mpx-picker-view';
5
+ import MpxPickerViewColumn from '../mpx-picker-view-column';
6
+ import { useUpdateEffect } from '../utils';
7
+ const styles = StyleSheet.create({
8
+ pickerContainer: {
9
+ width: 120,
10
+ height: 240,
11
+ alignSelf: 'center',
12
+ paddingHorizontal: 10,
13
+ borderTopLeftRadius: 10,
14
+ borderTopRightRadius: 10
17
15
  },
18
- hideModal: {
19
- opacity: 1,
20
- height: 0
16
+ pickerIndicator: {
17
+ height: 45
21
18
  },
22
- modal: {
23
- backgroundColor: 'black',
24
- opacity: 0.5
25
- },
26
- centeredView: {
27
- position: 'absolute',
28
- bottom: 0,
29
- width: '100%',
30
- overflow: 'scroll'
31
- },
32
- btnLine: {
33
- width: '100%',
34
- flex: 1,
35
- flexDirection: 'row',
36
- justifyContent: 'space-between',
37
- borderColor: 20,
38
- borderBottomWidth: 1,
39
- backgroundColor: 'white',
40
- paddingLeft: 40,
41
- paddingRight: 40
42
- },
43
- cancel: {
44
- height: 50,
45
- display: 'flex',
46
- justifyContent: 'center'
47
- },
48
- ok: {
49
- height: 50,
50
- display: 'flex',
51
- justifyContent: 'center'
52
- },
53
- btntext: {
54
- color: '#0ae',
55
- fontSize: 18
19
+ pickerItem: {
20
+ fontSize: 20,
21
+ lineHeight: 45,
22
+ textAlign: 'center'
56
23
  }
57
- };
58
- function formatStrToInt(timestr) {
59
- const [start, end] = timestr.split(':');
60
- return [parseInt(start), parseInt(end)];
61
- }
62
- // [9, 59] to 09:59
63
- function formatStr(arr) {
64
- let [hour, minute] = arr;
65
- if (hour < 10) {
66
- hour = '0' + hour;
24
+ });
25
+ const time2Array = (time, defaultValue = [0, 0]) => {
26
+ if (typeof time !== 'string') {
27
+ warn('[mpx runtime warn]: mpx-picker prop time must be a string');
28
+ return defaultValue;
67
29
  }
68
- if (minute < 10) {
69
- minute = '0' + minute;
30
+ let [hour = 0, minute = 0] = time.split(':').map(Number);
31
+ hour = Math.min(Math.max(hour, 0), 23);
32
+ minute = Math.min(Math.max(minute, 0), 59);
33
+ return [hour, minute];
34
+ };
35
+ const time2String = (time) => {
36
+ return time.map(i => i.toString().padStart(2, '0')).join(':');
37
+ };
38
+ const time2Minutes = (time) => {
39
+ return time[0] * 60 + time[1];
40
+ };
41
+ const calibrateTime = (time, start = [0, 0], end = [23, 59]) => {
42
+ time = typeof time === 'string' ? time2Array(time) : time;
43
+ start = typeof start === 'string' ? time2Array(start) : start;
44
+ end = typeof end === 'string' ? time2Array(end) : end;
45
+ const current = time2Minutes(time);
46
+ if (current < time2Minutes(start)) {
47
+ return start;
70
48
  }
71
- return hour + ':' + minute;
72
- }
73
- function generateMinute() {
74
- const arrMinute = [];
75
- for (let i = 0; i <= 59; i++) {
76
- const obj = {
77
- label: toSingleStr(i),
78
- value: i,
79
- children: []
80
- };
81
- arrMinute.push(obj);
49
+ else if (current > time2Minutes(end)) {
50
+ return end;
82
51
  }
83
- return arrMinute;
84
- }
85
- function generateColumns() {
86
- const pickData = [];
87
- for (let i = 0; i <= 23; i++) {
88
- const obj = {
89
- label: toSingleStr(i),
90
- value: i,
91
- children: generateMinute()
92
- };
93
- pickData.push(obj);
52
+ else {
53
+ return time;
94
54
  }
95
- return pickData;
96
- }
97
- function toSingleStr(str) {
98
- return str < 10 ? '0' + str : str;
99
- }
100
- function toStr(time) {
101
- const [hour, minute] = formatStrToInt(time);
102
- const newHour = toSingleStr(hour);
103
- const newMinute = toSingleStr(minute);
104
- return '' + newHour + newMinute;
105
- }
106
- function checkSelectedIsValid(strStart, strEnd, selected) {
107
- const strSel = '' + toSingleStr(selected[0]) + toSingleStr(selected[1]);
108
- if (strSel < strStart || strSel > strEnd)
109
- return false;
110
- return true;
111
- }
112
- /**
113
- * [{label:'', value: '', key: '', children: []}]
114
- label: string | ReactNode
115
- value: string | number
116
- key?: string | number
117
- children?: PickerColumnItem[]
118
- */
119
- // start="02:10" end = 23:01
120
- const _TimePicker = forwardRef((props, ref) => {
121
- const { children, start, end, value, bindchange, bindcancel, style } = props;
122
- const defaultProps = {
123
- start: '00:10',
124
- end: '23:59'
125
- };
126
- const defaultValue = formatStrToInt(value);
127
- const [timevalue, setTimeValue] = useState(defaultValue);
128
- // 存储layout布局信息
129
- const layoutRef = useRef({});
130
- const viewRef = useRef(null);
55
+ };
56
+ const hoursRange = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, '0'));
57
+ const minutesRange = Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, '0'));
58
+ const PickerTime = forwardRef((props, ref) => {
59
+ const { value = '00:00', start = '00:00', end = '23:59', bindchange } = props;
131
60
  const nodeRef = useRef(null);
132
- useNodesRef(props, ref, nodeRef, { style });
133
- // 存储modal的布局信息
134
- const modalLayoutRef = useRef({});
135
- const modalRef = useRef(null);
136
- const [visible, setVisible] = useState(false);
137
- const columnData = generateColumns();
138
- const [data, setData] = useState(columnData);
139
- const [offsetTop, setOffsetTop] = useState(0);
140
- const strStart = toStr(start);
141
- const strEnd = toStr(end);
142
- useEffect(() => {
143
- const newColumnData = generateColumns();
144
- setData(newColumnData);
145
- }, [start, end]);
146
- useEffect(() => {
147
- if (value) {
148
- const nValue = formatStrToInt(value);
149
- nValue && setTimeValue(nValue);
150
- }
151
- }, [value]);
152
- // console.log('---------------visible---', visible, JSON.stringify(columnData))
153
- const handleModalStatus = (status) => {
154
- setVisible(status);
61
+ const timerRef = useRef(null);
62
+ const startArray = time2Array(start);
63
+ const endArray = time2Array(end, [23, 59]);
64
+ const [formatValue, setFormatValue] = useState(calibrateTime(value, startArray, endArray));
65
+ const updateValue = (value = '00:00') => {
66
+ const calibratedValue = calibrateTime(value, startArray, endArray);
67
+ setFormatValue(calibratedValue);
155
68
  };
156
- const handleConfirm = () => {
157
- handleModalStatus(false);
158
- bindchange && bindchange({
159
- detail: {
160
- value: formatStr(timevalue)
69
+ const _props = useRef(props);
70
+ _props.current = props;
71
+ useImperativeHandle(ref, () => ({
72
+ updateValue,
73
+ getNodeInstance: () => ({
74
+ props: _props,
75
+ nodeRef,
76
+ instance: {
77
+ style: {}
161
78
  }
162
- });
163
- };
164
- const handleCancel = () => {
165
- handleModalStatus(false);
166
- bindcancel && bindcancel();
167
- };
168
- const handleChildClick = () => {
169
- handleModalStatus(true);
170
- };
171
- const handlePickChange = (date) => {
172
- // 不是有效的值
173
- if (!checkSelectedIsValid(strStart, strEnd, date)) {
174
- setTimeValue(timevalue);
79
+ })
80
+ }));
81
+ useEffect(() => {
82
+ return () => {
83
+ timerRef.current && clearTimeout(timerRef.current);
84
+ };
85
+ }, []);
86
+ useUpdateEffect(() => {
87
+ const calibratedValue = calibrateTime(value, startArray, endArray);
88
+ setFormatValue(calibratedValue);
89
+ }, [value]);
90
+ const onChange = (e) => {
91
+ const { value } = e.detail;
92
+ const calibratedValue = calibrateTime(value, startArray, endArray);
93
+ bindchange?.({ detail: { value: time2String(calibratedValue) } });
94
+ if (value[0] !== formatValue[0] || value[1] !== formatValue[1]) {
95
+ setFormatValue(value);
175
96
  }
176
- else {
177
- // [9, 13]
178
- setTimeValue(date);
97
+ if (value[0] !== calibratedValue[0] || value[1] !== calibratedValue[1]) {
98
+ timerRef.current && clearTimeout(timerRef.current);
99
+ timerRef.current = setTimeout(() => setFormatValue(calibratedValue));
179
100
  }
180
101
  };
181
- const onElementLayout = () => {
182
- viewRef.current?.measure((x, y, width, height, offsetLeft, offsetTop) => {
183
- layoutRef.current = { x, y, width, height, offsetLeft, offsetTop };
184
- props.getInnerLayout && props.getInnerLayout(layoutRef);
185
- });
186
- };
187
- const onModalLayout = () => {
188
- modalRef.current?.measure((x, y, width, height, offsetLeft, offsetTop) => {
189
- modalLayoutRef.current = { x, y, width, height, offsetLeft, offsetTop };
190
- setOffsetTop(offsetTop);
191
- });
192
- };
193
- const renderModalChildren = () => {
194
- const pickerProps = {
195
- ref: nodeRef,
196
- data,
197
- value: timevalue,
198
- defaultValue: timevalue,
199
- cols: 2,
200
- onChange: handlePickChange
201
- };
202
- return (<View style={styles.centeredView} ref={modalRef} onLayout={onModalLayout}>
203
- <View style={styles.btnLine}>
204
- <View style={styles.cancel}>
205
- <TouchableWithoutFeedback onPress={handleCancel}>
206
- <Text style={styles.btntext}>取消</Text>
207
- </TouchableWithoutFeedback>
208
- </View>
209
- <View style={styles.ok}>
210
- <TouchableWithoutFeedback onPress={handleConfirm}>
211
- <Text style={styles.btntext}>确定</Text>
212
- </TouchableWithoutFeedback>
213
- </View>
214
- </View>
215
- <PickerView {...pickerProps}></PickerView>
216
- </View>);
217
- };
218
- const renderChildren = () => {
219
- const touchProps = {
220
- onLayout: onElementLayout,
221
- ref: viewRef
222
- };
223
- return <View>
224
- <TouchableWithoutFeedback onPress={handleChildClick}>
225
- <View {...touchProps}>{children}</View>
226
- </TouchableWithoutFeedback>
227
- </View>;
228
- };
229
- // Animated.View
230
- return (<>
231
- <Portal>
232
- <View style={visible ? styles.showModal : styles.hideModal}>
233
- <Modal animationType="slide" transparent={true} visible={visible}>
234
- {renderModalChildren()}
235
- </Modal>
236
- </View>
237
- </Portal>
238
- {renderChildren()}
239
- </>);
102
+ return (<MpxPickerView style={styles.pickerContainer} indicator-style={styles.pickerIndicator} value={formatValue} bindchange={onChange}>
103
+ {/* @ts-expect-error ignore */}
104
+ <MpxPickerViewColumn key='hour'>
105
+ {hoursRange.map((item, index) => (<Text key={index} style={styles.pickerItem}>{item}</Text>))}
106
+ </MpxPickerViewColumn>
107
+ {/* @ts-expect-error ignore */}
108
+ <MpxPickerViewColumn key='minute'>
109
+ {minutesRange.map((item, index) => (<Text key={index} style={styles.pickerItem}>{item}</Text>))}
110
+ </MpxPickerViewColumn>
111
+ </MpxPickerView>);
240
112
  });
241
- _TimePicker.displayName = 'mpx-picker-time';
242
- export default _TimePicker;
113
+ PickerTime.displayName = 'MpxPickerTime';
114
+ export default PickerTime;
@@ -1,9 +1,10 @@
1
1
  import { View } from 'react-native';
2
- import React, { forwardRef, useRef } from 'react';
3
- import useInnerProps, { getCustomEvent } from './getInnerListeners';
4
- import useNodesRef from './useNodesRef';
5
- import { useLayout, splitProps, splitStyle, wrapChildren, useTransformStyle, extendObject } from './utils';
2
+ import React, { createElement, forwardRef, useRef } from 'react';
3
+ import useInnerProps, { getCustomEvent } from '../getInnerListeners';
4
+ import useNodesRef from '../useNodesRef';
5
+ import { useLayout, splitProps, splitStyle, wrapChildren, useTransformStyle, extendObject } from '../utils';
6
6
  import { PickerViewStyleContext } from './pickerVIewContext';
7
+ import Portal from '../mpx-portal';
7
8
  const styles = {
8
9
  wrapper: {
9
10
  display: 'flex',
@@ -16,14 +17,14 @@ const styles = {
16
17
  };
17
18
  const DefaultPickerItemH = 36;
18
19
  const _PickerView = forwardRef((props, ref) => {
19
- const { children, value = [], bindchange, style, 'indicator-style': indicatorStyle = {}, 'mask-style': pickerMaskStyle = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
20
+ const { children, value = [], bindchange, style, 'enable-wheel-animation': enableWheelAnimation = true, 'indicator-style': indicatorStyle = {}, 'mask-style': pickerMaskStyle = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
20
21
  const { height: indicatorH, ...pickerIndicatorStyle } = indicatorStyle;
21
22
  const nodeRef = useRef(null);
22
23
  const cloneRef = useRef(null);
23
24
  const activeValueRef = useRef(value);
24
25
  activeValueRef.current = value.slice();
25
26
  const snapActiveValueRef = useRef(null);
26
- const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
27
+ const { normalStyle, hasVarDec, varContextRef, hasSelfPercent, setWidth, setHeight, hasPositionFixed } = useTransformStyle(style, { enableVar, externalVarContext });
27
28
  useNodesRef(props, ref, nodeRef, {
28
29
  style: normalStyle
29
30
  });
@@ -47,13 +48,12 @@ const _PickerView = forwardRef((props, ref) => {
47
48
  snapActiveValueRef.current = value.slice();
48
49
  }
49
50
  };
50
- const innerProps = useInnerProps(props, extendObject({
51
+ const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
51
52
  ref: nodeRef,
52
53
  style: extendObject({}, normalStyle, layoutStyle, {
53
54
  position: 'relative',
54
55
  overflow: 'hidden'
55
- }),
56
- layoutProps
56
+ })
57
57
  }), [
58
58
  'enable-offset',
59
59
  'indicator-style',
@@ -75,7 +75,8 @@ const _PickerView = forwardRef((props, ref) => {
75
75
  onSelectChange: onSelectChange.bind(null, index),
76
76
  initialIndex,
77
77
  pickerIndicatorStyle,
78
- pickerMaskStyle
78
+ pickerMaskStyle,
79
+ enableWheelAnimation
79
80
  });
80
81
  const realElement = React.cloneElement(child, wrappedProps);
81
82
  return wrapChildren({
@@ -115,11 +116,11 @@ const _PickerView = forwardRef((props, ref) => {
115
116
  onInitialChange(isInvalid, validValue);
116
117
  return renderColumns;
117
118
  };
118
- return (<PickerViewStyleContext.Provider value={textStyle}>
119
- <View {...innerProps}>
120
- <View style={[styles.wrapper]}>{renderPickerColumns()}</View>
121
- </View>
122
- </PickerViewStyleContext.Provider>);
119
+ const finalComponent = createElement(PickerViewStyleContext.Provider, { value: textStyle }, createElement(View, innerProps, createElement(View, { style: [styles.wrapper] }, renderPickerColumns())));
120
+ if (hasPositionFixed) {
121
+ return createElement(Portal, null, finalComponent);
122
+ }
123
+ return finalComponent;
123
124
  });
124
125
  _PickerView.displayName = 'MpxPickerView';
125
126
  export default _PickerView;
@@ -1,15 +1,17 @@
1
- import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react';
1
+ import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback, createElement } from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
  import Reanimated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
4
- import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, isHarmony } from './utils';
5
- import useNodesRef from './useNodesRef';
4
+ import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, isHarmony, extendObject } from '../utils';
5
+ import useNodesRef from '../useNodesRef';
6
6
  import PickerIndicator from './pickerViewIndicator';
7
7
  import PickerMask from './pickerViewMask';
8
- import MpxPickerVIewColumnItem from './mpx-picker-view-column-item';
9
- import { PickerViewColumnAnimationContext } from './pickerVIewContext';
8
+ import MpxPickerViewColumnItem from './pickerViewColumnItem';
9
+ import MpxPickerViewColumnItemLite from './pickerViewColumnItemLite';
10
+ import { PickerViewColumnAnimationContext } from '../mpx-picker-view/pickerVIewContext';
11
+ import { calcHeightOffsets } from './pickerViewFaces';
10
12
  const visibleCount = 5;
11
13
  const _PickerViewColumn = forwardRef((props, ref) => {
12
- const { columnData, columnIndex, initialIndex, onSelectChange, style, wrapperStyle, pickerMaskStyle, pickerIndicatorStyle, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
14
+ const { columnData, columnIndex, initialIndex, onSelectChange, style, wrapperStyle, pickerMaskStyle, pickerIndicatorStyle, enableWheelAnimation = true, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
13
15
  const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
14
16
  const { textStyle = {} } = splitStyle(normalStyle);
15
17
  const { textProps = {} } = splitProps(props);
@@ -22,10 +24,11 @@ const _PickerViewColumn = forwardRef((props, ref) => {
22
24
  const [itemRawH, setItemRawH] = useState(itemHeight);
23
25
  const maxIndex = useMemo(() => columnData.length - 1, [columnData]);
24
26
  const prevScrollingInfo = useRef({ index: initialIndex, y: 0 });
25
- const touching = useRef(false);
27
+ const dragging = useRef(false);
26
28
  const scrolling = useRef(false);
27
29
  const timerResetPosition = useRef(null);
28
30
  const timerScrollTo = useRef(null);
31
+ const timerClickOnce = useRef(null);
29
32
  const activeIndex = useRef(initialIndex);
30
33
  const prevIndex = usePrevious(initialIndex);
31
34
  const prevMaxIndex = usePrevious(maxIndex);
@@ -57,6 +60,12 @@ const _PickerViewColumn = forwardRef((props, ref) => {
57
60
  timerScrollTo.current = null;
58
61
  }
59
62
  }, []);
63
+ const clearTimerClickOnce = useCallback(() => {
64
+ if (timerClickOnce.current) {
65
+ clearTimeout(timerClickOnce.current);
66
+ timerClickOnce.current = null;
67
+ }
68
+ }, []);
60
69
  useEffect(() => {
61
70
  return () => {
62
71
  clearTimerResetPosition();
@@ -66,7 +75,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
66
75
  useEffect(() => {
67
76
  if (!scrollViewRef.current ||
68
77
  !itemRawH ||
69
- touching.current ||
78
+ dragging.current ||
70
79
  scrolling.current ||
71
80
  prevIndex == null ||
72
81
  initialIndex === prevIndex ||
@@ -81,8 +90,8 @@ const _PickerViewColumn = forwardRef((props, ref) => {
81
90
  y: initialIndex * itemRawH,
82
91
  animated: false
83
92
  });
84
- }, isAndroid || isHarmony ? 200 : 0);
85
- activeIndex.current = initialIndex;
93
+ activeIndex.current = initialIndex;
94
+ }, isIOS ? 0 : 200);
86
95
  }, [itemRawH, maxIndex, initialIndex]);
87
96
  const onContentSizeChange = useCallback((_w, h) => {
88
97
  const y = initialIndex * itemRawH;
@@ -90,6 +99,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
90
99
  clearTimerScrollTo();
91
100
  timerScrollTo.current = setTimeout(() => {
92
101
  scrollViewRef.current?.scrollTo({ x: 0, y, animated: false });
102
+ activeIndex.current = initialIndex;
93
103
  }, 0);
94
104
  }
95
105
  }, [itemRawH, initialIndex]);
@@ -101,7 +111,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
101
111
  }
102
112
  }, [itemRawH]);
103
113
  const resetScrollPosition = useCallback((y) => {
104
- if (touching.current || scrolling.current) {
114
+ if (dragging.current || scrolling.current) {
105
115
  return;
106
116
  }
107
117
  scrolling.current = true;
@@ -126,17 +136,17 @@ const _PickerViewColumn = forwardRef((props, ref) => {
126
136
  }, [itemRawH, getIndex, onSelectChange, resetScrollPosition]);
127
137
  const onScrollBeginDrag = useCallback(() => {
128
138
  isIOS && clearTimerResetPosition();
129
- touching.current = true;
139
+ dragging.current = true;
130
140
  prevScrollingInfo.current = {
131
141
  index: activeIndex.current,
132
142
  y: activeIndex.current * itemRawH
133
143
  };
134
144
  }, [itemRawH]);
135
145
  const onScrollEndDrag = useCallback((e) => {
136
- touching.current = false;
137
- if (isIOS) {
146
+ dragging.current = false;
147
+ if (!isAndroid) {
138
148
  const { y } = e.nativeEvent.contentOffset;
139
- if (y % itemRawH === 0) {
149
+ if (y % itemRawH === 0 || (isHarmony && y > snapToOffsets[maxIndex])) {
140
150
  onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } });
141
151
  }
142
152
  else if (y > 0 && y < snapToOffsets[maxIndex]) {
@@ -148,13 +158,13 @@ const _PickerViewColumn = forwardRef((props, ref) => {
148
158
  }, [itemRawH, maxIndex, snapToOffsets, onMomentumScrollEnd, resetScrollPosition]);
149
159
  const onScroll = useCallback((e) => {
150
160
  // 全局注册的振动触感 hook
151
- const pickerVibrate = global.__mpx?.config?.rnConfig?.pickerVibrate;
152
- if (typeof pickerVibrate !== 'function') {
161
+ const onPickerVibrate = global.__mpx?.config?.rnConfig?.onPickerVibrate;
162
+ if (typeof onPickerVibrate !== 'function') {
153
163
  return;
154
164
  }
155
165
  const { y } = e.nativeEvent.contentOffset;
156
166
  const { index: prevIndex, y: _y } = prevScrollingInfo.current;
157
- if (touching.current || scrolling.current) {
167
+ if (dragging.current || scrolling.current) {
158
168
  if (Math.abs(y - _y) >= itemRawH) {
159
169
  const currentId = getIndex(y);
160
170
  if (currentId !== prevIndex) {
@@ -163,20 +173,78 @@ const _PickerViewColumn = forwardRef((props, ref) => {
163
173
  y: currentId * itemRawH
164
174
  };
165
175
  // vibrateShort({ type: 'selection' })
166
- pickerVibrate();
176
+ onPickerVibrate();
167
177
  }
168
178
  }
169
179
  }
170
180
  }, [itemRawH, getIndex]);
181
+ const offsetHeights = useMemo(() => calcHeightOffsets(itemRawH), [itemRawH]);
182
+ const calcOffset = useCallback((y) => {
183
+ const baselineY = activeIndex.current * itemRawH + pickerH / 2;
184
+ const diff = Math.abs(y - baselineY);
185
+ const positive = y - baselineY > 0 ? 1 : -1;
186
+ const [h1, h2, h3] = offsetHeights;
187
+ if (diff > h1 && diff < h3) {
188
+ if (diff < h2) {
189
+ return 1 * positive;
190
+ }
191
+ else {
192
+ return 2 * positive;
193
+ }
194
+ }
195
+ return false;
196
+ }, [offsetHeights]);
197
+ /**
198
+ * 和小程序表现对齐,点击(不滑动)非焦点选项自动滚动到对应位置
199
+ */
200
+ const onClickOnceItem = useCallback((e) => {
201
+ const { locationY } = e.nativeEvent || {};
202
+ const offsetIndex = calcOffset(locationY);
203
+ if (dragging.current || !offsetIndex) {
204
+ return;
205
+ }
206
+ const targetIndex = activeIndex.current + offsetIndex;
207
+ if (targetIndex < 0 || targetIndex > maxIndex) {
208
+ return;
209
+ }
210
+ const y = targetIndex * itemRawH;
211
+ scrollViewRef.current?.scrollTo({ x: 0, y, animated: true });
212
+ if (isAndroid) {
213
+ // Android scrollTo 不会自动触发 onMomentumScrollEnd,需要手动触发
214
+ clearTimerClickOnce();
215
+ timerClickOnce.current = setTimeout(() => {
216
+ onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } });
217
+ }, 250);
218
+ }
219
+ }, [itemRawH, maxIndex, calcOffset, onMomentumScrollEnd]);
171
220
  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}/>);
221
+ return enableWheelAnimation
222
+ ? (<MpxPickerViewColumnItem key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout}/>)
223
+ : (<MpxPickerViewColumnItemLite key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} onItemLayout={onItemLayout}/>);
173
224
  });
174
225
  const renderScollView = () => {
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>);
226
+ const innerProps = extendObject({}, layoutProps, {
227
+ ref: scrollViewRef,
228
+ bounces: true,
229
+ horizontal: false,
230
+ nestedScrollEnabled: true,
231
+ removeClippedSubviews: false,
232
+ showsVerticalScrollIndicator: false,
233
+ showsHorizontalScrollIndicator: false,
234
+ scrollEventThrottle: 16,
235
+ style: styles.scrollView,
236
+ decelerationRate: 'fast',
237
+ snapToOffsets: snapToOffsets,
238
+ onTouchEnd: onClickOnceItem,
239
+ onScroll,
240
+ onScrollBeginDrag,
241
+ onScrollEndDrag,
242
+ onMomentumScrollBegin,
243
+ onMomentumScrollEnd,
244
+ onContentSizeChange,
245
+ contentContainerStyle
246
+ });
247
+ return createElement(PickerViewColumnAnimationContext.Provider, { value: offsetYShared }, createElement(Reanimated.ScrollView, innerProps, renderInnerchild()));
180
248
  };
181
249
  const renderIndicator = () => (<PickerIndicator itemHeight={itemHeight} indicatorItemStyle={pickerIndicatorStyle}/>);
182
250
  const renderMask = () => (<PickerMask itemHeight={itemHeight} maskContainerStyle={pickerMaskStyle}/>);
@@ -187,7 +255,8 @@ const _PickerViewColumn = forwardRef((props, ref) => {
187
255
  </View>);
188
256
  });
189
257
  const styles = StyleSheet.create({
190
- wrapper: { display: 'flex', flex: 1 }
258
+ wrapper: { display: 'flex', flex: 1 },
259
+ scrollView: { width: '100%' }
191
260
  });
192
261
  _PickerViewColumn.displayName = 'MpxPickerViewColumn';
193
262
  export default _PickerViewColumn;