@mpxjs/webpack-plugin 2.9.18 → 2.9.19-react.0
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.
- package/lib/config.js +59 -97
- package/lib/dependencies/ResolveDependency.js +2 -2
- package/lib/helpers.js +5 -1
- package/lib/index.js +21 -18
- package/lib/loader.js +44 -97
- package/lib/native-loader.js +1 -1
- package/lib/platform/index.js +3 -0
- package/lib/platform/style/wx/index.js +413 -0
- package/lib/platform/template/wx/component-config/button.js +36 -0
- package/lib/platform/template/wx/component-config/image.js +15 -0
- package/lib/platform/template/wx/component-config/input.js +36 -0
- package/lib/platform/template/wx/component-config/scroll-view.js +27 -1
- package/lib/platform/template/wx/component-config/swiper-item.js +13 -1
- package/lib/platform/template/wx/component-config/swiper.js +25 -1
- package/lib/platform/template/wx/component-config/text.js +15 -0
- package/lib/platform/template/wx/component-config/textarea.js +39 -0
- package/lib/platform/template/wx/component-config/unsupported.js +18 -0
- package/lib/platform/template/wx/component-config/view.js +14 -0
- package/lib/platform/template/wx/index.js +88 -4
- package/lib/react/index.js +92 -0
- package/lib/react/processJSON.js +362 -0
- package/lib/react/processScript.js +40 -0
- package/lib/react/processStyles.js +63 -0
- package/lib/react/processTemplate.js +151 -0
- package/lib/react/script-helper.js +79 -0
- package/lib/react/style-helper.js +91 -0
- package/lib/runtime/components/react/event.config.ts +32 -0
- package/lib/runtime/components/react/getInnerListeners.ts +289 -0
- package/lib/runtime/components/react/getInnerListeners.type.ts +68 -0
- package/lib/runtime/components/react/mpx-button.tsx +402 -0
- package/lib/runtime/components/react/mpx-image/index.tsx +351 -0
- package/lib/runtime/components/react/mpx-image/svg.tsx +21 -0
- package/lib/runtime/components/react/mpx-input.tsx +389 -0
- package/lib/runtime/components/react/mpx-scroll-view.tsx +412 -0
- package/lib/runtime/components/react/mpx-swiper/carouse.tsx +407 -0
- package/lib/runtime/components/react/mpx-swiper/index.tsx +68 -0
- package/lib/runtime/components/react/mpx-swiper/type.ts +69 -0
- package/lib/runtime/components/react/mpx-swiper-item.tsx +42 -0
- package/lib/runtime/components/react/mpx-text.tsx +106 -0
- package/lib/runtime/components/react/mpx-textarea.tsx +46 -0
- package/lib/runtime/components/react/mpx-view.tsx +397 -0
- package/lib/runtime/components/react/utils.ts +92 -0
- package/lib/runtime/stringify.wxs +3 -7
- package/lib/runtime/useNodesRef.ts +39 -0
- package/lib/style-compiler/index.js +2 -1
- package/lib/template-compiler/compiler.js +539 -287
- package/lib/template-compiler/gen-node-react.js +95 -0
- package/lib/template-compiler/index.js +19 -31
- package/lib/utils/env.js +17 -0
- package/lib/utils/make-map.js +1 -1
- package/lib/utils/shallow-stringify.js +17 -0
- package/lib/web/index.js +122 -0
- package/lib/web/processMainScript.js +3 -4
- package/lib/web/processScript.js +9 -5
- package/lib/web/processTemplate.js +14 -14
- package/lib/web/script-helper.js +11 -19
- package/package.json +7 -3
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compared with Input:
|
|
3
|
+
* Subtraction:
|
|
4
|
+
* type, password, confirm-hold
|
|
5
|
+
* Addition:
|
|
6
|
+
* - confirm-type: Not support `return`
|
|
7
|
+
* ✔ auto-height
|
|
8
|
+
* ✘ fixed
|
|
9
|
+
* ✘ show-confirm-bar
|
|
10
|
+
* ✔ bindlinechange: No `heightRpx` info.
|
|
11
|
+
*/
|
|
12
|
+
import React, { forwardRef } from 'react'
|
|
13
|
+
import { Keyboard, TextInput } from 'react-native'
|
|
14
|
+
import Input, { InputProps, PrivateInputProps } from './mpx-input'
|
|
15
|
+
import { omit } from './utils'
|
|
16
|
+
import { HandlerRef } from '../../useNodesRef'
|
|
17
|
+
|
|
18
|
+
export type TextareProps = Omit<
|
|
19
|
+
InputProps & PrivateInputProps,
|
|
20
|
+
'type' | 'password' | 'multiline' | 'confirm-hold'
|
|
21
|
+
>
|
|
22
|
+
|
|
23
|
+
const Textarea = forwardRef<HandlerRef<TextInput, TextareProps>, TextareProps>(
|
|
24
|
+
(props, ref): React.JSX.Element => {
|
|
25
|
+
const restProps = omit(props, [
|
|
26
|
+
'ref',
|
|
27
|
+
'type',
|
|
28
|
+
'password',
|
|
29
|
+
'multiline',
|
|
30
|
+
'confirm-hold',
|
|
31
|
+
])
|
|
32
|
+
return (
|
|
33
|
+
<Input
|
|
34
|
+
ref={ref}
|
|
35
|
+
multiline
|
|
36
|
+
confirm-type='next'
|
|
37
|
+
bindblur={() => Keyboard.dismiss()}
|
|
38
|
+
{...restProps}
|
|
39
|
+
/>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
Textarea.displayName = 'mpx-textarea'
|
|
45
|
+
|
|
46
|
+
export default Textarea
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ✔ hover-class
|
|
3
|
+
* ✘ hover-stop-propagation
|
|
4
|
+
* ✔ hover-start-time
|
|
5
|
+
* ✔ hover-stay-time
|
|
6
|
+
*/
|
|
7
|
+
import { View, Text, StyleProp, TextStyle, ViewStyle, NativeSyntheticEvent, ViewProps, ImageStyle, ImageResizeMode, StyleSheet, Image, LayoutChangeEvent } from 'react-native'
|
|
8
|
+
import { useRef, useState, useEffect, forwardRef, ForwardedRef, ReactNode, JSX } from 'react'
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
import useInnerProps from './getInnerListeners'
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
import useNodesRef, { HandlerRef } from '../../useNodesRef' // 引入辅助函数
|
|
13
|
+
|
|
14
|
+
import { parseUrl, TEXT_STYLE_REGEX, PERCENT_REGX, isText} from './utils'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
type ExtendedViewStyle = ViewStyle & {
|
|
18
|
+
backgroundImage?: string
|
|
19
|
+
backgroundSize?: ImageResizeMode
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface _ViewProps extends ViewProps {
|
|
23
|
+
style?: Array<ExtendedViewStyle>
|
|
24
|
+
children?: ReactNode | ReactNode []
|
|
25
|
+
hoverStyle: Array<ExtendedViewStyle>
|
|
26
|
+
['hover-start-time']: number
|
|
27
|
+
['hover-stay-time']: number
|
|
28
|
+
'enable-offset'?: boolean
|
|
29
|
+
bindtouchstart?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void
|
|
30
|
+
bindtouchmove?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void
|
|
31
|
+
bindtouchend?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type Obj = Record<string, any>
|
|
35
|
+
|
|
36
|
+
type GroupData = Record<string, Record<string, any>>;
|
|
37
|
+
|
|
38
|
+
type Handler = (...args: any []) => void
|
|
39
|
+
|
|
40
|
+
type Size = {
|
|
41
|
+
width: number,
|
|
42
|
+
height: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type DimensionValue = number | 'auto' | `${number}%`
|
|
46
|
+
|
|
47
|
+
type PreImageInfo = {
|
|
48
|
+
src?: string,
|
|
49
|
+
sizeList: DimensionValue []
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type ImageProps = {
|
|
53
|
+
style: ImageStyle,
|
|
54
|
+
src?: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const IMAGE_STYLE_REGEX = /^background(Image|Size|Repeat|Position)$/
|
|
58
|
+
|
|
59
|
+
function groupBy(style: Obj, callback: (key: string, val: string) => string, group:GroupData = {}):GroupData {
|
|
60
|
+
let groupKey = ''
|
|
61
|
+
for (let key in style) {
|
|
62
|
+
if (style.hasOwnProperty(key)) { // 确保处理对象自身的属性
|
|
63
|
+
let val: string = style[key] as string
|
|
64
|
+
groupKey = callback(key, val)
|
|
65
|
+
if (!group[groupKey]) {
|
|
66
|
+
group[groupKey] = {}
|
|
67
|
+
}
|
|
68
|
+
group[groupKey][key] = val
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return group
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const applyHandlers = (handlers: Handler[] , args: any [] ) => {
|
|
75
|
+
for (let handler of handlers) {
|
|
76
|
+
handler(...args)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const checkNeedLayout = (style: PreImageInfo) => {
|
|
81
|
+
const [width, height] = style.sizeList
|
|
82
|
+
return (PERCENT_REGX.test(`${height}`) && width === 'auto') || (PERCENT_REGX.test(`${width}`) && height === 'auto')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* h - 用户设置的高度
|
|
87
|
+
* lh - 容器的高度
|
|
88
|
+
* ratio - 原始图片的宽高比
|
|
89
|
+
* **/
|
|
90
|
+
function calculateSize(h: number, lh: number, ratio: number) {
|
|
91
|
+
let height, width
|
|
92
|
+
if (PERCENT_REGX.test(`${h}`)) { // auto px/rpx
|
|
93
|
+
if (!lh) return null
|
|
94
|
+
height = (parseFloat(`${h}`) / 100) * lh
|
|
95
|
+
width = height * ratio
|
|
96
|
+
} else { // 2. auto px/rpx - 根据比例计算
|
|
97
|
+
height = h
|
|
98
|
+
width = height * ratio
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
width,
|
|
103
|
+
height
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// background-size 转换
|
|
108
|
+
function backgroundSize (imageProps: ImageProps, preImageInfo: PreImageInfo, imageSize: Size, layoutInfo: Size) {
|
|
109
|
+
let sizeList = preImageInfo.sizeList
|
|
110
|
+
if (!sizeList) return
|
|
111
|
+
// 枚举值
|
|
112
|
+
if (['cover', 'contain'].includes(`${sizeList[0]}`)) {
|
|
113
|
+
imageProps.style.resizeMode = sizeList[0] as ImageResizeMode
|
|
114
|
+
} else {
|
|
115
|
+
const [width, height] = sizeList
|
|
116
|
+
let newWidth: ImageStyle['width'] = 0, newHeight: ImageStyle['height'] = 0
|
|
117
|
+
|
|
118
|
+
const { width: imageSizeWidth, height: imageSizeHeight } = imageSize || {}
|
|
119
|
+
|
|
120
|
+
if (width === 'auto' && height === 'auto') { // 均为auto
|
|
121
|
+
if (!imageSize) return
|
|
122
|
+
newHeight = imageSizeHeight
|
|
123
|
+
newWidth = imageSizeWidth
|
|
124
|
+
} else if (width === 'auto') { // auto px/rpx/%
|
|
125
|
+
if (!imageSize) return
|
|
126
|
+
const dimensions = calculateSize(height as number, layoutInfo?.height, imageSizeWidth / imageSizeHeight)
|
|
127
|
+
if (!dimensions) return
|
|
128
|
+
newWidth = dimensions.width
|
|
129
|
+
newHeight = dimensions.height
|
|
130
|
+
}else if (height === 'auto') { // auto px/rpx/%
|
|
131
|
+
if (!imageSize) return
|
|
132
|
+
const dimensions = calculateSize(width as number, layoutInfo?.width, imageSizeHeight / imageSizeWidth)
|
|
133
|
+
if (!dimensions) return
|
|
134
|
+
newHeight = dimensions.width
|
|
135
|
+
newWidth = dimensions.height
|
|
136
|
+
} else { // 数值类型 ImageStyle
|
|
137
|
+
// 数值类型设置为 stretch
|
|
138
|
+
(imageProps.style as ImageStyle).resizeMode = 'stretch'
|
|
139
|
+
newWidth = PERCENT_REGX.test(`${width}`) ? width : +width! as DimensionValue
|
|
140
|
+
newHeight = PERCENT_REGX.test(`${width}`) ? height : +height! as DimensionValue
|
|
141
|
+
}
|
|
142
|
+
// 样式合并
|
|
143
|
+
imageProps.style = {
|
|
144
|
+
...imageProps.style as ImageStyle,
|
|
145
|
+
width: newWidth,
|
|
146
|
+
height: newHeight
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// background-image转换为source
|
|
152
|
+
function backgroundImage(imageProps: ImageProps, preImageInfo: PreImageInfo) {
|
|
153
|
+
imageProps.src = preImageInfo.src
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const imageStyleToProps = (preImageInfo: PreImageInfo, imageSize: Size, layoutInfo: Size) => {
|
|
157
|
+
// 初始化
|
|
158
|
+
const imageProps: ImageProps = {
|
|
159
|
+
style: {
|
|
160
|
+
resizeMode: 'cover' as ImageResizeMode,
|
|
161
|
+
...StyleSheet.absoluteFillObject
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
applyHandlers([ backgroundSize, backgroundImage ],[imageProps, preImageInfo, imageSize, layoutInfo])
|
|
166
|
+
if (!imageProps?.src) return null
|
|
167
|
+
return imageProps
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
function preParseImage(imageStyle?: ExtendedViewStyle) {
|
|
172
|
+
|
|
173
|
+
const { backgroundImage, backgroundSize = [ "auto" ] } = imageStyle || {}
|
|
174
|
+
const src = parseUrl(backgroundImage)
|
|
175
|
+
|
|
176
|
+
let sizeList = backgroundSize.slice() as DimensionValue []
|
|
177
|
+
|
|
178
|
+
sizeList.length === 1 && sizeList.push(sizeList[0])
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
src,
|
|
182
|
+
sizeList
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function wrapImage(imageStyle?: ExtendedViewStyle) {
|
|
187
|
+
const [show, setShow] = useState<boolean>(false)
|
|
188
|
+
const [, setImageSizeWidth] = useState<number | null>(null)
|
|
189
|
+
const [, setImageSizeHeight] = useState<number | null>(null)
|
|
190
|
+
const [, setLayoutInfoWidth] = useState<number | null>(null)
|
|
191
|
+
const [, setLayoutInfoHeight] = useState<number | null>(null)
|
|
192
|
+
const sizeInfo = useRef<Size | null>(null)
|
|
193
|
+
const layoutInfo = useRef<Size | null>(null)
|
|
194
|
+
|
|
195
|
+
// 预解析
|
|
196
|
+
const preImageInfo: PreImageInfo = preParseImage(imageStyle)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
// 判断是否可挂载onLayout
|
|
200
|
+
const needLayout = checkNeedLayout(preImageInfo)
|
|
201
|
+
const { src, sizeList } = preImageInfo
|
|
202
|
+
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if(!src) {
|
|
205
|
+
setShow(false)
|
|
206
|
+
sizeInfo.current = null
|
|
207
|
+
layoutInfo.current = null
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!sizeList.includes('auto')) {
|
|
212
|
+
setShow(true)
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
Image.getSize(src, (width, height) => {
|
|
216
|
+
sizeInfo.current = {
|
|
217
|
+
width,
|
|
218
|
+
height
|
|
219
|
+
}
|
|
220
|
+
//1. 当需要绑定onLayout 2. 获取到布局信息
|
|
221
|
+
if (!needLayout || layoutInfo.current) {
|
|
222
|
+
setImageSizeWidth(width)
|
|
223
|
+
setImageSizeHeight(height)
|
|
224
|
+
if(layoutInfo.current) {
|
|
225
|
+
setLayoutInfoWidth(layoutInfo.current.width)
|
|
226
|
+
setLayoutInfoHeight(layoutInfo.current.height)
|
|
227
|
+
}
|
|
228
|
+
setShow(true)
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
}, [preImageInfo?.src])
|
|
232
|
+
|
|
233
|
+
if (!preImageInfo?.src) return null
|
|
234
|
+
|
|
235
|
+
const onLayout = (res: LayoutChangeEvent) => {
|
|
236
|
+
const { width, height } = res?.nativeEvent?.layout || {}
|
|
237
|
+
layoutInfo.current = {
|
|
238
|
+
width,
|
|
239
|
+
height
|
|
240
|
+
}
|
|
241
|
+
if (sizeInfo.current) {
|
|
242
|
+
setImageSizeWidth(sizeInfo.current.width)
|
|
243
|
+
setImageSizeHeight(sizeInfo.current.height)
|
|
244
|
+
setLayoutInfoWidth(width)
|
|
245
|
+
setLayoutInfoHeight(height)
|
|
246
|
+
setShow(true)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return <View key='viewBgImg' {...needLayout ? {onLayout} : null } style={{ ...StyleSheet.absoluteFillObject, width: '100%', height: '100%', overflow: 'hidden'}}>
|
|
251
|
+
{show && <Image {...imageStyleToProps(preImageInfo, sizeInfo.current as Size, layoutInfo.current as Size)} />}
|
|
252
|
+
</View>
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function splitStyle(styles: ExtendedViewStyle) {
|
|
256
|
+
return groupBy(styles, (key) => {
|
|
257
|
+
if (TEXT_STYLE_REGEX.test(key))
|
|
258
|
+
return 'textStyle'
|
|
259
|
+
else if (IMAGE_STYLE_REGEX.test(key)) return 'imageStyle'
|
|
260
|
+
return 'innerStyle'
|
|
261
|
+
}, {})
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function every(children: ReactNode [], callback: (children: ReactNode) => boolean) {
|
|
265
|
+
return children.every((child) => callback(child))
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function wrapChildren(children: ReactNode | ReactNode [] , textStyle?: StyleProp<TextStyle>, imageStyle?: ExtendedViewStyle) {
|
|
269
|
+
children = Array.isArray(children) ? children : [children]
|
|
270
|
+
if (every(children as ReactNode[], (child)=>isText(child))) {
|
|
271
|
+
children = [<Text key='viewTextWrap' style={textStyle}>{children}</Text>]
|
|
272
|
+
} else {
|
|
273
|
+
if(textStyle) console.warn('Text style will be ignored unless every child of the view is Text node!')
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return [
|
|
277
|
+
wrapImage(imageStyle),
|
|
278
|
+
...children
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const _View = forwardRef<HandlerRef<View, _ViewProps>, _ViewProps>((props, ref): JSX.Element => {
|
|
283
|
+
const {
|
|
284
|
+
style = [],
|
|
285
|
+
children,
|
|
286
|
+
hoverStyle,
|
|
287
|
+
'hover-start-time': hoverStartTime = 50,
|
|
288
|
+
'hover-stay-time': hoverStayTime = 400,
|
|
289
|
+
'enable-offset': enableOffset
|
|
290
|
+
} = props
|
|
291
|
+
|
|
292
|
+
const [isHover, setIsHover] = useState(false)
|
|
293
|
+
|
|
294
|
+
const layoutRef = useRef({})
|
|
295
|
+
|
|
296
|
+
// 打平 style 数组
|
|
297
|
+
const styleObj:ExtendedViewStyle = StyleSheet.flatten(style)
|
|
298
|
+
// 默认样式
|
|
299
|
+
const defaultStyle:ExtendedViewStyle = {
|
|
300
|
+
// flex 布局相关的默认样式
|
|
301
|
+
...styleObj.display === 'flex' && {
|
|
302
|
+
flexDirection: 'row',
|
|
303
|
+
flexBasis: 'auto',
|
|
304
|
+
flexShrink: 1,
|
|
305
|
+
flexWrap: 'nowrap'
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const { nodeRef } = useNodesRef<View, _ViewProps>(props, ref, {
|
|
310
|
+
defaultStyle
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
const dataRef = useRef<{
|
|
314
|
+
startTimer?: ReturnType<typeof setTimeout>
|
|
315
|
+
stayTimer?: ReturnType<typeof setTimeout>
|
|
316
|
+
}>({})
|
|
317
|
+
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
return () => {
|
|
320
|
+
dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer)
|
|
321
|
+
dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer)
|
|
322
|
+
}
|
|
323
|
+
}, [])
|
|
324
|
+
|
|
325
|
+
const setStartTimer = () => {
|
|
326
|
+
dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer)
|
|
327
|
+
dataRef.current.startTimer = setTimeout(() => {
|
|
328
|
+
setIsHover(() => true)
|
|
329
|
+
}, +hoverStartTime)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const setStayTimer = () => {
|
|
333
|
+
dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer)
|
|
334
|
+
dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer)
|
|
335
|
+
dataRef.current.stayTimer = setTimeout(() => {
|
|
336
|
+
setIsHover(() => false)
|
|
337
|
+
}, +hoverStayTime)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function onTouchStart(e: NativeSyntheticEvent<TouchEvent>){
|
|
341
|
+
const { bindtouchstart } = props;
|
|
342
|
+
bindtouchstart && bindtouchstart(e)
|
|
343
|
+
setStartTimer()
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function onTouchEnd(e: NativeSyntheticEvent<TouchEvent>){
|
|
347
|
+
const { bindtouchend } = props;
|
|
348
|
+
bindtouchend && bindtouchend(e)
|
|
349
|
+
setStayTimer()
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const onLayout = () => {
|
|
353
|
+
|
|
354
|
+
nodeRef.current?.measure((x: number, y: number, width: number, height: number, offsetLeft: number, offsetTop: number) => {
|
|
355
|
+
layoutRef.current = { x, y, width, height, offsetLeft, offsetTop }
|
|
356
|
+
})
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const {textStyle, imageStyle, innerStyle} = splitStyle(StyleSheet.flatten<ExtendedViewStyle>([
|
|
360
|
+
defaultStyle,
|
|
361
|
+
styleObj,
|
|
362
|
+
...(isHover ? hoverStyle : [])]
|
|
363
|
+
))
|
|
364
|
+
|
|
365
|
+
const innerProps = useInnerProps(props, {
|
|
366
|
+
ref: nodeRef,
|
|
367
|
+
...enableOffset ? { onLayout } : {},
|
|
368
|
+
...(hoverStyle && {
|
|
369
|
+
bindtouchstart: onTouchStart,
|
|
370
|
+
bindtouchend: onTouchEnd
|
|
371
|
+
})
|
|
372
|
+
}, [
|
|
373
|
+
'style',
|
|
374
|
+
'children',
|
|
375
|
+
'hover-start-time',
|
|
376
|
+
'hover-stay-time',
|
|
377
|
+
'hoverStyle',
|
|
378
|
+
'hover-class',
|
|
379
|
+
'enable-offset'
|
|
380
|
+
], {
|
|
381
|
+
layoutRef
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<View
|
|
386
|
+
{...innerProps}
|
|
387
|
+
style={innerStyle}
|
|
388
|
+
>
|
|
389
|
+
{wrapChildren(children, textStyle, imageStyle)}
|
|
390
|
+
</View>
|
|
391
|
+
)
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
_View.displayName = 'mpx-view'
|
|
395
|
+
|
|
396
|
+
export default _View
|
|
397
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { useEffect, useRef, Children, ReactNode, FunctionComponent, isValidElement } from 'react'
|
|
2
|
+
import { StyleProp, StyleSheet, TextStyle, ViewStyle } from 'react-native'
|
|
3
|
+
|
|
4
|
+
export const TEXT_STYLE_REGEX = /color|font.*|text.*|letterSpacing|lineHeight|includeFontPadding|writingDirection/
|
|
5
|
+
|
|
6
|
+
export const PERCENT_REGX = /\d+(\.\d+)?%$/
|
|
7
|
+
|
|
8
|
+
const URL_REGEX = /url\(["']?(.*?)["']?\)/
|
|
9
|
+
|
|
10
|
+
export function omit<T, K extends string>(obj: T, fields: K[]): Omit<T, K> {
|
|
11
|
+
const shallowCopy: any = Object.assign({}, obj)
|
|
12
|
+
for (let i = 0; i < fields.length; i += 1) {
|
|
13
|
+
const key = fields[i]
|
|
14
|
+
delete shallowCopy[key]
|
|
15
|
+
}
|
|
16
|
+
return shallowCopy
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 从 style 中提取 TextStyle
|
|
21
|
+
* @param style
|
|
22
|
+
* @returns
|
|
23
|
+
*/
|
|
24
|
+
export const extractTextStyle = (style: StyleProp<ViewStyle & TextStyle>): TextStyle => {
|
|
25
|
+
return Object.entries(StyleSheet.flatten(style)).reduce((textStyle, [key, value]) => {
|
|
26
|
+
TEXT_STYLE_REGEX.test(key) && Object.assign(textStyle, { [key]: value })
|
|
27
|
+
return textStyle
|
|
28
|
+
}, {})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 用法等同于 useEffect,但是会忽略首次执行,只在依赖更新时执行
|
|
33
|
+
*/
|
|
34
|
+
export const useUpdateEffect = (effect: any, deps: any) => {
|
|
35
|
+
const isMounted = useRef(false)
|
|
36
|
+
|
|
37
|
+
// for react-refresh
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
return () => {
|
|
40
|
+
isMounted.current = false
|
|
41
|
+
}
|
|
42
|
+
}, [])
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!isMounted.current) {
|
|
46
|
+
isMounted.current = true
|
|
47
|
+
} else {
|
|
48
|
+
return effect()
|
|
49
|
+
}
|
|
50
|
+
}, deps)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 解析行内样式
|
|
55
|
+
* @param inlineStyle
|
|
56
|
+
* @returns
|
|
57
|
+
*/
|
|
58
|
+
export const parseInlineStyle = (inlineStyle = ''): Record<string, string> => {
|
|
59
|
+
return inlineStyle.split(';').reduce((styleObj, style) => {
|
|
60
|
+
const [k, v, ...rest] = style.split(':')
|
|
61
|
+
if (rest.length || !v || !k) return styleObj
|
|
62
|
+
const key = k.trim().replace(/-./g, c => c.substring(1).toUpperCase())
|
|
63
|
+
return Object.assign(styleObj, { [key]: v.trim() })
|
|
64
|
+
}, {})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const parseUrl = (cssUrl: string = '') => {
|
|
68
|
+
if (!cssUrl) return
|
|
69
|
+
|
|
70
|
+
const match = cssUrl.match(URL_REGEX)
|
|
71
|
+
|
|
72
|
+
return match?.[1]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const getRestProps = (transferProps: any = {}, originProps: any = {}, deletePropsKey: any = []) => {
|
|
76
|
+
return {
|
|
77
|
+
...transferProps,
|
|
78
|
+
...omit(originProps, deletePropsKey)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const isText = (ele: ReactNode) => {
|
|
83
|
+
if (isValidElement(ele)) {
|
|
84
|
+
const displayName = (ele.type as FunctionComponent)?.displayName
|
|
85
|
+
return displayName === 'mpx-text' || displayName === 'Text'
|
|
86
|
+
}
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function every(children: ReactNode, callback: (children: ReactNode) => boolean ) {
|
|
91
|
+
return Children.toArray(children).every((child) => callback(child as ReactNode))
|
|
92
|
+
}
|
|
@@ -90,10 +90,6 @@ function isArray (arr) {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
function isDef (v) {
|
|
94
|
-
return v !== undefined && v !== null
|
|
95
|
-
}
|
|
96
|
-
|
|
97
93
|
var escapeMap = {
|
|
98
94
|
'(': '_pl_',
|
|
99
95
|
')': '_pr_',
|
|
@@ -145,11 +141,11 @@ function stringifyDynamicClass (value) {
|
|
|
145
141
|
|
|
146
142
|
function stringifyArray (value) {
|
|
147
143
|
var res = ''
|
|
148
|
-
var
|
|
144
|
+
var classString
|
|
149
145
|
for (var i = 0; i < value.length; i++) {
|
|
150
|
-
if (
|
|
146
|
+
if ((classString = stringifyDynamicClass(value[i]))) {
|
|
151
147
|
if (res) res += ' '
|
|
152
|
-
res +=
|
|
148
|
+
res += classString
|
|
153
149
|
}
|
|
154
150
|
}
|
|
155
151
|
return res
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useRef, useEffect, useImperativeHandle, RefObject, ForwardedRef } from 'react'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
type Obj = Record<string, any>
|
|
5
|
+
|
|
6
|
+
export type HandlerRef<T, P> = {
|
|
7
|
+
getNodeInstance(): {
|
|
8
|
+
props: RefObject<P>,
|
|
9
|
+
nodeRef: RefObject<T>,
|
|
10
|
+
instance: Obj
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function useNodesRef<T, P>(props: P, ref: ForwardedRef<HandlerRef<T, P>>, instance:Obj = {} ) {
|
|
15
|
+
const nodeRef = useRef<T>(null)
|
|
16
|
+
const _props = useRef<P | null>(props)
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
_props.current = props
|
|
20
|
+
return () => {
|
|
21
|
+
_props.current = null // 组件销毁,清空 _props 依赖数据
|
|
22
|
+
}
|
|
23
|
+
}, [props])
|
|
24
|
+
useImperativeHandle(ref, () => {
|
|
25
|
+
return {
|
|
26
|
+
getNodeInstance () {
|
|
27
|
+
return {
|
|
28
|
+
props: _props,
|
|
29
|
+
nodeRef,
|
|
30
|
+
instance
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
nodeRef
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -9,6 +9,7 @@ const scopeId = require('./plugins/scope-id')
|
|
|
9
9
|
const transSpecial = require('./plugins/trans-special')
|
|
10
10
|
const { matchCondition } = require('../utils/match-condition')
|
|
11
11
|
const parseRequest = require('../utils/parse-request')
|
|
12
|
+
const isReact = require('../utils/env').isReact
|
|
12
13
|
|
|
13
14
|
module.exports = function (css, map) {
|
|
14
15
|
this.cacheable()
|
|
@@ -47,7 +48,7 @@ module.exports = function (css, map) {
|
|
|
47
48
|
plugins.push(transSpecial({ id }))
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
if (mode === 'web') {
|
|
51
|
+
if (mode === 'web' || isReact(mode)) {
|
|
51
52
|
plugins.push(transSpecial({ id }))
|
|
52
53
|
}
|
|
53
54
|
|