@mpxjs/webpack-plugin 2.10.3 → 2.10.4

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 (103) hide show
  1. package/lib/config.js +2 -1
  2. package/lib/index.js +1 -1
  3. package/lib/platform/json/wx/index.js +6 -3
  4. package/lib/platform/style/wx/index.js +23 -12
  5. package/lib/platform/template/wx/component-config/button.js +19 -2
  6. package/lib/platform/template/wx/component-config/canvas.js +4 -0
  7. package/lib/platform/template/wx/component-config/checkbox-group.js +4 -0
  8. package/lib/platform/template/wx/component-config/checkbox.js +4 -0
  9. package/lib/platform/template/wx/component-config/cover-image.js +7 -1
  10. package/lib/platform/template/wx/component-config/cover-view.js +4 -0
  11. package/lib/platform/template/wx/component-config/fix-component-name.js +3 -2
  12. package/lib/platform/template/wx/component-config/form.js +7 -1
  13. package/lib/platform/template/wx/component-config/icon.js +4 -0
  14. package/lib/platform/template/wx/component-config/image.js +7 -1
  15. package/lib/platform/template/wx/component-config/input.js +18 -3
  16. package/lib/platform/template/wx/component-config/label.js +4 -0
  17. package/lib/platform/template/wx/component-config/movable-area.js +7 -1
  18. package/lib/platform/template/wx/component-config/movable-view.js +12 -3
  19. package/lib/platform/template/wx/component-config/navigator.js +4 -0
  20. package/lib/platform/template/wx/component-config/picker-view-column.js +4 -0
  21. package/lib/platform/template/wx/component-config/picker-view.js +7 -1
  22. package/lib/platform/template/wx/component-config/picker.js +7 -1
  23. package/lib/platform/template/wx/component-config/radio-group.js +4 -0
  24. package/lib/platform/template/wx/component-config/radio.js +4 -0
  25. package/lib/platform/template/wx/component-config/rich-text.js +4 -0
  26. package/lib/platform/template/wx/component-config/root-portal.js +4 -0
  27. package/lib/platform/template/wx/component-config/scroll-view.js +10 -2
  28. package/lib/platform/template/wx/component-config/swiper-item.js +7 -1
  29. package/lib/platform/template/wx/component-config/swiper.js +12 -3
  30. package/lib/platform/template/wx/component-config/switch.js +4 -0
  31. package/lib/platform/template/wx/component-config/text.js +24 -3
  32. package/lib/platform/template/wx/component-config/textarea.js +17 -2
  33. package/lib/platform/template/wx/component-config/unsupported.js +7 -0
  34. package/lib/platform/template/wx/component-config/video.js +10 -2
  35. package/lib/platform/template/wx/component-config/view.js +11 -1
  36. package/lib/platform/template/wx/component-config/web-view.js +4 -0
  37. package/lib/platform/template/wx/index.js +42 -75
  38. package/lib/react/processScript.js +1 -18
  39. package/lib/runtime/components/react/dist/event.config.js +1 -0
  40. package/lib/runtime/components/react/dist/getInnerListeners.js +18 -7
  41. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +1 -1
  42. package/lib/runtime/components/react/dist/mpx-inline-text.jsx +11 -0
  43. package/lib/runtime/components/react/dist/mpx-input.jsx +3 -6
  44. package/lib/runtime/components/react/dist/mpx-keyboard-avoiding-view.jsx +2 -2
  45. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +194 -68
  46. package/lib/runtime/components/react/dist/mpx-picker/dateData.js +17 -0
  47. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +178 -96
  48. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +79 -139
  49. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +190 -90
  50. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +60 -75
  51. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +100 -228
  52. package/lib/runtime/components/react/dist/{mpx-picker-view.jsx → mpx-picker-view/index.jsx} +3 -3
  53. package/lib/runtime/components/react/dist/{mpx-picker-view-column.jsx → mpx-picker-view-column/index.jsx} +64 -16
  54. package/lib/runtime/components/react/dist/{mpx-picker-view-column-item.jsx → mpx-picker-view-column/pickerViewColumnItem.jsx} +8 -5
  55. package/lib/runtime/components/react/dist/{pickerFaces.js → mpx-picker-view-column/pickerViewFaces.js} +6 -0
  56. package/lib/runtime/components/react/dist/mpx-popup/index.jsx +61 -0
  57. package/lib/runtime/components/react/dist/mpx-popup/popupBase.jsx +92 -0
  58. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +192 -25
  59. package/lib/runtime/components/react/dist/mpx-simple-text.jsx +8 -7
  60. package/lib/runtime/components/react/dist/mpx-simple-view.jsx +11 -15
  61. package/lib/runtime/components/react/dist/mpx-video.jsx +3 -3
  62. package/lib/runtime/components/react/dist/mpx-web-view.jsx +7 -4
  63. package/lib/runtime/components/react/dist/utils.jsx +2 -1
  64. package/lib/runtime/components/react/event.config.ts +2 -0
  65. package/lib/runtime/components/react/getInnerListeners.ts +28 -25
  66. package/lib/runtime/components/react/mpx-canvas/index.tsx +2 -2
  67. package/lib/runtime/components/react/mpx-inline-text.tsx +18 -0
  68. package/lib/runtime/components/react/mpx-input.tsx +2 -6
  69. package/lib/runtime/components/react/mpx-keyboard-avoiding-view.tsx +1 -1
  70. package/lib/runtime/components/react/mpx-picker/date.tsx +226 -69
  71. package/lib/runtime/components/react/mpx-picker/dateData.ts +22 -0
  72. package/lib/runtime/components/react/mpx-picker/index.tsx +239 -118
  73. package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +96 -139
  74. package/lib/runtime/components/react/mpx-picker/region.tsx +217 -89
  75. package/lib/runtime/components/react/mpx-picker/selector.tsx +75 -80
  76. package/lib/runtime/components/react/mpx-picker/time.tsx +119 -236
  77. package/lib/runtime/components/react/mpx-picker/type.ts +85 -71
  78. package/lib/runtime/components/react/{mpx-picker-view.tsx → mpx-picker-view/index.tsx} +7 -7
  79. package/lib/runtime/components/react/{mpx-picker-view-column.tsx → mpx-picker-view-column/index.tsx} +70 -19
  80. package/lib/runtime/components/react/{mpx-picker-view-column-item.tsx → mpx-picker-view-column/pickerViewColumnItem.tsx} +8 -5
  81. package/lib/runtime/components/react/{pickerFaces.ts → mpx-picker-view-column/pickerViewFaces.ts} +7 -0
  82. package/lib/runtime/components/react/mpx-popup/index.tsx +86 -0
  83. package/lib/runtime/components/react/mpx-popup/popupBase.tsx +130 -0
  84. package/lib/runtime/components/react/mpx-scroll-view.tsx +249 -43
  85. package/lib/runtime/components/react/mpx-simple-text.tsx +10 -8
  86. package/lib/runtime/components/react/mpx-simple-view.tsx +11 -16
  87. package/lib/runtime/components/react/mpx-video.tsx +2 -2
  88. package/lib/runtime/components/react/mpx-web-view.tsx +7 -4
  89. package/lib/runtime/components/react/types/getInnerListeners.d.ts +5 -1
  90. package/lib/runtime/components/react/types/global.d.ts +1 -1
  91. package/lib/runtime/components/react/utils.tsx +3 -2
  92. package/lib/runtime/components/web/mini-video-controls.min.js +1 -1
  93. package/lib/runtime/components/web/mpx-input.vue +1 -1
  94. package/lib/runtime/stringify.wxs +2 -2
  95. package/lib/template-compiler/compiler.js +8 -8
  96. package/lib/utils/env.js +1 -1
  97. package/package.json +4 -5
  98. /package/lib/runtime/components/react/dist/{pickerVIewContext.js → mpx-picker-view/pickerVIewContext.js} +0 -0
  99. /package/lib/runtime/components/react/dist/{pickerViewIndicator.jsx → mpx-picker-view-column/pickerViewIndicator.jsx} +0 -0
  100. /package/lib/runtime/components/react/dist/{pickerViewMask.jsx → mpx-picker-view-column/pickerViewMask.jsx} +0 -0
  101. /package/lib/runtime/components/react/{pickerVIewContext.ts → mpx-picker-view/pickerVIewContext.ts} +0 -0
  102. /package/lib/runtime/components/react/{pickerViewIndicator.tsx → mpx-picker-view-column/pickerViewIndicator.tsx} +0 -0
  103. /package/lib/runtime/components/react/{pickerViewMask.tsx → mpx-picker-view-column/pickerViewMask.tsx} +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,8 +1,8 @@
