@mpxjs/webpack-plugin 2.9.59 → 2.9.62

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 (106) hide show
  1. package/lib/platform/style/wx/index.js +314 -254
  2. package/lib/platform/template/wx/component-config/checkbox-group.js +8 -0
  3. package/lib/platform/template/wx/component-config/checkbox.js +8 -0
  4. package/lib/platform/template/wx/component-config/cover-image.js +15 -0
  5. package/lib/platform/template/wx/component-config/cover-view.js +9 -0
  6. package/lib/platform/template/wx/component-config/form.js +13 -1
  7. package/lib/platform/template/wx/component-config/icon.js +8 -0
  8. package/lib/platform/template/wx/component-config/index.js +5 -1
  9. package/lib/platform/template/wx/component-config/label.js +15 -0
  10. package/lib/platform/template/wx/component-config/movable-area.js +18 -1
  11. package/lib/platform/template/wx/component-config/movable-view.js +18 -1
  12. package/lib/platform/template/wx/component-config/navigator.js +8 -0
  13. package/lib/platform/template/wx/component-config/picker-view-column.js +8 -0
  14. package/lib/platform/template/wx/component-config/picker-view.js +18 -2
  15. package/lib/platform/template/wx/component-config/picker.js +14 -1
  16. package/lib/platform/template/wx/component-config/radio-group.js +8 -0
  17. package/lib/platform/template/wx/component-config/radio.js +8 -0
  18. package/lib/platform/template/wx/component-config/root-portal.js +15 -0
  19. package/lib/platform/template/wx/component-config/switch.js +8 -0
  20. package/lib/platform/template/wx/component-config/unsupported.js +1 -3
  21. package/lib/react/processScript.js +2 -0
  22. package/lib/runtime/components/react/context.ts +38 -0
  23. package/lib/runtime/components/react/dist/context.js +7 -0
  24. package/lib/runtime/components/react/dist/getInnerListeners.js +22 -11
  25. package/lib/runtime/components/react/dist/mpx-button.jsx +67 -45
  26. package/lib/runtime/components/react/dist/mpx-checkbox-group.jsx +81 -0
  27. package/lib/runtime/components/react/dist/mpx-checkbox.jsx +152 -0
  28. package/lib/runtime/components/react/dist/mpx-form.jsx +59 -0
  29. package/lib/runtime/components/react/dist/mpx-icon.jsx +51 -0
  30. package/lib/runtime/components/react/dist/mpx-image/index.jsx +17 -22
  31. package/lib/runtime/components/react/dist/mpx-image/svg.jsx +0 -1
  32. package/lib/runtime/components/react/dist/mpx-input.jsx +38 -16
  33. package/lib/runtime/components/react/dist/mpx-label.jsx +63 -0
  34. package/lib/runtime/components/react/dist/mpx-movable-area.jsx +46 -0
  35. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +346 -0
  36. package/lib/runtime/components/react/dist/mpx-navigator.jsx +35 -0
  37. package/lib/runtime/components/react/dist/mpx-picker/date.jsx +69 -0
  38. package/lib/runtime/components/react/dist/mpx-picker/index.jsx +138 -0
  39. package/lib/runtime/components/react/dist/mpx-picker/multiSelector.jsx +139 -0
  40. package/lib/runtime/components/react/dist/mpx-picker/region.jsx +90 -0
  41. package/lib/runtime/components/react/dist/mpx-picker/regionData.js +6099 -0
  42. package/lib/runtime/components/react/dist/mpx-picker/selector.jsx +76 -0
  43. package/lib/runtime/components/react/dist/mpx-picker/time.jsx +244 -0
  44. package/lib/runtime/components/react/dist/mpx-picker/type.js +1 -0
  45. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +15 -0
  46. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +68 -0
  47. package/lib/runtime/components/react/dist/mpx-radio-group.jsx +79 -0
  48. package/lib/runtime/components/react/dist/mpx-radio.jsx +169 -0
  49. package/lib/runtime/components/react/dist/mpx-root-portal.jsx +11 -0
  50. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +66 -50
  51. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +206 -147
  52. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +9 -7
  53. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +3 -3
  54. package/lib/runtime/components/react/dist/mpx-switch.jsx +76 -0
  55. package/lib/runtime/components/react/dist/mpx-text.jsx +7 -19
  56. package/lib/runtime/components/react/dist/mpx-textarea.jsx +1 -1
  57. package/lib/runtime/components/react/dist/mpx-view.jsx +326 -96
  58. package/lib/runtime/components/react/dist/mpx-web-view.jsx +9 -15
  59. package/lib/runtime/components/react/dist/types/common.js +1 -0
  60. package/lib/runtime/components/react/dist/useNodesRef.js +3 -8
  61. package/lib/runtime/components/react/dist/utils.js +82 -14
  62. package/lib/runtime/components/react/getInnerListeners.ts +25 -13
  63. package/lib/runtime/components/react/mpx-button.tsx +87 -67
  64. package/lib/runtime/components/react/mpx-checkbox-group.tsx +147 -0
  65. package/lib/runtime/components/react/mpx-checkbox.tsx +245 -0
  66. package/lib/runtime/components/react/mpx-form.tsx +89 -0
  67. package/lib/runtime/components/react/mpx-icon.tsx +103 -0
  68. package/lib/runtime/components/react/mpx-image/index.tsx +20 -32
  69. package/lib/runtime/components/react/mpx-image/svg.tsx +2 -2
  70. package/lib/runtime/components/react/mpx-input.tsx +54 -26
  71. package/lib/runtime/components/react/mpx-label.tsx +115 -0
  72. package/lib/runtime/components/react/mpx-movable-area.tsx +67 -0
  73. package/lib/runtime/components/react/mpx-movable-view.tsx +425 -0
  74. package/lib/runtime/components/react/mpx-navigator.tsx +67 -0
  75. package/lib/runtime/components/react/mpx-picker/date.tsx +83 -0
  76. package/lib/runtime/components/react/mpx-picker/index.tsx +155 -0
  77. package/lib/runtime/components/react/mpx-picker/multiSelector.tsx +153 -0
  78. package/lib/runtime/components/react/mpx-picker/region.tsx +104 -0
  79. package/lib/runtime/components/react/mpx-picker/regionData.ts +6101 -0
  80. package/lib/runtime/components/react/mpx-picker/selector.tsx +92 -0
  81. package/lib/runtime/components/react/mpx-picker/time.tsx +274 -0
  82. package/lib/runtime/components/react/mpx-picker/type.ts +107 -0
  83. package/lib/runtime/components/react/mpx-picker-view-column.tsx +28 -0
  84. package/lib/runtime/components/react/mpx-picker-view.tsx +104 -0
  85. package/lib/runtime/components/react/mpx-radio-group.tsx +147 -0
  86. package/lib/runtime/components/react/mpx-radio.tsx +246 -0
  87. package/lib/runtime/components/react/mpx-root-portal.tsx +25 -0
  88. package/lib/runtime/components/react/mpx-scroll-view.tsx +82 -58
  89. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +203 -156
  90. package/lib/runtime/components/react/mpx-swiper/index.tsx +12 -13
  91. package/lib/runtime/components/react/mpx-swiper/type.ts +11 -4
  92. package/lib/runtime/components/react/mpx-swiper-item.tsx +5 -3
  93. package/lib/runtime/components/react/mpx-switch.tsx +127 -0
  94. package/lib/runtime/components/react/mpx-text.tsx +52 -68
  95. package/lib/runtime/components/react/mpx-textarea.tsx +2 -2
  96. package/lib/runtime/components/react/mpx-view.tsx +373 -140
  97. package/lib/runtime/components/react/mpx-web-view.tsx +24 -28
  98. package/lib/runtime/components/react/types/common.ts +12 -0
  99. package/lib/runtime/components/react/types/getInnerListeners.ts +2 -1
  100. package/lib/runtime/components/react/types/global.d.ts +4 -0
  101. package/lib/runtime/components/react/useNodesRef.ts +3 -8
  102. package/lib/runtime/components/react/utils.ts +93 -15
  103. package/lib/runtime/optionProcessor.js +19 -17
  104. package/lib/template-compiler/compiler.js +56 -41
  105. package/lib/template-compiler/gen-node-react.js +7 -7
  106. package/package.json +6 -3
