@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,7 +1,7 @@
1
1
  import { useRef, useMemo, RefObject } from 'react'
2
2
  import { hasOwn, collectDataset } from '@mpxjs/utils'
3
3
  import { omit, extendObject, useNavigation } from './utils'
4
- import eventConfigMap, { TAP_EVENTS } from './event.config'
4
+ import eventConfigMap, { TAP_EVENTS, LONGPRESS_EVENTS } from './event.config'
5
5
  import {
6
6
  Props,
7
7
  AdditionalProps,
@@ -10,7 +10,7 @@ import {
10
10
  InnerRef,
11
11
  SetTimeoutReturnType,
12
12
  LayoutRef,
13
- NativeTouchEvent,
13
+ ExtendedNativeTouchEvent,
14
14
  Navigation
15
15
  } from './types/getInnerListeners'
16
16
 
@@ -20,7 +20,7 @@ const globalEventState = {
20
20
 
21
21
  const getTouchEvent = (
22
22
  type: string,
23
- event: NativeTouchEvent,
23
+ event: ExtendedNativeTouchEvent,
24
24
  props: Props,
25
25
  config: UseInnerPropsConfig,
26
26
  navigation: Navigation
@@ -110,7 +110,7 @@ export const getCustomEvent = (
110
110
  function handleEmitEvent (
111
111
  events: string[],
112
112
  type: string,
113
- oe: NativeTouchEvent,
113
+ oe: ExtendedNativeTouchEvent,
114
114
  propsRef: Record<string, any>,
115
115
  config: UseInnerPropsConfig,
116
116
  navigation: Navigation
@@ -118,8 +118,22 @@ function handleEmitEvent (
118
118
  events.forEach((event) => {
119
119
  if (propsRef.current[event]) {
120
120
  const match = /^(catch|capture-catch):?(.*?)(?:\.(.*))?$/.exec(event)
121
+ // 检查是否已经被上层的 catch 阻止
122
+ if ((type === 'tap' || type === 'longpress') && oe._stoppedEventTypes?.has(type)) {
123
+ return
124
+ }
121
125
  if (match) {
122
- oe.stopPropagation()
126
+ const eventBase = match[2] || ''
127
+ if (eventBase === 'tap' || eventBase === 'longpress') {
128
+ // 为 tap、longpress 添加标记,影响后续的冒泡
129
+ if (!oe._stoppedEventTypes) {
130
+ oe._stoppedEventTypes = new Set()
131
+ }
132
+ oe._stoppedEventTypes.add(eventBase)
133
+ } else {
134
+ // 原生 touch 事件使用 stopPropagation
135
+ oe.stopPropagation()
136
+ }
123
137
  }
124
138
  propsRef.current[event](
125
139
  getTouchEvent(type, oe, propsRef.current, config, navigation)
@@ -128,7 +142,7 @@ function handleEmitEvent (
128
142
  })
129
143
  }
130
144
 
131
- function checkIsNeedPress (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>) {
145
+ function checkIsNeedPress (e: ExtendedNativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>) {
132
146
  const tapDetailInfo = ref.current!.mpxPressInfo.detail || { x: 0, y: 0 }
133
147
  const nativeEvent = e.nativeEvent
134
148
  const currentPageX = nativeEvent.changedTouches[0].pageX
@@ -144,7 +158,7 @@ function checkIsNeedPress (e: NativeTouchEvent, type: 'bubble' | 'capture', ref:
144
158
  }
145
159
  }
146
160
 
147
- function handleTouchstart (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) {
161
+ function handleTouchstart (e: ExtendedNativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) {
148
162
  e.persist()
149
163
  const bubbleTouchEvent = ['catchtouchstart', 'bindtouchstart']
150
164
  const bubblePressEvent = ['catchlongpress', 'bindlongpress']
@@ -168,18 +182,7 @@ function handleTouchstart (e: NativeTouchEvent, type: 'bubble' | 'capture', ref:
168
182
  const currentPressEvent =
169
183
  type === 'bubble' ? bubblePressEvent : capturePressEvent
170
184
  handleEmitEvent(currentTouchEvent, 'touchstart', e, propsRef, config, navigation)
171
- const {
172
- catchlongpress,
173
- bindlongpress,
174
- 'capture-catchlongpress': captureCatchlongpress,
175
- 'capture-bindlongpress': captureBindlongpress
176
- } = propsRef.current
177
- if (
178
- catchlongpress ||
179
- bindlongpress ||
180
- captureCatchlongpress ||
181
- captureBindlongpress
182
- ) {
185
+ if (LONGPRESS_EVENTS.some(eventName => propsRef.current[eventName])) {
183
186
  ref.current!.startTimer[type] = setTimeout(() => {
184
187
  // 只要触发过longpress, 全局就不再触发tap
185
188
  globalEventState.needPress = false
@@ -188,7 +191,7 @@ function handleTouchstart (e: NativeTouchEvent, type: 'bubble' | 'capture', ref:
188
191
  }
189
192
  }
190
193
 
191
- function handleTouchmove (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) {
194
+ function handleTouchmove (e: ExtendedNativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) {
192
195
  const bubbleTouchEvent = ['catchtouchmove', 'bindtouchmove']
193
196
  const captureTouchEvent = [
194
197
  'capture-catchtouchmove',
@@ -202,7 +205,7 @@ function handleTouchmove (e: NativeTouchEvent, type: 'bubble' | 'capture', ref:
202
205
  }
203
206
  }
204
207
 
205
- function handleTouchend (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) {
208
+ function handleTouchend (e: ExtendedNativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) {
206
209
  // move event may not be triggered
207
210
  if (TAP_EVENTS.some(eventName => propsRef.current[eventName])) {
208
211
  checkIsNeedPress(e, type, ref)
@@ -231,7 +234,7 @@ function handleTouchend (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: R
231
234
  }
232
235
 
233
236
  function handleTouchcancel (
234
- e: NativeTouchEvent,
237
+ e: ExtendedNativeTouchEvent,
235
238
  type: 'bubble' | 'capture',
236
239
  ref: RefObject<InnerRef>,
237
240
  propsRef: Record<string, any>,
@@ -252,7 +255,7 @@ function handleTouchcancel (
252
255
  }
253
256
 
254
257
  function createTouchEventHandler (eventName: 'onTouchStart'|'onTouchMove'|'onTouchEnd'|'onTouchCancel', type: 'bubble' | 'capture') {
255
- return (e: NativeTouchEvent, ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) => {
258
+ return (e: ExtendedNativeTouchEvent, ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) => {
256
259
  const handlerMap = {
257
260
  onTouchStart: handleTouchstart,
258
261
  onTouchMove: handleTouchmove,
@@ -341,11 +344,11 @@ const useInnerProps = (
341
344
  return acc
342
345
  }, [])
343
346
  const finalEventKeys = [...new Set(transformedEventKeys)]
344
- const events: Record<string, (e: NativeTouchEvent) => void> = {}
347
+ const events: Record<string, (e: ExtendedNativeTouchEvent) => void> = {}
345
348
 
346
349
  touchEventList.forEach((item) => {
347
350
  if (finalEventKeys.includes(item.eventName)) {
348
- events[item.eventName] = (e: NativeTouchEvent) =>
351
+ events[item.eventName] = (e: ExtendedNativeTouchEvent) =>
349
352
  item.handler(e, ref, propsRef, config, navigation)
350
353
  }
351
354
  })
@@ -251,8 +251,8 @@ const _Canvas = forwardRef<HandlerRef<CanvasProps & View, CanvasProps>, CanvasPr
251
251
  context: context2D
252
252
  })
253
253
 
254
- if (Platform.OS === 'android') {
255
- const isAndroid9 = Platform.Version >= 28
254
+ if (__mpx_mode__ !== 'ios') {
255
+ const isAndroid9 = Platform.Version as number >= 28
256
256
  return createElement(View, innerProps, createElement(
257
257
  WebView,
258
258
  {
@@ -0,0 +1,18 @@
1
+ import { Text, TextProps } from 'react-native'
2
+ import { JSX, createElement } from 'react'
3
+
4
+ import { extendObject } from './utils'
5
+
6
+ const InlineText = (props: TextProps): JSX.Element => {
7
+ const {
8
+ allowFontScaling = false
9
+ } = props
10
+
11
+ return createElement(Text, extendObject({}, props, {
12
+ allowFontScaling
13
+ }))
14
+ }
15
+
16
+ InlineText.displayName = 'MpxInlineText'
17
+
18
+ export default InlineText
@@ -54,7 +54,7 @@ import {
54
54
  TextInputSubmitEditingEventData
55
55
  } from 'react-native'
56
56
  import { warn } from '@mpxjs/utils'
57
- import { useUpdateEffect, useTransformStyle, useLayout, extendObject } from './utils'
57
+ import { useUpdateEffect, useTransformStyle, useLayout, extendObject, isIOS } from './utils'
58
58
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
59
59
  import useNodesRef, { HandlerRef } from './useNodesRef'
60
60
  import { FormContext, FormFieldValue, KeyboardAvoidContext } from './context'
@@ -121,11 +121,7 @@ const keyboardTypeMap: Record<Type, string> = {
121
121
  text: 'default',
122
122
  number: 'numeric',
123
123
  idcard: 'default',
124
- digit:
125
- Platform.select({
126
- ios: 'decimal-pad',
127
- android: 'numeric'
128
- }) || ''
124
+ digit: isIOS ? 'decimal-pad' : 'numeric'
129
125
  }
130
126
 
131
127
  const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps>((props: FinalInputProps, ref): JSX.Element => {
@@ -3,6 +3,7 @@ import { DimensionValue, EmitterSubscription, Keyboard, Platform, View, ViewStyl
3
3
  import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing, runOnJS } from 'react-native-reanimated'
4
4
  import { GestureDetector, Gesture } from 'react-native-gesture-handler'
5
5
  import { KeyboardAvoidContext } from './context'
6
+ import { isIOS } from './utils'
6
7
 
7
8
  type KeyboardAvoidViewProps = {
8
9
  children?: ReactNode
@@ -11,7 +12,6 @@ type KeyboardAvoidViewProps = {
11
12
  }
12
13
 
13
14
  const KeyboardAvoidingView = ({ children, style, contentContainerStyle }: KeyboardAvoidViewProps) => {
14
- const isIOS = Platform.OS === 'ios'
15
15
  const duration = isIOS ? 250 : 300
16
16
  const easing = isIOS ? Easing.inOut(Easing.ease) : Easing.out(Easing.quad)
17
17
 
@@ -1,86 +1,243 @@
1
- import { View, TouchableWithoutFeedback } from 'react-native'
2
- import { DatePicker } from '@ant-design/react-native'
3
- import React, { forwardRef, useState, useRef, useEffect } from 'react'
4
- import useNodesRef, { HandlerRef } from '../useNodesRef' // 引入辅助函数
5
- import { DateProps, LayoutType } from './type'
6
-
7
- function formatTimeStr (time = ''): Date {
8
- let [year, month, day]: any = time.split('-')
9
- year = ~~year || 2000
10
- month = ~~month || 1
11
- day = ~~day || 1
12
- return new Date(year, month - 1, day)
1
+ import React, { forwardRef, useCallback, useMemo, useRef, useState, useEffect, useImperativeHandle } from 'react'
2
+ import { StyleSheet, Text, View } from 'react-native'
3
+ import MpxPickerView from '../mpx-picker-view'
4
+ import MpxPickerViewColumn from '../mpx-picker-view-column'
5
+ import { DateProps, TimeValue } from './type'
6
+ import { HandlerRef } from '../useNodesRef'
7
+ import { useUpdateEffect } from '../utils'
8
+ import { years, months, daysInMonth, wrapDate, daysInMonthLength, START_YEAR, END_YEAR } from './dateData'
9
+
10
+ type FormatObj = {
11
+ indexArr: number[]
12
+ rangeArr: string[][]
13
+ nameArr?: string[]
13
14
  }
14
15
 
15
- function dateToString (date: Date, fields: 'day' | 'month' | 'year' = 'day'): string {
16
- const yyyy: string = date.getFullYear() + ''
17
- const MM: string = ('0' + (date.getMonth() + 1)).slice(-2)
18
- const dd: string = ('0' + date.getDate()).slice(-2)
19
- let ret: string = yyyy
20
- if (fields === 'month' || fields === 'day') {
21
- ret += `-${MM}`
22
- if (fields === 'day') {
23
- ret += `-${dd}`
24
- }
16
+ const START_DATE: TimeValue = `${START_YEAR}-01-01`
17
+ const END_DATE: TimeValue = `${END_YEAR}-12-31`
18
+ const START_DATE_ARR = [START_YEAR, 1, 1]
19
+ const END_DATE_ARR = [END_YEAR, 12, 31]
20
+
21
+ const styles = StyleSheet.create({
22
+ pickerContainer: {
23
+ height: 240,
24
+ paddingHorizontal: 10,
25
+ borderTopLeftRadius: 10,
26
+ borderTopRightRadius: 10
27
+ },
28
+ pickerIndicator: {
29
+ height: 45
30
+ },
31
+ pickerItem: {
32
+ fontSize: 16,
33
+ lineHeight: 45,
34
+ textAlign: 'center'
35
+ }
36
+ })
37
+
38
+ const getColumnLength = (fields: DateProps['fields'] = 'day') => {
39
+ return fields === 'year' ? 1 : fields === 'month' ? 2 : 3
40
+ }
41
+
42
+ const compareDateStr = (date1: TimeValue | number[], date2: TimeValue | number[]) => {
43
+ const [y1 = START_YEAR, m1 = 0, d1 = 0] = typeof date1 === 'string' ? date1.split('-').map(Number) : date1
44
+ const [y2 = START_YEAR, m2 = 0, d2 = 0] = typeof date2 === 'string' ? date2.split('-').map(Number) : date2
45
+ const num1 = y1 * 10000 + m1 * 100 + d1
46
+ const num2 = y2 * 10000 + m2 * 100 + d2
47
+ if (num1 === num2) {
48
+ return 0
25
49
  }
26
- return ret
50
+ return num1 > num2 ? 1 : -1
27
51
  }
28
52
 
29
- const _DatePicker = forwardRef<HandlerRef<View, DateProps>, DateProps>((props: DateProps, ref): React.JSX.Element => {
30
- const { children, start = '1970-01-01', end = '2999-01-01', value, bindchange, bindcancel, disabled, fields, style } = props
31
- const [datevalue, setDateValue] = useState(value)
32
- // 存储layout布局信息
33
- const layoutRef = useRef({})
34
- const viewRef = useRef<View>(null)
35
- const nodeRef = useRef<View>(null)
36
- useNodesRef<View, DateProps>(props, ref, nodeRef, {
37
- style
38
- })
53
+ const getDateArr = (date: TimeValue | number[]): number[] => {
54
+ const [y, m, d] = typeof date === 'string' ? date.split('-').map(Number) : date
55
+ return [y || 0, m || 0, d || 0]
56
+ }
39
57
 
40
- useEffect(() => {
41
- value && setDateValue(value)
42
- }, [value])
43
-
44
- const onChange = (date: Date): void => {
45
- const { fields = 'day' } = props
46
- const ret = dateToString(date, fields)
47
- setDateValue(ret)
48
- bindchange && bindchange({ detail: { value: ret } })
58
+ const calibrateDate = (date: TimeValue | number[], start: TimeValue, end: TimeValue): number[] => {
59
+ let startArr = getDateArr(start)
60
+ let endArr = getDateArr(end)
61
+ let dateArr = getDateArr(date)
62
+ if (compareDateStr(startArr, endArr) > 0) {
63
+ startArr = START_DATE_ARR
64
+ }
65
+ if (compareDateStr(endArr, startArr) < 0) {
66
+ endArr = END_DATE_ARR
67
+ }
68
+ if (compareDateStr(start, end) > 0) {
69
+ startArr = START_DATE_ARR
70
+ endArr = END_DATE_ARR
49
71
  }
72
+ if (compareDateStr(dateArr, endArr) > 0) {
73
+ dateArr = endArr
74
+ }
75
+ if (compareDateStr(dateArr, startArr) < 0) {
76
+ dateArr = startArr
77
+ }
78
+ return dateArr
79
+ }
50
80
 
51
- const onDismiss = (): void => {
52
- bindcancel && bindcancel()
81
+ const initDateStr2Arr = (dateStr: TimeValue | number[], start: TimeValue, end: TimeValue): number[] => {
82
+ if (!dateStr) {
83
+ const today = new Date()
84
+ const todayYear = today.getFullYear()
85
+ const todayMonth = today.getMonth() + 1
86
+ const todayDay = today.getDate()
87
+ dateStr = [todayYear, todayMonth, todayDay]
53
88
  }
89
+ const [y, m, d] = getDateArr(dateStr)
90
+ const year = Math.min(Math.max(START_YEAR, y), END_YEAR)
91
+ const month = Math.min(Math.max(1, m), 12)
92
+ const day = Math.min(Math.max(1, d), daysInMonthLength(year, month))
93
+ const res = [year, month, day]
94
+ return calibrateDate(res, start, end)
95
+ }
54
96
 
55
- const onElementLayout = (layout: LayoutType) => {
56
- viewRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => {
57
- layoutRef.current = { x, y, width, height, offsetLeft, offsetTop }
58
- props.getInnerLayout && props.getInnerLayout(layoutRef)
59
- })
97
+ const valueStr2Obj = (
98
+ _value: TimeValue | number[] = '', // eg: 2025-2-12
99
+ limit: number,
100
+ start: TimeValue,
101
+ end: TimeValue
102
+ ): FormatObj => {
103
+ const [y, m, d] = initDateStr2Arr(_value, start, end)
104
+ const ans = {
105
+ indexArr: [y - START_YEAR],
106
+ rangeArr: [years]
107
+ }
108
+ if (limit === 2) {
109
+ ans.indexArr.push(m - 1)
110
+ ans.rangeArr.push(months)
111
+ } else if (limit === 3) {
112
+ const days = daysInMonth(y, m)
113
+ ans.indexArr.push(m - 1, d - 1)
114
+ ans.rangeArr.push(months, days)
60
115
  }
116
+ return ans
117
+ }
118
+
119
+ const valueChanged2Obj = (currentObj: FormatObj, value: number[], limit = 3) => {
120
+ const currentValue = currentObj.indexArr
121
+ const rangeArr = currentObj.rangeArr
61
122
 
62
- const dateProps = {
63
- ref: nodeRef,
64
- precision: fields,
65
- value: formatTimeStr(datevalue),
66
- minDate: formatTimeStr(start),
67
- maxDate: formatTimeStr(end),
68
- onChange,
69
- onDismiss,
70
- disabled
123
+ if (limit === 3 && (currentValue[0] !== value[0] || currentValue[1] !== value[1])) {
124
+ const days = daysInMonth(value[0], value[1] + 1)
125
+ rangeArr[2] = days
126
+ const maxIndex = days.length - 1
127
+ if (value[2] > maxIndex) {
128
+ value[2] = maxIndex
129
+ }
71
130
  }
72
- const touchProps = {
73
- onLayout: onElementLayout,
74
- ref: viewRef
131
+
132
+ return {
133
+ indexArr: value,
134
+ rangeArr
75
135
  }
136
+ }
137
+
138
+ const valueChanged2Obj2 = (value: number[], limit = 3, start: TimeValue, end: TimeValue) => {
139
+ const y = value[0] + START_YEAR
140
+ const m = value[1] + 1
141
+ const d = value[2] + 1
142
+ return valueStr2Obj([y, m, d], limit, start, end)
143
+ }
144
+
145
+ const valueNum2String = (value: number[]) => {
146
+ return value.map((item, index) => {
147
+ if (index === 0) {
148
+ return item + START_YEAR
149
+ } else {
150
+ return wrapDate()(item + 1)
151
+ }
152
+ }).join('-')
153
+ }
154
+
155
+ const hasDiff = (currentValue: number[], value: number[], limit = 3) => {
156
+ for (let i = 0; i < limit; i++) {
157
+ if (currentValue[i] !== value[i]) {
158
+ return true
159
+ }
160
+ }
161
+ return false
162
+ }
163
+
164
+ const PickerDate = forwardRef<
165
+ HandlerRef<View, DateProps>,
166
+ DateProps
167
+ >((props: DateProps, ref): React.JSX.Element => {
168
+ const { value = '', start = START_DATE, end = END_DATE, fields, bindchange } = props
169
+ const nodeRef = useRef(null)
170
+ const columnLength = useMemo(() => getColumnLength(fields), [fields])
171
+ const [formatObj, setFormatObj] = useState<FormatObj>(valueStr2Obj(value, columnLength, start, end))
172
+ const timerRef = useRef<NodeJS.Timeout | null>(null)
173
+
174
+ useEffect(() => {
175
+ return () => {
176
+ timerRef.current && clearTimeout(timerRef.current)
177
+ }
178
+ }, [])
179
+
180
+ useUpdateEffect(() => {
181
+ const calibratedValue = valueStr2Obj(value, columnLength, start, end)
182
+ setFormatObj(calibratedValue)
183
+ }, [value, columnLength, start, end])
184
+
185
+ const updateValue = useCallback((value: TimeValue = '') => {
186
+ const calibratedValue = valueStr2Obj(value, columnLength, start, end)
187
+ setFormatObj(calibratedValue)
188
+ }, [columnLength, start, end])
189
+
190
+ const _props = useRef(props)
191
+ _props.current = props
192
+ useImperativeHandle(ref, () => ({
193
+ updateValue,
194
+ getNodeInstance: () => ({
195
+ props: _props,
196
+ nodeRef,
197
+ instance: {
198
+ style: {}
199
+ }
200
+ })
201
+ }))
202
+
203
+ const onChange = useCallback((e: { detail: { value: number[] } }) => {
204
+ const { value } = e.detail
205
+ const currentValue = formatObj.indexArr
206
+ const newObj = valueChanged2Obj(formatObj, value, columnLength)
207
+ if (hasDiff(currentValue, value, columnLength)) {
208
+ setFormatObj(newObj)
209
+ const newObj2 = valueChanged2Obj2(value, columnLength, start, end)
210
+ if (hasDiff(newObj.indexArr, newObj2.indexArr, columnLength)) {
211
+ timerRef.current && clearTimeout(timerRef.current)
212
+ timerRef.current = setTimeout(() => setFormatObj(newObj2))
213
+ }
214
+ }
215
+ bindchange?.({ detail: { value: valueNum2String(newObj.indexArr) } })
216
+ }, [formatObj, columnLength, bindchange, start, end])
217
+
218
+ const renderColumn = () => {
219
+ return formatObj.rangeArr?.map((item, index) => (
220
+ // @ts-expect-error ignore
221
+ <MpxPickerViewColumn key={index}>
222
+ {item.map((item, index) => {
223
+ return <Text key={index} style={styles.pickerItem}>
224
+ {item}
225
+ </Text>
226
+ })}
227
+ </MpxPickerViewColumn>
228
+ ))
229
+ }
230
+
76
231
  return (
77
- <DatePicker {...dateProps}>
78
- <TouchableWithoutFeedback>
79
- <View {...touchProps}>{children}</View>
80
- </TouchableWithoutFeedback>
81
- </DatePicker>
82
- )
232
+ <MpxPickerView
233
+ style={styles.pickerContainer}
234
+ indicator-style={styles.pickerIndicator}
235
+ value={formatObj.indexArr}
236
+ bindchange={onChange}
237
+ >
238
+ {renderColumn()}
239
+ </MpxPickerView>)
83
240
  })
84
241
 
85
- _DatePicker.displayName = 'mpx-picker-date'
86
- export default _DatePicker
242
+ PickerDate.displayName = 'MpxPickerDate'
243
+ export default PickerDate
@@ -0,0 +1,22 @@
1
+ export const wrapDate = (union = '') => (num: number) => String(num).padStart(2, '0') + union
2
+
3
+ export const START_YEAR = 1900
4
+ export const END_YEAR = 2099
5
+
6
+ export const years = Array.from({ length: 200 }, (_, index) => index + START_YEAR + '年')
7
+
8
+ export const months = Array.from({ length: 12 }, (_, index) => index + 1).map(wrapDate('月'))
9
+
10
+ export const daysInMonthLength = (year: number, month: number) => {
11
+ return month === 2
12
+ ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
13
+ ? 29
14
+ : 28
15
+ : [4, 6, 9, 11].includes(month)
16
+ ? 30
17
+ : 31
18
+ }
19
+
20
+ export const daysInMonth = (year: number, month: number) => {
21
+ return Array.from({ length: daysInMonthLength(year, month) }, (_, index) => index + 1).map(wrapDate('日'))
22
+ }