1
1
  import { View } from 'react-native';
2
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';
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
7
  const styles = {
8
8
  wrapper: {
@@ -1,12 +1,13 @@
1
1
  import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } 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 } from './utils';
5
- import useNodesRef from './useNodesRef';
4
+ import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS } 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 { PickerViewColumnAnimationContext } from '../mpx-picker-view/pickerVIewContext';
10
+ import { calcHeightOffsets } from './pickerViewFaces';
10
11
  const visibleCount = 5;
11
12
  const _PickerViewColumn = forwardRef((props, ref) => {
12
13
  const { columnData, columnIndex, initialIndex, onSelectChange, style, wrapperStyle, pickerMaskStyle, pickerIndicatorStyle, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
@@ -22,10 +23,11 @@ const _PickerViewColumn = forwardRef((props, ref) => {
22
23
  const [itemRawH, setItemRawH] = useState(itemHeight);
23
24
  const maxIndex = useMemo(() => columnData.length - 1, [columnData]);
24
25
  const prevScrollingInfo = useRef({ index: initialIndex, y: 0 });
25
- const touching = useRef(false);
26
+ const dragging = useRef(false);
26
27
  const scrolling = useRef(false);
27
28
  const timerResetPosition = useRef(null);
28
29
  const timerScrollTo = useRef(null);
30
+ const timerClickOnce = useRef(null);
29
31
  const activeIndex = useRef(initialIndex);
30
32
  const prevIndex = usePrevious(initialIndex);
31
33
  const prevMaxIndex = usePrevious(maxIndex);
@@ -57,6 +59,12 @@ const _PickerViewColumn = forwardRef((props, ref) => {
57
59
  timerScrollTo.current = null;
58
60
  }
59
61
  }, []);
62
+ const clearTimerClickOnce = useCallback(() => {
63
+ if (timerClickOnce.current) {
64
+ clearTimeout(timerClickOnce.current);
65
+ timerClickOnce.current = null;
66
+ }
67
+ }, []);
60
68
  useEffect(() => {
61
69
  return () => {
62
70
  clearTimerResetPosition();
@@ -66,7 +74,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
66
74
  useEffect(() => {
67
75
  if (!scrollViewRef.current ||
68
76
  !itemRawH ||
69
- touching.current ||
77
+ dragging.current ||
70
78
  scrolling.current ||
71
79
  prevIndex == null ||
72
80
  initialIndex === prevIndex ||
@@ -81,8 +89,8 @@ const _PickerViewColumn = forwardRef((props, ref) => {
81
89
  y: initialIndex * itemRawH,
82
90
  animated: false
83
91
  });
84
- }, isAndroid ? 200 : 0);
85
- activeIndex.current = initialIndex;
92
+ activeIndex.current = initialIndex;
93
+ }, isIOS ? 0 : 200);
86
94
  }, [itemRawH, maxIndex, initialIndex]);
87
95
  const onContentSizeChange = useCallback((_w, h) => {
88
96
  const y = initialIndex * itemRawH;
@@ -90,6 +98,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
90
98
  clearTimerScrollTo();
91
99
  timerScrollTo.current = setTimeout(() => {
92
100
  scrollViewRef.current?.scrollTo({ x: 0, y, animated: false });
101
+ activeIndex.current = initialIndex;
93
102
  }, 0);
94
103
  }
95
104
  }, [itemRawH, initialIndex]);
@@ -101,7 +110,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
101
110
  }
102
111
  }, [itemRawH]);