@@ -1,13 +1,8 @@
1
- import { useRef, useEffect, useImperativeHandle } from 'react';
1
+ import { useRef, useImperativeHandle } from 'react';
2
2
  export default function useNodesRef(props, ref, instance = {}) {
3
3
  const nodeRef = useRef(null);
4
- const _props = useRef(props);
5
- useEffect(() => {
6
- _props.current = props;
7
- return () => {
8
- _props.current = null; // 组件销毁,清空 _props 依赖数据
9
- };
10
- }, [props]);
4
+ const _props = useRef(null);
5
+ _props.current = props;
11
6
  useImperativeHandle(ref, () => {
12
7
  return {
13
8
  getNodeInstance() {
@@ -1,7 +1,11 @@
1
- import { useEffect, useRef, Children, isValidElement } from 'react';
2
- import { StyleSheet } from 'react-native';
1
+ import { useEffect, useRef, isValidElement } from 'react';
3
2
  export const TEXT_STYLE_REGEX = /color|font.*|text.*|letterSpacing|lineHeight|includeFontPadding|writingDirection/;
4
3
  export const PERCENT_REGEX = /^\s*-?\d+(\.\d+)?%\s*$/;
4
+ export const IMAGE_STYLE_REGEX = /^background(Image|Size|Repeat|Position)$/;
5
+ export const TEXT_PROPS_REGEX = /ellipsizeMode|numberOfLines/;
6
+ export const DEFAULT_STYLE = {
7
+ fontSize: 16
8
+ };
5
9
  const URL_REGEX = /url\(["']?(.*?)["']?\)/;
6
10
  export function omit(obj, fields) {
7
11
  const shallowCopy = Object.assign({}, obj);
@@ -11,17 +15,6 @@ export function omit(obj, fields) {
11
15
  }
12
16
  return shallowCopy;
13
17
  }
14
- /**
15
- * 从 style 中提取 TextStyle
16
- * @param style
17
- * @returns
18
- */
19
- export const extractTextStyle = (style) => {
20
- return Object.entries(StyleSheet.flatten(style)).reduce((textStyle, [key, value]) => {
21
- TEXT_STYLE_REGEX.test(key) && Object.assign(textStyle, { [key]: value });
22
- return textStyle;
23
- }, {});
24
- };
25
18
  /**
26
19
  * 用法等同于 useEffect,但是会忽略首次执行,只在依赖更新时执行
27
20
  */
@@ -75,6 +68,81 @@ export const isText = (ele) => {
75
68
  }
76
69
  return false;
77
70
  };
71
+ export const isEmbedded = (ele) => {
72
+ if (isValidElement(ele)) {
73
+ const displayName = ele.type?.displayName;
74
+ return displayName && ['mpx-checkbox', 'mpx-radio', 'mpx-switch'].includes(displayName);
75
+ }
76
+ return false;
77
+ };
78
78
  export function every(children, callback) {
79
- return Children.toArray(children).every((child) => callback(child));
79
+ const childrenArray = Array.isArray(children) ? children : [children];
80
+ return childrenArray.every((child) => callback(child));
81
+ }
82
+ export function groupBy(obj, callback, group = {}) {
83
+ let groupKey = '';
84
+ for (let key in obj) {
85
+ if (obj.hasOwnProperty(key)) { // 确保处理对象自身的属性
86
+ let val = obj[key];
87
+ groupKey = callback(key, val);
88
+ if (!group[groupKey]) {
89
+ group[groupKey] = {};
90
+ }
91
+ group[groupKey][key] = val;
92
+ }
93
+ }
94
+ return group;
80
95
  }
96
+ export const normalizeStyle = (style = {}) => {
97
+ const { borderRadius } = style;
98
+ if (borderRadius && PERCENT_REGEX.test(borderRadius)) {
99
+ style.borderTopLeftRadius = borderRadius;
100
+ style.borderBottomLeftRadius = borderRadius;
101
+ style.borderBottomRightRadius = borderRadius;
102
+ style.borderTopRightRadius = borderRadius;
103
+ delete style.borderRadius;
104
+ }
105
+ ['backgroundSize', 'backgroundPosition'].forEach(name => {
106
+ if (style[name] && typeof style[name] === 'string') {
107
+ if (style[name].trim()) {
108
+ style[name] = style[name].split(' ');
109
+ }
110
+ }
111
+ });
112
+ return style;
113
+ };
114
+ export function splitStyle(styles) {
115
+ return groupBy(styles, (key) => {
116
+ if (TEXT_STYLE_REGEX.test(key)) {
117
+ return 'textStyle';
118
+ }
119
+ else if (IMAGE_STYLE_REGEX.test(key)) {
120
+ return 'imageStyle';
121
+ }
122
+ else {
123
+ return 'innerStyle';
124
+ }
125
+ }, {});
126
+ }
127
+ export function splitProps(props) {
128
+ return groupBy(props, (key) => {
129
+ if (TEXT_PROPS_REGEX.test(key)) {
130
+ return 'textProps';
131
+ }
132
+ else {
133
+ return 'innerProps';
134
+ }
135
+ }, {});
136
+ }
137
+ export const throwReactWarning = (message) => {
138
+ setTimeout(() => {
139
+ console.warn(message);
140
+ }, 0);
141
+ };
142
+ export const transformTextStyle = (styleObj) => {
143
+ let { lineHeight } = styleObj;
144
+ if (typeof lineHeight === 'string' && PERCENT_REGEX.test(lineHeight)) {
145
+ lineHeight = (parseFloat(lineHeight) / 100) * (styleObj.fontSize || DEFAULT_STYLE.fontSize);
146
+ styleObj.lineHeight = lineHeight;
147
+ }
148
+ };
@@ -97,8 +97,10 @@ export const getCustomEvent = (
97
97
  dataset: getDataSet(props),
98
98
  offsetLeft: layoutRef?.current?.offsetLeft || 0,
99
99
  offsetTop: layoutRef?.current?.offsetTop || 0
100
- }
101
-
100
+ },
101
+ persist: oe.persist,
102
+ stopPropagation: oe.stopPropagation,
103
+ preventDefault: oe.preventDefault
102
104
  }
103
105
  }
104
106
 
@@ -106,7 +108,7 @@ const useInnerProps = (
106
108
  props: Props = {},
107
109
  additionalProps: AdditionalProps = {},
108
110
  removeProps: RemoveProps = [],
109
- rawConfig: UseInnerPropsConfig
111
+ rawConfig?: UseInnerPropsConfig
110
112
  ) => {
111
113
  const ref = useRef<InnerRef>({
112
114
  startTimer: {
@@ -127,7 +129,7 @@ const useInnerProps = (
127
129
 
128
130
  const propsRef = useRef<Record<string, any>>({})
129
131
  const eventConfig: { [key: string]: string[] } = {}
130
- const config = rawConfig || {}
132
+ const config = rawConfig || { layoutRef: { current: {} }, disableTouch: false, disableTap: false }
131
133
 
132
134
  propsRef.current = { ...props, ...additionalProps }
133
135
 
@@ -156,6 +158,19 @@ const useInnerProps = (
156
158
  }
157
159
  })
158
160
  }
161
+
162
+ function checkIsNeedPress (e: NativeTouchEvent, type: 'bubble' | 'capture') {
163
+ const tapDetailInfo = ref.current.mpxPressInfo.detail || { x: 0, y: 0 }
164
+ const nativeEvent = e.nativeEvent
165
+ const currentPageX = nativeEvent.changedTouches[0].pageX
166
+ const currentPageY = nativeEvent.changedTouches[0].pageY
167
+ if (Math.abs(currentPageX - tapDetailInfo.x) > 1 || Math.abs(currentPageY - tapDetailInfo.y) > 1) {
168
+ ref.current.needPress[type] = false
169
+ ref.current.startTimer[type] && clearTimeout(ref.current.startTimer[type] as SetTimeoutReturnType)
170
+ ref.current.startTimer[type] = null
171
+ }
172
+ }
173
+
159
174
  function handleTouchstart(e: NativeTouchEvent, type: 'bubble' | 'capture') {
160
175
  e.persist()
161
176
  const bubbleTouchEvent = ['catchtouchstart', 'bindtouchstart']
@@ -184,20 +199,14 @@ const useInnerProps = (
184
199
  function handleTouchmove(e: NativeTouchEvent, type: 'bubble' | 'capture') {
185
200
  const bubbleTouchEvent = ['catchtouchmove', 'bindtouchmove']
186
201
  const captureTouchEvent = ['capture-catchtouchmove', 'capture-bindtouchmove']
187
- const tapDetailInfo = ref.current.mpxPressInfo.detail || { x: 0, y: 0 }
188
- const nativeEvent = e.nativeEvent
189
- const currentPageX = nativeEvent.changedTouches[0].pageX
190
- const currentPageY = nativeEvent.changedTouches[0].pageY
191
202
  const currentTouchEvent = type === 'bubble' ? bubbleTouchEvent : captureTouchEvent
192
- if (Math.abs(currentPageX - tapDetailInfo.x) > 1 || Math.abs(currentPageY - tapDetailInfo.y) > 1) {
193
- ref.current.needPress[type] = false
194
- ref.current.startTimer[type] && clearTimeout(ref.current.startTimer[type] as SetTimeoutReturnType)
195
- ref.current.startTimer[type] = null
196
- }
197
203
  handleEmitEvent(currentTouchEvent, 'touchmove', e)
204
+ checkIsNeedPress(e, type)
198
205
  }
199
206
 
200
207
  function handleTouchend(e: NativeTouchEvent, type: 'bubble' | 'capture') {
208
+ // move event may not be triggered
209
+ checkIsNeedPress(e, type)
201
210
  const bubbleTouchEvent = ['catchtouchend', 'bindtouchend']
202
211
  const bubbleTapEvent = ['catchtap', 'bindtap']
203
212
  const captureTouchEvent = ['capture-catchtouchend', 'capture-bindtouchend']
@@ -208,6 +217,9 @@ const useInnerProps = (
208
217
  ref.current.startTimer[type] = null
209
218
  handleEmitEvent(currentTouchEvent, 'touchend', e)
210
219
  if (ref.current.needPress[type]) {
220
+ if (type === 'bubble' && config.disableTap) {
221
+ return
222
+ }
211
223
  handleEmitEvent(currentTapEvent, 'tap', e)
212
224
  }
213
225
  }
@@ -4,7 +4,7 @@
4
4
  * ✔ plain
5
5
  * ✔ disabled
6
6
  * ✔ loading
7
- * form-type
7
+ * form-type
8
8
  * - open-type: Partially. Only support `share`、`getUserInfo`
9
9
  * ✔ hover-class: Convert hoverClass to hoverStyle.
10
10
  * ✔ hover-style
@@ -34,27 +34,23 @@
34
34
  * ✘ bindagreeprivacyauthorization
35
35
  * ✔ bindtap
36
36
  */
37
- import React, {
38
- useEffect,
39
- useRef,
40
- useState,
41
- ReactNode,
42
- forwardRef,
43
- } from 'react'
37
+ import { useEffect, useRef, useState, ReactNode, forwardRef, useContext, JSX } from 'react'
38
+
44
39
  import {
45
40
  View,
46
41
  Text,
47
42
  StyleSheet,
48
- StyleProp,
49
43
  ViewStyle,
50
44
  TextStyle,
51
45
  Animated,
52
46
  Easing,
53
47
  NativeSyntheticEvent,
54
48
  } from 'react-native'
55
- import { extractTextStyle, isText, every } from './utils'
49
+ import { splitStyle, isText, every, splitProps, throwReactWarning, transformTextStyle } from './utils'
56
50
  import useInnerProps, { getCustomEvent } from './getInnerListeners'
57
51
  import useNodesRef, { HandlerRef } from './useNodesRef'
52
+ import { FormContext } from './context'
53
+ import { isEmptyObject } from '@mpxjs/utils'
58
54
 
59
55
  export type Type = 'default' | 'primary' | 'warn'
60
56
 
@@ -74,16 +70,16 @@ export interface ButtonProps {
74
70
  disabled?: boolean
75
71
  loading?: boolean
76
72
  'hover-class'?: string
77
- 'hover-style'?: StyleProp<ViewStyle & TextStyle>
73
+ 'hover-style'?: ViewStyle & TextStyle & Record<string, any>
78
74
  'hover-start-time'?: number
79
75
  'hover-stay-time'?: number
80
76
  'open-type'?: OpenType
77
+ 'form-type'?: 'submit' | 'reset'
81
78
  'enable-offset'?: boolean,
82
- style?: StyleProp<ViewStyle & TextStyle>
79
+ style?: ViewStyle & TextStyle & Record<string, any>
83
80
  children: ReactNode
84
81
  bindgetuserinfo?: (userInfo: any) => void
85
82
  bindtap?: (evt: NativeSyntheticEvent<TouchEvent> | unknown) => void
86
- catchtap?: (evt: NativeSyntheticEvent<TouchEvent> | unknown) => void
87
83
  bindtouchstart?: (evt: NativeSyntheticEvent<TouchEvent> | unknown) => void
88
84
  bindtouchend?: (evt: NativeSyntheticEvent<TouchEvent> | unknown) => void
89
85
  }
@@ -132,27 +128,27 @@ const styles = StyleSheet.create({
132
128
  const getOpenTypeEvent = (openType: OpenType) => {
133
129
  // @ts-ignore
134
130
  if (!global?.__mpx?.config?.rnConfig) {
135
- console.warn('Environment not supported')
131
+ throwReactWarning('[Mpx runtime warn]: Environment not supported')
136
132
  return
137
133
  }
138
134
 
139
135
  const eventName = OpenTypeEventsMap.get(openType)
140
136
  if (!eventName) {
141
- console.warn(`open-type not support ${openType}`)
137
+ throwReactWarning(`[Mpx runtime warn]: open-type not support ${openType}`)
142
138
  return
143
139
  }
144
140
 
145
141
  // @ts-ignore
146
142
  const event = global.__mpx.config.rnConfig?.openTypeHandler?.[eventName]
147
143
  if (!event) {
148
- console.warn(`Unregistered ${eventName} event`)
144
+ throwReactWarning(`[Mpx runtime warn]: Unregistered ${eventName} event`)
149
145
  return
150
146
  }
151
147
 
152
148
  return event
153
149
  }
154
150
 
155
- const Loading = ({ alone = false }: { alone: boolean }): React.JSX.Element => {
151
+ const Loading = ({ alone = false }: { alone: boolean }): JSX.Element => {
156
152
  const image = useRef(new Animated.Value(0)).current
157
153
 
158
154
  const rotate = image.interpolate({
@@ -188,7 +184,7 @@ const Loading = ({ alone = false }: { alone: boolean }): React.JSX.Element => {
188
184
  return <Animated.Image testID="loading" style={loadingStyle} source={{ uri: LOADING_IMAGE_URI }} />
189
185
  }
190
186
 
191
- const Button = forwardRef<HandlerRef< View, ButtonProps>,ButtonProps >((props, ref): React.JSX.Element => {
187
+ const Button = forwardRef<HandlerRef<View, ButtonProps>, ButtonProps>((props, ref): JSX.Element => {
192
188
  const {
193
189
  size = 'default',
194
190
  type = 'default',
@@ -196,20 +192,31 @@ const Button = forwardRef<HandlerRef< View, ButtonProps>,ButtonProps >((props, r
196
192
  disabled = false,
197
193
  loading = false,
198
194
  'hover-class': hoverClass,
199
- 'hover-style': hoverStyle = [],
195
+ 'hover-style': hoverStyle = {},
200
196
  'hover-start-time': hoverStartTime = 20,
201
197
  'hover-stay-time': hoverStayTime = 70,
202
198
  'open-type': openType,
203
199
  'enable-offset': enableOffset,
204
- style = [],
200
+ 'form-type': formType,
201
+ style = {},
205
202
  children,
206
203
  bindgetuserinfo,
207
204
  bindtap,
208
- catchtap,
209
205
  bindtouchstart,
210
206
  bindtouchend,
211
207
  } = props
212
208
 
209
+
210
+ const formContext = useContext(FormContext)
211
+
212
+ let submitFn: () => void | undefined;
213
+ let resetFn: () => void | undefined;
214
+
215
+ if (formContext) {
216
+ submitFn = formContext.submit
217
+ resetFn = formContext.reset
218
+ }
219
+
213
220
  const refs = useRef<{
214
221
  hoverStartTimer: ReturnType<typeof setTimeout> | undefined
215
222
  hoverStayTimer: ReturnType<typeof setTimeout> | undefined
@@ -226,9 +233,13 @@ const Button = forwardRef<HandlerRef< View, ButtonProps>,ButtonProps >((props, r
226
233
 
227
234
  const applyHoverEffect = isHover && hoverClass !== 'none'
228
235
 
229
- const inheritTextStyle = extractTextStyle(style)
236
+ const { textStyle, imageStyle, innerStyle } = splitStyle(style)
237
+
238
+ const { textStyle: hoverTextStyle, imageStyle: hoverImageStyle, innerStyle: hoverInnerStyle } = splitStyle(hoverStyle)
230
239
 
231
- const textHoverStyle = extractTextStyle(hoverStyle)
240
+ if (imageStyle || hoverImageStyle) {
241
+ throwReactWarning('[Mpx runtime warn]: Button does not support background image-related styles!')
242
+ }
232
243
 
233
244
  const [color, hoverColor, plainColor, disabledColor] = TypeColorMap[type]
234
245
 
@@ -237,16 +248,16 @@ const Button = forwardRef<HandlerRef< View, ButtonProps>,ButtonProps >((props, r
237
248
  const plainBorderColor = disabled
238
249
  ? 'rgba(0, 0, 0, .2)'
239
250
  : applyHoverEffect
240
- ? `rgba(${plainColor},.6)`
241
- : `rgb(${plainColor})`
251
+ ? `rgba(${plainColor},.6)`
252
+ : `rgb(${plainColor})`
242
253
 
243
254
  const normalBorderColor = type === 'default' ? 'rgba(0, 0, 0, .2)' : normalBackgroundColor
244
255
 
245
256
  const plainTextColor = disabled
246
257
  ? 'rgba(0, 0, 0, .2)'
247
258
  : applyHoverEffect
248
- ? `rgba(${plainColor}, .6)`
249
- : `rgb(${plainColor})`
259
+ ? `rgba(${plainColor}, .6)`
260
+ : `rgb(${plainColor})`
250
261
 
251
262
  const normalTextColor =
252
263
  type === 'default'
@@ -260,20 +271,17 @@ const Button = forwardRef<HandlerRef< View, ButtonProps>,ButtonProps >((props, r
260
271
  backgroundColor: plain ? 'transparent' : normalBackgroundColor,
261
272
  }
262
273
 
263
- const textStyle = {
264
- color: plain ? plainTextColor : normalTextColor,
265
- ...inheritTextStyle
274
+ const defaultViewStyle = {
275
+ ...styles.button,
276
+ ...(isMiniSize && styles.buttonMini),
277
+ ...viewStyle,
266
278
  }
267
279
 
268
- const defaultViewStyle = [
269
- styles.button,
270
- isMiniSize && styles.buttonMini || {},
271
- viewStyle,
272
- ]
273
-
274
- const defaultTextStyle = [
275
- styles.text, isMiniSize && styles.textMini, textStyle
276
- ]
280
+ const defaultTextStyle = {
281
+ ...styles.text,
282
+ ...(isMiniSize && styles.textMini),
283
+ color: plain ? plainTextColor : normalTextColor,
284
+ }
277
285
 
278
286
  const handleOpenTypeEvent = (evt: NativeSyntheticEvent<TouchEvent>) => {
279
287
  if (!openType) return
@@ -286,11 +294,13 @@ const Button = forwardRef<HandlerRef< View, ButtonProps>,ButtonProps >((props, r
286
294
  })
287
295
  }
288
296
 
289
- if (openType === 'getUserInfo') {
290
- const userInfo = handleEvent && handleEvent()
291
- if (typeof userInfo === 'object') {
292
- bindgetuserinfo && bindgetuserinfo(userInfo)
293
- }
297
+ if (openType === 'getUserInfo' && handleEvent && bindgetuserinfo) {
298
+ Promise.resolve(handleEvent)
299
+ .then((userInfo) => {
300
+ if (typeof userInfo === 'object') {
301
+ bindgetuserinfo(userInfo)
302
+ }
303
+ })
294
304
  }
295
305
  }
296
306
 
@@ -322,32 +332,41 @@ const Button = forwardRef<HandlerRef< View, ButtonProps>,ButtonProps >((props, r
322
332
  setStayTimer()
323
333
  }
324
334
 
335
+ const handleFormTypeFn = () => {
336
+ if (formType === 'submit') {
337
+ submitFn && submitFn()
338
+ } else if (formType === 'reset') {
339
+ resetFn && resetFn()
340
+ }
341
+ }
325
342
  const onTap = (evt: NativeSyntheticEvent<TouchEvent>) => {
326
343
  if (disabled) return
327
344
  bindtap && bindtap(getCustomEvent('tap', evt, { layoutRef }, props))
328
345
  handleOpenTypeEvent(evt)
346
+ handleFormTypeFn()
329
347
  }
330
348
 
331
- const catchTap = (evt: NativeSyntheticEvent<TouchEvent>) => {
332
- if (disabled) return
333
- catchtap && catchtap(getCustomEvent('tap', evt, { layoutRef }, props))
334
- }
349
+ function wrapChildren(children: ReactNode, defaultTextStyle: Record<string, any>, textStyle: Record<string, any>) {
350
+ if (!children) return children
351
+ const hasTextStyle = !isEmptyObject(textStyle)
352
+ const { textProps } = splitProps(props)
335
353
 
336
- function wrapChildren(children: ReactNode, textStyle?: StyleProp<TextStyle>) {
337
- if (every(children, (child)=>isText(child))) {
338
- children = [<Text key='buttonTextWrap' style={textStyle}>{children}</Text>]
354
+ if (every(children, (child) => isText(child))) {
355
+ transformTextStyle(textStyle as TextStyle)
356
+ children = <Text key='buttonTextWrap' style={{ ...defaultTextStyle, ...textStyle }} {...(textProps || {})}>{children}</Text>
339
357
  } else {
340
- if(textStyle) console.warn('Text style will be ignored unless every child of the Button is Text node!')
358
+ if (hasTextStyle) throwReactWarning('[Mpx runtime warn]: Text style will be ignored unless every child of the Button is Text node!')
341
359
  }
342
-
360
+
343
361
  return children
344
362
  }
345
363
 
346
364
  const { nodeRef } = useNodesRef(props, ref, {
347
- defaultStyle: StyleSheet.flatten([
365
+ defaultStyle: {
348
366
  ...defaultViewStyle,
349
367
  ...defaultTextStyle,
350
- ])
368
+ ...textStyle
369
+ }
351
370
  })
352
371
 
353
372
  const onLayout = () => {
@@ -363,33 +382,34 @@ const Button = forwardRef<HandlerRef< View, ButtonProps>,ButtonProps >((props, r
363
382
  bindtouchstart: onTouchStart,
364
383
  bindtouchend: onTouchEnd,
365
384
  bindtap: onTap,
366
- catchtap: catchTap,
367
385
  ...(enableOffset ? { onLayout } : {}),
368
386
  },
369
387
  [
370
388
  'enable-offset'
371
389
  ],
372
- {
373
- layoutRef
390
+ {
391
+ layoutRef,
392
+ disableTap: disabled
374
393
  }
375
394
  );
376
395
 
377
396
  return (
378
397
  <View
379
398
  {...innerProps}
380
- style={[
399
+ style={{
381
400
  ...defaultViewStyle,
382
- style,
383
- applyHoverEffect && hoverStyle,
384
- ]}>
401
+ ...innerStyle,
402
+ ...(applyHoverEffect && hoverInnerStyle),
403
+ } as ViewStyle}>
385
404
  {loading && <Loading alone={!children} />}
386
405
  {
387
406
  wrapChildren(
388
- children,
389
- [
390
- ...defaultTextStyle,
391
- applyHoverEffect && textHoverStyle,
392
- ]
407
+ children,
408
+ defaultTextStyle,
409
+ {
410
+ ...textStyle,
411
+ ...(applyHoverEffect && hoverTextStyle),
412
+ }
393
413
  )
394
414
  }
395
415
  </View>
@@ -0,0 +1,147 @@
1
+ /**
2
+ * ✔ bindchange
3
+ */
4
+ import {
5
+ JSX,
6
+ useRef,
7
+ forwardRef,
8
+ ReactNode,
9
+ useContext
10
+ } from 'react'
11
+ import {
12
+ View,
13
+ NativeSyntheticEvent,
14
+ ViewStyle
15
+ } from 'react-native'
16
+ import { FormContext, FormFieldValue, CheckboxGroupContext, GroupValue } from './context'
17
+ import useInnerProps, { getCustomEvent } from './getInnerListeners'
18
+ import useNodesRef, { HandlerRef } from './useNodesRef'
19
+ import { throwReactWarning } from './utils'
20
+
21
+ export interface CheckboxGroupProps {
22
+ name: string
23
+ style?: ViewStyle & Record<string, any>
24
+ 'enable-offset'?: boolean
25
+ children: ReactNode
26
+ bindchange?: (evt: NativeSyntheticEvent<TouchEvent> | unknown) => void
27
+ }
28
+
29
+ const CheckboxGroup = forwardRef<
30
+ HandlerRef<View, CheckboxGroupProps>,
31
+ CheckboxGroupProps
32
+ >((props, ref): JSX.Element => {
33
+ const {
34
+ style = {},
35
+ 'enable-offset': enableOffset,
36
+ children,
37
+ bindchange
38
+ } = props
39
+
40
+ const layoutRef = useRef({})
41
+ const formContext = useContext(FormContext)
42
+
43
+ let formValuesMap: Map<string, FormFieldValue> | undefined;
44
+
45
+ if (formContext) {
46
+ formValuesMap = formContext.formValuesMap
47
+ }
48
+
49
+ const groupValue: GroupValue = useRef({}).current
50
+
51
+ const defaultStyle = {
52
+ flexDirection: 'row',
53
+ flexWrap: 'wrap',
54
+ ...style
55
+ }
56
+ const { nodeRef } = useNodesRef(props, ref, {
57
+ defaultStyle
58
+ })
59
+
60
+ const onLayout = () => {
61
+ nodeRef.current?.measure(
62
+ (
63
+ x: number,
64
+ y: number,
65
+ width: number,
66
+ height: number,
67
+ offsetLeft: number,
68
+ offsetTop: number
69
+ ) => {
70
+ layoutRef.current = { x, y, width, height, offsetLeft, offsetTop }
71
+ }
72
+ )
73
+ }
74
+
75
+ const getSelectionValue = (): string[] => {
76
+ const arr: string[] = []
77
+ for (let key in groupValue) {
78
+ if (groupValue[key].checked) {
79
+ arr.push(key)
80
+ }
81
+ }
82
+ return arr
83
+ }
84
+
85
+ const getValue = () => {
86
+ return getSelectionValue()
87
+ }
88
+
89
+ const resetValue = () => {
90
+ Object.keys(groupValue).forEach((key) => {
91
+ groupValue[key].checked = false
92
+ groupValue[key].setValue(false)
93
+ })
94
+ }
95
+
96
+ if (formValuesMap) {
97
+ if (!props.name) {
98
+ throwReactWarning('[Mpx runtime warn]: If a form component is used, the name attribute is required.')
99
+ } else {
100
+ formValuesMap.set(props.name, { getValue, resetValue })
101
+ }
102
+ }
103
+
104
+ const notifyChange = (
105
+ evt: NativeSyntheticEvent<TouchEvent>
106
+ ) => {
107
+ bindchange &&
108
+ bindchange(
109
+ getCustomEvent(
110
+ 'tap',
111
+ evt,
112
+ {
113
+ layoutRef,
114
+ detail: {
115
+ value: getSelectionValue()
116
+ }
117
+ },
118
+ props
119
+ )
120
+ )
121
+ }
122
+
123
+ const innerProps = useInnerProps(
124
+ props,
125
+ {
126
+ ref: nodeRef,
127
+ style: defaultStyle,
128
+ ...(enableOffset ? { onLayout } : {})
129
+ },
130
+ ['enable-offset'],
131
+ {
132
+ layoutRef
133
+ }
134
+ )
135
+
136
+ return (
137
+ <View {...innerProps}>
138
+ <CheckboxGroupContext.Provider value={{ groupValue, notifyChange }}>
139
+ {children}
140
+ </CheckboxGroupContext.Provider>
141
+ </View>
142
+ )
143
+ })
144
+
145
+ CheckboxGroup.displayName = 'mpx-checkbox-group'
146
+
147
+ export default CheckboxGroup