103
112
  const resetScrollPosition = useCallback((y) => {
104
- if (touching.current || scrolling.current) {
113
+ if (dragging.current || scrolling.current) {
105
114
  return;
106
115
  }
107
116
  scrolling.current = true;
@@ -126,14 +135,14 @@ const _PickerViewColumn = forwardRef((props, ref) => {
126
135
  }, [itemRawH, getIndex, onSelectChange, resetScrollPosition]);
127
136
  const onScrollBeginDrag = useCallback(() => {
128
137
  isIOS && clearTimerResetPosition();
129
- touching.current = true;
138
+ dragging.current = true;
130
139
  prevScrollingInfo.current = {
131
140
  index: activeIndex.current,
132
141
  y: activeIndex.current * itemRawH
133
142
  };
134
143
  }, [itemRawH]);
135
144
  const onScrollEndDrag = useCallback((e) => {
136
- touching.current = false;
145
+ dragging.current = false;
137
146
  if (isIOS) {
138
147
  const { y } = e.nativeEvent.contentOffset;
139
148
  if (y % itemRawH === 0) {
@@ -154,7 +163,7 @@ const _PickerViewColumn = forwardRef((props, ref) => {
154
163
  }
155
164
  const { y } = e.nativeEvent.contentOffset;
156
165
  const { index: prevIndex, y: _y } = prevScrollingInfo.current;
157
- if (touching.current || scrolling.current) {
166
+ if (dragging.current || scrolling.current) {
158
167
  if (Math.abs(y - _y) >= itemRawH) {
159
168
  const currentId = getIndex(y);
160
169
  if (currentId !== prevIndex) {
@@ -168,12 +177,51 @@ const _PickerViewColumn = forwardRef((props, ref) => {
168
177
  }
169
178
  }
170
179
  }, [itemRawH, getIndex]);
180
+ const offsetHeights = useMemo(() => calcHeightOffsets(itemRawH), [itemRawH]);
181
+ const calcOffset = useCallback((y) => {
182
+ const baselineY = activeIndex.current * itemRawH + pickerH / 2;
183
+ const diff = Math.abs(y - baselineY);
184
+ const positive = y - baselineY > 0 ? 1 : -1;
185
+ const [h1, h2, h3] = offsetHeights;
186
+ if (diff > h1 && diff < h3) {
187
+ if (diff < h2) {
188
+ return 1 * positive;
189
+ }
190
+ else {
191
+ return 2 * positive;
192
+ }
193
+ }
194
+ return false;
195
+ }, [offsetHeights]);
196
+ /**
197
+ * 和小程序表现对齐,点击(不滑动)非焦点选项自动滚动到对应位置
198
+ */
199
+ const onClickOnceItem = useCallback((e) => {
200
+ const { locationY } = e.nativeEvent || {};
201
+ const offsetIndex = calcOffset(locationY);
202
+ if (dragging.current || !offsetIndex) {
203
+ return;
204
+ }
205
+ const targetIndex = activeIndex.current + offsetIndex;
206
+ if (targetIndex < 0 || targetIndex > maxIndex) {
207
+ return;
208
+ }
209
+ const y = targetIndex * itemRawH;
210
+ scrollViewRef.current?.scrollTo({ x: 0, y, animated: true });
211
+ if (isAndroid) {
212
+ // Android scrollTo 不会自动触发 onMomentumScrollEnd,需要手动触发
213
+ clearTimerClickOnce();
214
+ timerClickOnce.current = setTimeout(() => {
215
+ onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } });
216
+ }, 250);
217
+ }
218
+ }, [itemRawH, maxIndex, calcOffset, onMomentumScrollEnd]);
171
219
  const renderInnerchild = () => columnData.map((item, index) => {
172
220
  return (<MpxPickerVIewColumnItem key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout}/>);
173
221
  });
174
222
  const renderScollView = () => {
175
223
  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}>
224
+ <Reanimated.ScrollView ref={scrollViewRef} bounces={true} horizontal={false} nestedScrollEnabled={true} removeClippedSubviews={false} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} scrollEventThrottle={16} {...layoutProps} onTouchStart={onClickOnceItem} style={[{ width: '100%' }]} decelerationRate="fast" snapToOffsets={snapToOffsets} onScroll={onScroll} onScrollBeginDrag={onScrollBeginDrag} onScrollEndDrag={onScrollEndDrag} onMomentumScrollBegin={onMomentumScrollBegin} onMomentumScrollEnd={onMomentumScrollEnd} onContentSizeChange={onContentSizeChange} contentContainerStyle={contentContainerStyle}>
177
225
  {renderInnerchild()}
178
226
  </Reanimated.ScrollView>
179
227
  </PickerViewColumnAnimationContext.Provider>);
@@ -181,9 +229,9 @@ const _PickerViewColumn = forwardRef((props, ref) => {
181
229
  const renderIndicator = () => (<PickerIndicator itemHeight={itemHeight} indicatorItemStyle={pickerIndicatorStyle}/>);
182
230
  const renderMask = () => (<PickerMask itemHeight={itemHeight} maskContainerStyle={pickerMaskStyle}/>);
183
231
  return (<View style={[styles.wrapper, normalStyle]}>
184
- {renderScollView()}
185
- {renderMask()}
186
- {renderIndicator()}
232
+ {renderScollView()}
233
+ {renderMask()}
234
+ {renderIndicator()}
187
235
  </View>);
188
236
  });
189
237
  const styles = StyleSheet.create({
@@ -1,8 +1,8 @@
1
1
  import React, { useEffect } from 'react';
2
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';
3
+ import { extendObject } from '../utils';
4
+ import { createFaces } from './pickerViewFaces';
5
+ import { usePickerViewColumnAnimationContext, usePickerViewStyleContext } from '../mpx-picker-view/pickerVIewContext';
6
6
  const PickerViewColumnItem = ({ item, index, itemHeight, itemWidth = '100%', textStyle, textProps, visibleCount, onItemLayout }) => {
7
7
  const textStyleFromAncestor = usePickerViewStyleContext();
8
8
  const offsetYShared = usePickerViewColumnAnimationContext();
@@ -27,8 +27,11 @@ const PickerViewColumnItem = ({ item, index, itemHeight, itemWidth = '100%', tex
27
27
  style: extendObject({ height: itemHeight, width: '100%' }, textStyleFromAncestor, textStyle, item.props.style)
28
28
  }, textProps, restProps);
29
29
  const realItem = React.cloneElement(item, itemProps);
30
- return (<Reanimated.View key={strKey} style={[{ height: itemHeight, width: itemWidth }, animatedStyles]}>
31
- {realItem}
30
+ return (<Reanimated.View key={strKey} style={[
31
+ { height: itemHeight, width: itemWidth, pointerEvents: 'none' },
32
+ animatedStyles
33
+ ]}>
34
+ {realItem}
32
35
  </Reanimated.View>);
33
36
  };
34
37
  PickerViewColumnItem.displayName = 'MpxPickerViewColumnItem';
@@ -11,6 +11,12 @@ export const calcPickerHeight = (faces, itemHeight) => {
11
11
  }
12
12
  return faces.reduce((r, v) => r + calcHeight(Math.abs(v.deg), itemHeight), 0);
13
13
  };
14
+ export const calcHeightOffsets = (itemHeight) => {
15
+ const h1 = itemHeight / 2;
16
+ const h2 = h1 + calcHeight(30, itemHeight);
17
+ const h3 = h2 + calcHeight(60, itemHeight);
18
+ return [h1, h2, h3];
19
+ };
14
20
  export const createFaces = (itemHeight, visibleCount) => {
15
21
  // e.g [30, 60, 90]
16
22
  const getDegreesRelativeCenter = () => {
@@ -0,0 +1,61 @@
1
+ import { cloneElement } from 'react';
2
+ import Portal from '../mpx-portal';
3
+ import PopupBase from './popupBase';
4
+ /**
5
+ * 根据 type 返回对应的弹窗壳子组件
6
+ */
7
+ const getPopup = (type) => {
8
+ switch (type) {
9
+ case "picker" /* PopupType.PICKER */:
10
+ default:
11
+ return PopupBase;
12
+ }
13
+ };
14
+ /**
15
+ * 基于 Portal 封装的 Popup 弹窗组件管理 Hooks
16
+ */
17
+ const createPopupManager = (options = {}) => {
18
+ const { modal, type } = options;
19
+ const Modal = modal || getPopup(type);
20
+ let popupKey = null;
21
+ let isOpen = false;
22
+ let child = null;
23
+ const remove = () => {
24
+ if (popupKey !== null) {
25
+ Portal.remove(popupKey);
26
+ popupKey = null;
27
+ }
28
+ isOpen = false;
29
+ };
30
+ const open = (childComponent, pageId, options) => {
31
+ if (!isOpen && pageId != null) {
32
+ isOpen = true;
33
+ child = (<Modal hide={hide} {...options} visible={false}>
34
+ {childComponent}
35
+ </Modal>);
36
+ popupKey = Portal.add(child, pageId);
37
+ }
38
+ };
39
+ const update = (updatedChild) => {
40
+ if (popupKey !== null && child !== null && updatedChild !== null) {
41
+ child = cloneElement(child, { children: updatedChild });
42
+ Portal.update(popupKey, child);
43
+ }
44
+ };
45
+ const _updateVisible = (visible) => {
46
+ if (popupKey !== null && child !== null) {
47
+ child = cloneElement(child, { visible });
48
+ Portal.update(popupKey, child);
49
+ }
50
+ };
51
+ const show = () => _updateVisible(true);
52
+ const hide = () => _updateVisible(false);
53
+ return {
54
+ open,
55
+ show,
56
+ hide,
57
+ update,
58
+ remove
59
+ };
60
+ };
61
+ export { createPopupManager };