@mpxjs/webpack-plugin 2.9.70-alpha.0 → 2.9.71

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 (126) hide show
  1. package/LICENSE +433 -0
  2. package/README.md +1 -1
  3. package/lib/config.js +0 -14
  4. package/lib/dependencies/ResolveDependency.js +0 -5
  5. package/lib/index.js +7 -38
  6. package/lib/json-compiler/helper.js +3 -3
  7. package/lib/loader.js +0 -53
  8. package/lib/parser.js +1 -1
  9. package/lib/platform/json/wx/index.js +21 -8
  10. package/lib/platform/style/wx/index.js +51 -54
  11. package/lib/platform/template/wx/component-config/button.js +2 -14
  12. package/lib/platform/template/wx/component-config/fix-component-name.js +15 -12
  13. package/lib/platform/template/wx/component-config/image.js +0 -4
  14. package/lib/platform/template/wx/component-config/index.js +1 -1
  15. package/lib/platform/template/wx/component-config/input.js +0 -4
  16. package/lib/platform/template/wx/component-config/movable-view.js +8 -1
  17. package/lib/platform/template/wx/component-config/picker-view.js +1 -5
  18. package/lib/platform/template/wx/component-config/rich-text.js +6 -2
  19. package/lib/platform/template/wx/component-config/scroll-view.js +1 -5
  20. package/lib/platform/template/wx/component-config/switch.js +0 -4
  21. package/lib/platform/template/wx/component-config/text.js +0 -4
  22. package/lib/platform/template/wx/component-config/textarea.js +0 -5
  23. package/lib/platform/template/wx/component-config/unsupported.js +1 -1
  24. package/lib/platform/template/wx/component-config/view.js +0 -4
  25. package/lib/platform/template/wx/index.js +1 -131
  26. package/lib/resolve-loader.js +1 -4
  27. package/lib/runtime/components/react/context.ts +8 -0
  28. package/lib/runtime/components/react/dist/context.js +2 -0
  29. package/lib/runtime/components/react/dist/getInnerListeners.js +39 -37
  30. package/lib/runtime/components/react/dist/mpx-button.jsx +16 -44
  31. package/lib/runtime/components/react/dist/mpx-canvas/html.js +2 -4
  32. package/lib/runtime/components/react/dist/mpx-canvas/index.jsx +5 -1
  33. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +93 -58
  34. package/lib/runtime/components/react/dist/mpx-navigator.jsx +1 -1
  35. package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +10 -14
  36. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +94 -81
  37. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +19 -16
  38. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -11
  39. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +11 -4
  40. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +31 -8
  41. package/lib/runtime/components/react/dist/mpx-swiper.jsx +435 -371
  42. package/lib/runtime/components/react/dist/mpx-view.jsx +18 -53
  43. package/lib/runtime/components/react/dist/pickerFaces.js +3 -8
  44. package/lib/runtime/components/react/dist/pickerVIewContext.js +5 -0
  45. package/lib/runtime/components/react/dist/{pickerViewOverlay.jsx → pickerViewIndicator.jsx} +6 -6
  46. package/lib/runtime/components/react/dist/pickerViewMask.jsx +2 -2
  47. package/lib/runtime/components/react/dist/useAnimationHooks.js +27 -10
  48. package/lib/runtime/components/react/dist/utils.jsx +162 -70
  49. package/lib/runtime/components/react/event.config.ts +25 -26
  50. package/lib/runtime/components/react/getInnerListeners.ts +236 -182
  51. package/lib/runtime/components/react/mpx-button.tsx +27 -69
  52. package/lib/runtime/components/react/mpx-canvas/html.ts +2 -4
  53. package/lib/runtime/components/react/mpx-canvas/index.tsx +44 -46
  54. package/lib/runtime/components/react/mpx-checkbox-group.tsx +15 -13
  55. package/lib/runtime/components/react/mpx-checkbox.tsx +20 -21
  56. package/lib/runtime/components/react/mpx-form.tsx +15 -20
  57. package/lib/runtime/components/react/mpx-icon.tsx +2 -2
  58. package/lib/runtime/components/react/mpx-image.tsx +87 -47
  59. package/lib/runtime/components/react/mpx-input.tsx +24 -32
  60. package/lib/runtime/components/react/mpx-label.tsx +12 -14
  61. package/lib/runtime/components/react/mpx-movable-area.tsx +10 -16
  62. package/lib/runtime/components/react/mpx-movable-view.tsx +133 -92
  63. package/lib/runtime/components/react/mpx-navigator.tsx +3 -9
  64. package/lib/runtime/components/react/mpx-picker-view-column-item.tsx +76 -0
  65. package/lib/runtime/components/react/mpx-picker-view-column.tsx +206 -183
  66. package/lib/runtime/components/react/mpx-picker-view.tsx +49 -48
  67. package/lib/runtime/components/react/mpx-radio-group.tsx +13 -15
  68. package/lib/runtime/components/react/mpx-radio.tsx +19 -25
  69. package/lib/runtime/components/react/mpx-rich-text/html.ts +40 -0
  70. package/lib/runtime/components/react/mpx-rich-text/index.tsx +115 -0
  71. package/lib/runtime/components/react/mpx-root-portal.tsx +3 -5
  72. package/lib/runtime/components/react/mpx-scroll-view.tsx +53 -43
  73. package/lib/runtime/components/react/mpx-swiper-item.tsx +45 -11
  74. package/lib/runtime/components/react/mpx-swiper.tsx +742 -0
  75. package/lib/runtime/components/react/mpx-switch.tsx +19 -15
  76. package/lib/runtime/components/react/mpx-text.tsx +8 -16
  77. package/lib/runtime/components/react/mpx-textarea.tsx +11 -10
  78. package/lib/runtime/components/react/mpx-view.tsx +28 -77
  79. package/lib/runtime/components/react/mpx-web-view.tsx +94 -59
  80. package/lib/runtime/components/react/pickerFaces.ts +10 -7
  81. package/lib/runtime/components/react/pickerVIewContext.ts +27 -0
  82. package/lib/runtime/components/react/pickerViewIndicator.tsx +34 -0
  83. package/lib/runtime/components/react/pickerViewMask.tsx +30 -0
  84. package/lib/runtime/components/react/types/{getInnerListeners.ts → getInnerListeners.d.ts} +4 -5
  85. package/lib/runtime/components/react/types/global.d.ts +12 -1
  86. package/lib/runtime/components/react/useAnimationHooks.ts +60 -15
  87. package/lib/runtime/components/react/utils.tsx +175 -71
  88. package/lib/runtime/components/web/mpx-checkbox.vue +1 -1
  89. package/lib/runtime/components/web/mpx-picker-view-column.vue +9 -4
  90. package/lib/runtime/components/web/mpx-web-view.vue +34 -20
  91. package/lib/runtime/optionProcessor.js +0 -22
  92. package/lib/style-compiler/index.js +1 -1
  93. package/lib/style-compiler/plugins/scope-id.js +30 -2
  94. package/lib/template-compiler/compiler.js +91 -39
  95. package/lib/utils/env.js +1 -6
  96. package/lib/utils/pre-process-json.js +9 -5
  97. package/lib/wxss/loader.js +15 -2
  98. package/package.json +4 -7
  99. package/lib/dependencies/AddEntryDependency.js +0 -24
  100. package/lib/runtime/components/react/dist/types/common.js +0 -1
  101. package/lib/runtime/components/react/dist/types/getInnerListeners.js +0 -1
  102. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +0 -527
  103. package/lib/runtime/components/react/mpx-swiper/index.tsx +0 -80
  104. package/lib/runtime/components/react/mpx-swiper/type.ts +0 -87
  105. package/lib/runtime/components/react/pickerOverlay.tsx +0 -32
  106. package/lib/runtime/components/tenon/getInnerListeners.js +0 -334
  107. package/lib/runtime/components/tenon/tenon-button.vue +0 -309
  108. package/lib/runtime/components/tenon/tenon-image.vue +0 -66
  109. package/lib/runtime/components/tenon/tenon-input.vue +0 -171
  110. package/lib/runtime/components/tenon/tenon-rich-text.vue +0 -26
  111. package/lib/runtime/components/tenon/tenon-scroll-view.vue +0 -127
  112. package/lib/runtime/components/tenon/tenon-switch.vue +0 -96
  113. package/lib/runtime/components/tenon/tenon-text.vue +0 -70
  114. package/lib/runtime/components/tenon/tenon-textarea.vue +0 -86
  115. package/lib/runtime/components/tenon/tenon-view.vue +0 -93
  116. package/lib/runtime/components/web/event.js +0 -105
  117. package/lib/runtime/optionProcessor.tenon.js +0 -84
  118. package/lib/style-compiler/plugins/hm.js +0 -20
  119. package/lib/tenon/index.js +0 -117
  120. package/lib/tenon/processJSON.js +0 -352
  121. package/lib/tenon/processScript.js +0 -203
  122. package/lib/tenon/processStyles.js +0 -21
  123. package/lib/tenon/processTemplate.js +0 -126
  124. package/lib/tenon/script-helper.js +0 -223
  125. package/lib/utils/get-relative-path.js +0 -25
  126. /package/lib/runtime/components/react/types/{common.ts → common.d.ts} +0 -0
@@ -0,0 +1,30 @@
1
+ import React from 'react'
2
+ import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
3
+ import LinearGradient from 'react-native-linear-gradient'
4
+
5
+ type MaskProps = {
6
+ itemHeight: number
7
+ maskContainerStyle?: StyleProp<ViewStyle>
8
+ }
9
+
10
+ const _PickerViewMask = ({
11
+ itemHeight,
12
+ maskContainerStyle
13
+ }: MaskProps) => {
14
+ return (
15
+ <View style={[styles.maskContainer, maskContainerStyle]} pointerEvents={'none'}>
16
+ <LinearGradient colors={['rgba(255,255,255,1)', 'rgba(255,255,255,0.5)']} style={{ flex: 1 }} />
17
+ <View style={{ height: itemHeight }} />
18
+ <LinearGradient colors={['rgba(255,255,255,0.5)', 'rgba(255,255,255,1)']} style={{ flex: 1 }} />
19
+ </View>
20
+ )
21
+ }
22
+ const styles = StyleSheet.create({
23
+ maskContainer: {
24
+ ...StyleSheet.absoluteFillObject,
25
+ zIndex: 100
26
+ }
27
+ })
28
+
29
+ _PickerViewMask.displayName = 'MpxPickerViewMask'
30
+ export default _PickerViewMask
@@ -13,6 +13,8 @@ type RemoveProps = string[];
13
13
 
14
14
  type NativeTouchEvent = NativeSyntheticEvent<NativeEvent>
15
15
 
16
+ type Navigation = Record<string, any>
17
+
16
18
  interface NativeEvent {
17
19
  timestamp: number;
18
20
  pageX: number;
@@ -36,10 +38,6 @@ interface InnerRef {
36
38
  bubble: null | ReturnType<typeof setTimeout>;
37
39
  capture: null | ReturnType<typeof setTimeout>;
38
40
  };
39
- needPress: {
40
- bubble: boolean;
41
- capture: boolean;
42
- };
43
41
  mpxPressInfo: {
44
42
  detail: {
45
43
  x: number;
@@ -65,5 +63,6 @@ export {
65
63
  InnerRef,
66
64
  LayoutRef,
67
65
  SetTimeoutReturnType,
68
- DataSetType
66
+ DataSetType,
67
+ Navigation
69
68
  }
@@ -19,10 +19,21 @@ declare module '@mpxjs/utils' {
19
19
  bottom: number
20
20
  left: number
21
21
  right: number
22
- }
22
+ },
23
+ layout: {
24
+ x: number
25
+ y: number
26
+ width: number
27
+ height: number
28
+ },
29
+ setOptions: (params: Record<string, any>) => void
23
30
  } | undefined
24
31
  }
25
32
 
26
33
  declare let global: {
27
34
  __formatValue (value: string): string | number
28
35
  } & Record<string, any>
36
+
37
+ declare module '@react-navigation/native' {
38
+ export function useNavigation (): Record<string, any>
39
+ }
@@ -13,6 +13,7 @@ import {
13
13
  WithTimingConfig,
14
14
  AnimationCallback
15
15
  } from 'react-native-reanimated'
16
+ import { error } from '@mpxjs/utils'
16
17
  import { ExtendedViewStyle } from './types/common'
17
18
  import type { _ViewProps } from './mpx-view'
18
19
 
@@ -83,12 +84,34 @@ const InitialValue: ExtendedViewStyle = Object.assign({
83
84
  const TransformOrigin = 'transformOrigin'
84
85
  // transform
85
86
  const isTransform = (key: string) => Object.keys(TransformInitial).includes(key)
87
+ // 多value解析
88
+ const parseValues = (str: string, char = ' ') => {
89
+ let stack = 0
90
+ let temp = ''
91
+ const result = []
92
+ for (let i = 0; i < str.length; i++) {
93
+ if (str[i] === '(') {
94
+ stack++
95
+ } else if (str[i] === ')') {
96
+ stack--
97
+ }
98
+ // 非括号内 或者 非分隔字符且非空
99
+ if (stack !== 0 || (str[i] !== char && str[i] !== ' ')) {
100
+ temp += str[i]
101
+ }
102
+ if ((stack === 0 && str[i] === char) || i === str.length - 1) {
103
+ result.push(temp)
104
+ temp = ''
105
+ }
106
+ }
107
+ return result
108
+ }
86
109
  // parse string transform, eg: transform: 'rotateX(45deg) rotateZ(0.785398rad)'
87
110
  const parseTransform = (transformStr: string) => {
88
- const values = transformStr.trim().split(/\s+/)
111
+ const values = parseValues(transformStr)
89
112
  const transform: {[propName: string]: string|number|number[]}[] = []
90
113
  values.forEach(item => {
91
- const match = item.match(/([/\w]+)\(([^)]+)\)/)
114
+ const match = item.match(/([/\w]+)\((.+)\)/)
92
115
  if (match && match.length >= 3) {
93
116
  let key = match[1]
94
117
  const val = match[2]
@@ -109,7 +132,7 @@ const parseTransform = (transformStr: string) => {
109
132
  break
110
133
  case 'matrix':
111
134
  case 'matrix3d':
112
- transform.push({ [key]: val.split(',').map(val => +val) })
135
+ transform.push({ [key]: parseValues(val, ',').map(val => +val) })
113
136
  break
114
137
  case 'translate':
115
138
  case 'scale':
@@ -120,8 +143,8 @@ const parseTransform = (transformStr: string) => {
120
143
  {
121
144
  // 2 个以上的值处理
122
145
  key = key.replace('3d', '')
123
- const vals = val.split(',', key === 'rotate' ? 4 : 3)
124
- // scale(.5) === scaleX(.5) scaleY(.5) 这里处理一下
146
+ const vals = parseValues(val, ',').splice(0, key === 'rotate' ? 4 : 3)
147
+ // scale(.5) === scaleX(.5) scaleY(.5)
125
148
  if (vals.length === 1 && key === 'scale') {
126
149
  vals.push(vals[0])
127
150
  }
@@ -144,20 +167,32 @@ const formatStyle = (style: ExtendedViewStyle): ExtendedViewStyle => {
144
167
  })
145
168
  }
146
169
 
147
- export default function useAnimationHooks<T, P> (props: _ViewProps) {
148
- const { style = {}, animation } = props
170
+ export default function useAnimationHooks<T, P> (props: _ViewProps & { enableAnimation?: boolean }) {
171
+ const { style = {}, animation, enableAnimation } = props
172
+
173
+ const enableStyleAnimation = enableAnimation || !!animation
174
+ const enableAnimationRef = useRef(enableStyleAnimation)
175
+ if (enableAnimationRef.current !== enableStyleAnimation) {
176
+ error('[Mpx runtime error]: animation use should be stable in the component lifecycle, or you can set [enable-animation] with true.')
177
+ }
178
+
179
+ if (!enableAnimationRef.current) return { enableStyleAnimation: false }
180
+
149
181
  const originalStyle = formatStyle(style)
150
182
  // id 标识
151
183
  const id = animation?.id || -1
152
184
  // 有动画样式的 style key
185
+ // eslint-disable-next-line react-hooks/rules-of-hooks
153
186
  const animatedStyleKeys = useSharedValue([] as (string|string[])[])
154
187
  // 记录动画key的style样式值 没有的话设置为false
188
+ // eslint-disable-next-line react-hooks/rules-of-hooks
155
189
  const animatedKeys = useRef({} as {[propName: keyof ExtendedViewStyle]: boolean})
156
190
  // const animatedKeys = useRef({} as {[propName: keyof ExtendedViewStyle]: boolean|number|string})
157
191
  // ** 全量 style prop sharedValue
158
192
  // 不能做增量的原因:
159
193
  // 1 尝试用 useRef,但 useAnimatedStyle 访问后的 ref 不能在增加新的值,被冻结
160
194
  // 2 尝试用 useSharedValue,因为实际触发的 style prop 需要是 sharedValue 才能驱动动画,若外层 shareValMap 也是 sharedValue,动画无法驱动。
195
+ // eslint-disable-next-line react-hooks/rules-of-hooks
161
196
  const shareValMap = useMemo(() => {
162
197
  return Object.keys(InitialValue).reduce((valMap, key) => {
163
198
  const defaultVal = getInitialVal(key, isTransform(key))
@@ -166,6 +201,7 @@ export default function useAnimationHooks<T, P> (props: _ViewProps) {
166
201
  }, {} as { [propName: keyof ExtendedViewStyle]: SharedValue<string|number> })
167
202
  }, [])
168
203
  // ** 获取动画样式prop & 驱动动画
204
+ // eslint-disable-next-line react-hooks/rules-of-hooks
169
205
  useEffect(() => {
170
206
  if (id === -1) return
171
207
  // 更新动画样式 key map
@@ -186,6 +222,7 @@ export default function useAnimationHooks<T, P> (props: _ViewProps) {
186
222
  // })
187
223
  // }, [style])
188
224
  // ** 清空动画
225
+ // eslint-disable-next-line react-hooks/rules-of-hooks
189
226
  useEffect(() => {
190
227
  return () => {
191
228
  Object.values(shareValMap).forEach((value) => {
@@ -218,12 +255,14 @@ export default function useAnimationHooks<T, P> (props: _ViewProps) {
218
255
  }
219
256
  // 添加每个key的多次step动画
220
257
  animatedKeys.forEach(key => {
221
- let toVal = (rules.get(key) || transform.get(key))
258
+ const ruleV = isTransform(key) ? transform.get(key) : rules.get(key)
222
259
  // key不存在,第一轮取shareValMap[key]value,非第一轮取上一轮的
223
- if (toVal === undefined) {
224
- toVal = index > 0 ? lastValueMap[key] : shareValMap[key].value
225
- }
226
- const animation = getAnimation({ key, value: toVal }, { delay, duration, easing }, needSetCallback ? setTransformOrigin : undefined)
260
+ const toVal = ruleV !== undefined
261
+ ? ruleV
262
+ : index > 0
263
+ ? lastValueMap[key]
264
+ : shareValMap[key].value
265
+ const animation = getAnimation({ key, value: toVal! }, { delay, duration, easing }, needSetCallback ? setTransformOrigin : undefined)
227
266
  needSetCallback = false
228
267
  if (!sequence[key]) {
229
268
  sequence[key] = [animation]
@@ -231,7 +270,7 @@ export default function useAnimationHooks<T, P> (props: _ViewProps) {
231
270
  sequence[key].push(animation)
232
271
  }
233
272
  // 更新一下 lastValueMap
234
- lastValueMap[key] = toVal
273
+ lastValueMap[key] = toVal!
235
274
  })
236
275
  // 赋值驱动动画
237
276
  animatedKeys.forEach((key) => {
@@ -311,7 +350,8 @@ export default function useAnimationHooks<T, P> (props: _ViewProps) {
311
350
  }, {} as { [propName: string]: string | number })
312
351
  }
313
352
  // ** 生成动画样式
314
- return useAnimatedStyle(() => {
353
+ // eslint-disable-next-line react-hooks/rules-of-hooks
354
+ const animationStyle = useAnimatedStyle(() => {
315
355
  // console.info(`useAnimatedStyle styles=`, originalStyle)
316
356
  return animatedStyleKeys.value.reduce((styles, key) => {
317
357
  // console.info('getAnimationStyles', key, shareValMap[key].value)
@@ -327,6 +367,11 @@ export default function useAnimationHooks<T, P> (props: _ViewProps) {
327
367
  styles[key] = shareValMap[key].value
328
368
  }
329
369
  return styles
330
- }, Object.assign({}, originalStyle) as ExtendedViewStyle)
370
+ }, {} as ExtendedViewStyle)
331
371
  })
372
+
373
+ return {
374
+ enableStyleAnimation: enableAnimationRef.current,
375
+ animationStyle
376
+ }
332
377
  }
@@ -1,11 +1,14 @@
1
1
  import { useEffect, useCallback, useMemo, useRef, ReactNode, ReactElement, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement } from 'react'
2
- import { LayoutChangeEvent, TextStyle, ImageProps, Image } from 'react-native'
3
- import { isObject, isFunction, isNumber, hasOwn, diffAndCloneA, error, warn, getFocusedNavigation } from '@mpxjs/utils'
4
- import { VarContext } from './context'
2
+ import { LayoutChangeEvent, TextStyle, ImageProps, Image, Platform } from 'react-native'
3
+ import { isObject, isFunction, isNumber, hasOwn, diffAndCloneA, error, warn } from '@mpxjs/utils'
4
+ import { VarContext, ScrollViewContext } from './context'
5
5
  import { ExpressionParser, parseFunc, ReplaceSource } from './parser'
6
6
  import { initialWindowMetrics } from 'react-native-safe-area-context'
7
+ import { useNavigation } from '@react-navigation/native'
7
8
  import FastImage, { FastImageProps } from '@d11/react-native-fast-image'
8
- import type { AnyFunc, ExtendedFunctionComponent } from './types/common'
9
+ import type { AnyFunc, ExtendedFunctionComponent, ExtendedViewStyle } from './types/common'
10
+ import { runOnJS } from 'react-native-reanimated'
11
+ import { Gesture } from 'react-native-gesture-handler'
9
12
 
10
13
  export const TEXT_STYLE_REGEX = /color|font.*|text.*|letterSpacing|lineHeight|includeFontPadding|writingDirection/
11
14
  export const PERCENT_REGEX = /^\s*-?\d+(\.\d+)?%\s*$/
@@ -18,8 +21,15 @@ export const HIDDEN_STYLE = {
18
21
  opacity: 0
19
22
  }
20
23
 
21
- const varDecRegExp = /^--.*/
24
+ declare const __mpx_mode__: 'ios' | 'android'
25
+
26
+ export const isIOS = __mpx_mode__ === 'ios'
27
+ export const isAndroid = __mpx_mode__ === 'android'
28
+
29
+ const varDecRegExp = /^--/
22
30
  const varUseRegExp = /var\(/
31
+ const unoVarDecRegExp = /^--un-/
32
+ const unoVarUseRegExp = /var\(--un-/
23
33
  const calcUseRegExp = /calc\(/
24
34
  const envUseRegExp = /env\(/
25
35
 
@@ -30,14 +40,13 @@ const safeAreaInsetMap: Record<string, 'top' | 'right' | 'bottom' | 'left'> = {
30
40
  'safe-area-inset-left': 'left'
31
41
  }
32
42
 
33
- function getSafeAreaInset (name: string) {
34
- const navigation = getFocusedNavigation()
43
+ function getSafeAreaInset (name: string, navigation: Record<string, any>) {
35
44
  const insets = extendObject({}, initialWindowMetrics?.insets, navigation?.insets)
36
45
  return insets[safeAreaInsetMap[name]]
37
46
  }
38
47
 
39
48
  export function omit<T, K extends string> (obj: T, fields: K[]): Omit<T, K> {
40
- const shallowCopy: any = Object.assign({}, obj)
49
+ const shallowCopy: any = extendObject({}, obj)
41
50
  for (let i = 0; i < fields.length; i += 1) {
42
51
  const key = fields[i]
43
52
  delete shallowCopy[key]
@@ -77,7 +86,7 @@ export const parseInlineStyle = (inlineStyle = ''): Record<string, string> => {
77
86
  const [k, v, ...rest] = style.split(':')
78
87
  if (rest.length || !v || !k) return styleObj
79
88
  const key = k.trim().replace(/-./g, c => c.substring(1).toUpperCase())
80
- return Object.assign(styleObj, { [key]: global.__formatValue(v.trim()) })
89
+ return extendObject(styleObj, { [key]: global.__formatValue(v.trim()) })
81
90
  }, {})
82
91
  }
83
92
 
@@ -230,7 +239,7 @@ function transformVar (styleObj: Record<string, any>, varKeyPaths: Array<Array<s
230
239
  })
231
240
  }
232
241
 
233
- function transformEnv (styleObj: Record<string, any>, envKeyPaths: Array<Array<string>>) {
242
+ function transformEnv (styleObj: Record<string, any>, envKeyPaths: Array<Array<string>>, navigation: Record<string, any>) {
234
243
  envKeyPaths.forEach((envKeyPath) => {
235
244
  setStyle(styleObj, envKeyPath, ({ target, key, value }) => {
236
245
  const parsed = parseFunc(value, 'env')
@@ -238,7 +247,7 @@ function transformEnv (styleObj: Record<string, any>, envKeyPaths: Array<Array<s
238
247
  parsed.forEach(({ start, end, args }) => {
239
248
  const name = args[0]
240
249
  const fallback = args[1] || ''
241
- const value = '' + (getSafeAreaInset(name) ?? global.__formatValue(fallback))
250
+ const value = '' + (getSafeAreaInset(name, navigation) ?? global.__formatValue(fallback))
242
251
  replaced.replace(start, end - 1, value)
243
252
  })
244
253
  target[key] = global.__formatValue(replaced.source())
@@ -286,20 +295,27 @@ interface TransformStyleConfig {
286
295
 
287
296
  export function useTransformStyle (styleObj: Record<string, any> = {}, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight }: TransformStyleConfig) {
288
297
  const varStyle: Record<string, any> = {}
298
+ const unoVarStyle: Record<string, any> = {}
289
299
  const normalStyle: Record<string, any> = {}
300
+ const normalStyleRef = useRef<Record<string, any>>({})
301
+ const normalStyleChangedRef = useRef(false)
290
302
  let hasVarDec = false
291
303
  let hasVarUse = false
292
304
  let hasSelfPercent = false
293
305
  const varKeyPaths: Array<Array<string>> = []
306
+ const unoVarKeyPaths: Array<Array<string>> = []
294
307
  const percentKeyPaths: Array<Array<string>> = []
295
308
  const calcKeyPaths: Array<Array<string>> = []
296
309
  const envKeyPaths: Array<Array<string>> = []
297
310
  const [width, setWidth] = useState(0)
298
311
  const [height, setHeight] = useState(0)
312
+ const navigation = useNavigation()
299
313
 
300
314
  function varVisitor ({ key, value, keyPath }: VisitorArg) {
301
315
  if (keyPath.length === 1) {
302
- if (varDecRegExp.test(key)) {
316
+ if (unoVarDecRegExp.test(key)) {
317
+ unoVarStyle[key] = value
318
+ } else if (varDecRegExp.test(key)) {
303
319
  hasVarDec = true
304
320
  varStyle[key] = value
305
321
  } else {
@@ -308,25 +324,32 @@ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableV
308
324
  }
309
325
  }
310
326
  // 对于var定义中使用的var无需替换值,可以通过resolveVar递归解析出值
311
- if (!varDecRegExp.test(key) && varUseRegExp.test(value)) {
312
- hasVarUse = true
313
- varKeyPaths.push(keyPath.slice())
327
+ if (!varDecRegExp.test(key)) {
328
+ // 一般情况下一个样式属性中不会混用unocss var和普通css var,可分开进行互斥处理
329
+ if (unoVarUseRegExp.test(value)) {
330
+ unoVarKeyPaths.push(keyPath.slice())
331
+ } else if (varUseRegExp.test(value)) {
332
+ hasVarUse = true
333
+ varKeyPaths.push(keyPath.slice())
334
+ }
314
335
  }
315
336
  }
316
337
 
317
- // traverse var
338
+ // traverse var & generate normalStyle
318
339
  traverseStyle(styleObj, [varVisitor])
340
+
319
341
  hasVarDec = hasVarDec || !!externalVarContext
320
342
  enableVar = enableVar || hasVarDec || hasVarUse
321
343
  const enableVarRef = useRef(enableVar)
322
344
  if (enableVarRef.current !== enableVar) {
323
345
  error('css variable use/declare should be stable in the component lifecycle, or you can set [enable-var] with true.')
324
346
  }
325
- // apply var
347
+ // apply css var
326
348
  const varContextRef = useRef({})
327
349
  if (enableVarRef.current) {
350
+ // eslint-disable-next-line react-hooks/rules-of-hooks
328
351
  const varContext = useContext(VarContext)
329
- const newVarContext = Object.assign({}, varContext, externalVarContext, varStyle)
352
+ const newVarContext = extendObject({}, varContext, externalVarContext, varStyle)
330
353
  // 缓存比较newVarContext是否发生变化
331
354
  if (diffAndCloneA(varContextRef.current, newVarContext).diff) {
332
355
  varContextRef.current = newVarContext
@@ -334,70 +357,86 @@ export function useTransformStyle (styleObj: Record<string, any> = {}, { enableV
334
357
  transformVar(normalStyle, varKeyPaths, varContextRef.current)
335
358
  }
336
359
 
337
- function envVisitor ({ value, keyPath }: VisitorArg) {
338
- if (envUseRegExp.test(value)) {
339
- envKeyPaths.push(keyPath.slice())
340
- }
360
+ // apply unocss var
361
+ if (unoVarKeyPaths.length) {
362
+ transformVar(normalStyle, unoVarKeyPaths, unoVarStyle)
341
363
  }
342
364
 
343
- function calcVisitor ({ value, keyPath }: VisitorArg) {
344
- if (calcUseRegExp.test(value)) {
345
- calcKeyPaths.push(keyPath.slice())
346
- }
365
+ const { clone, diff } = diffAndCloneA(normalStyle, normalStyleRef.current)
366
+ if (diff) {
367
+ normalStyleRef.current = clone
368
+ normalStyleChangedRef.current = !normalStyleChangedRef.current
347
369
  }
348
370
 
349
- function percentVisitor ({ key, value, keyPath }: VisitorArg) {
350
- if (hasOwn(selfPercentRule, key) && PERCENT_REGEX.test(value)) {
351
- hasSelfPercent = true
352
- percentKeyPaths.push(keyPath.slice())
353
- } else if ((key === 'fontSize' || key === 'lineHeight') && PERCENT_REGEX.test(value)) {
354
- percentKeyPaths.push(keyPath.slice())
371
+ const memoResult = useMemo(() => {
372
+ // transform can be memoized
373
+ function envVisitor ({ value, keyPath }: VisitorArg) {
374
+ if (envUseRegExp.test(value)) {
375
+ envKeyPaths.push(keyPath.slice())
376
+ }
355
377
  }
356
- }
357
378
 
358
- // traverse env & calc & percent
359
- traverseStyle(normalStyle, [envVisitor, percentVisitor, calcVisitor])
379
+ function calcVisitor ({ value, keyPath }: VisitorArg) {
380
+ if (calcUseRegExp.test(value)) {
381
+ calcKeyPaths.push(keyPath.slice())
382
+ }
383
+ }
360
384
 
361
- const percentConfig = {
362
- width,
363
- height,
364
- fontSize: normalStyle.fontSize,
365
- parentWidth,
366
- parentHeight,
367
- parentFontSize
368
- }
385
+ function percentVisitor ({ key, value, keyPath }: VisitorArg) {
386
+ if (hasOwn(selfPercentRule, key) && PERCENT_REGEX.test(value)) {
387
+ hasSelfPercent = true
388
+ percentKeyPaths.push(keyPath.slice())
389
+ } else if ((key === 'fontSize' || key === 'lineHeight') && PERCENT_REGEX.test(value)) {
390
+ percentKeyPaths.push(keyPath.slice())
391
+ }
392
+ }
369
393
 
370
- // apply env
371
- transformEnv(normalStyle, envKeyPaths)
372
- // apply percent
373
- transformPercent(normalStyle, percentKeyPaths, percentConfig)
374
- // apply calc
375
- transformCalc(normalStyle, calcKeyPaths, (value: string, key: string) => {
376
- if (PERCENT_REGEX.test(value)) {
377
- const resolved = resolvePercent(value, key, percentConfig)
378
- return typeof resolved === 'number' ? resolved : 0
379
- } else {
380
- const formatted = global.__formatValue(value)
381
- if (typeof formatted === 'number') {
382
- return formatted
394
+ // traverse env & calc & percent
395
+ traverseStyle(normalStyle, [envVisitor, percentVisitor, calcVisitor])
396
+
397
+ const percentConfig = {
398
+ width,
399
+ height,
400
+ fontSize: normalStyle.fontSize,
401
+ parentWidth,
402
+ parentHeight,
403
+ parentFontSize
404
+ }
405
+
406
+ // apply env
407
+ transformEnv(normalStyle, envKeyPaths, navigation)
408
+ // apply percent
409
+ transformPercent(normalStyle, percentKeyPaths, percentConfig)
410
+ // apply calc
411
+ transformCalc(normalStyle, calcKeyPaths, (value: string, key: string) => {
412
+ if (PERCENT_REGEX.test(value)) {
413
+ const resolved = resolvePercent(value, key, percentConfig)
414
+ return typeof resolved === 'number' ? resolved : 0
383
415
  } else {
384
- warn('calc() only support number, px, rpx, % temporarily.')
385
- return 0
416
+ const formatted = global.__formatValue(value)
417
+ if (typeof formatted === 'number') {
418
+ return formatted
419
+ } else {
420
+ warn('calc() only support number, px, rpx, % temporarily.')
421
+ return 0
422
+ }
386
423
  }
424
+ })
425
+ // transform number enum stringify
426
+ transformStringify(normalStyle)
427
+
428
+ return {
429
+ normalStyle,
430
+ hasSelfPercent
387
431
  }
388
- })
389
- // transform number enum stringify
390
- transformStringify(normalStyle)
432
+ }, [normalStyleChangedRef.current, width, height, parentWidth, parentHeight, parentFontSize])
391
433
 
392
- return {
393
- normalStyle,
394
- hasSelfPercent,
434
+ return extendObject({
395
435
  hasVarDec,
396
- enableVarRef,
397
436
  varContextRef,
398
437
  setWidth,
399
438
  setHeight
400
- }
439
+ }, memoResult)
401
440
  }
402
441
 
403
442
  export interface VisitorArg {
@@ -477,7 +516,7 @@ interface LayoutConfig {
477
516
  export const useLayout = ({ props, hasSelfPercent, setWidth, setHeight, onLayout, nodeRef }: LayoutConfig) => {
478
517
  const layoutRef = useRef({})
479
518
  const hasLayoutRef = useRef(false)
480
- const layoutStyle: Record<string, any> = !hasLayoutRef.current && hasSelfPercent ? HIDDEN_STYLE : {}
519
+ const layoutStyle = useMemo(() => { return !hasLayoutRef.current && hasSelfPercent ? HIDDEN_STYLE : {} }, [hasLayoutRef.current])
481
520
  const layoutProps: Record<string, any> = {}
482
521
  const enableOffset = props['enable-offset']
483
522
  if (hasSelfPercent || onLayout || enableOffset) {
@@ -534,13 +573,14 @@ export const debounce = <T extends AnyFunc> (
534
573
  ): ((...args: Parameters<T>) => void) & { clear: () => void } => {
535
574
  let timer: any
536
575
  const wrapper = (...args: ReadonlyArray<any>) => {
537
- clearTimeout(timer)
576
+ timer && clearTimeout(timer)
538
577
  timer = setTimeout(() => {
539
578
  func(...args)
540
579
  }, delay)
541
580
  }
542
581
  wrapper.clear = () => {
543
- clearTimeout(timer)
582
+ timer && clearTimeout(timer)
583
+ timer = null
544
584
  }
545
585
  return wrapper
546
586
  }
@@ -564,8 +604,8 @@ export const useStableCallback = <T extends AnyFunc | null | undefined> (
564
604
  )
565
605
  }
566
606
 
567
- export const usePrevious = <T, > (value: T): T | undefined => {
568
- const ref = useRef<T | undefined>(undefined)
607
+ export function usePrevious<T> (value: T): T | undefined {
608
+ const ref = useRef<T | undefined>()
569
609
  const prev = ref.current
570
610
  ref.current = value
571
611
  return prev
@@ -610,3 +650,67 @@ export function pickStyle (styleObj: Record<string, any> = {}, pickedKeys: Array
610
650
  return acc
611
651
  }, {})
612
652
  }
653
+
654
+ export function useHover ({ enableHover, hoverStartTime, hoverStayTime, disabled } : { enableHover: boolean, hoverStartTime: number, hoverStayTime: number, disabled?: boolean }) {
655
+ const enableHoverRef = useRef(enableHover)
656
+ if (enableHoverRef.current !== enableHover) {
657
+ error('[Mpx runtime error]: hover-class use should be stable in the component lifecycle.')
658
+ }
659
+
660
+ if (!enableHoverRef.current) return { isHover: false }
661
+ // eslint-disable-next-line react-hooks/rules-of-hooks
662
+ const gestureRef = useContext(ScrollViewContext).gestureRef
663
+ // eslint-disable-next-line react-hooks/rules-of-hooks
664
+ const [isHover, setIsHover] = useState(false)
665
+ // eslint-disable-next-line react-hooks/rules-of-hooks
666
+ const dataRef = useRef<{
667
+ startTimer?: ReturnType<typeof setTimeout>
668
+ stayTimer?: ReturnType<typeof setTimeout>
669
+ }>({})
670
+
671
+ // eslint-disable-next-line react-hooks/rules-of-hooks
672
+ useEffect(() => {
673
+ return () => {
674
+ dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer)
675
+ dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer)
676
+ }
677
+ }, [])
678
+
679
+ const setStartTimer = () => {
680
+ if (disabled) return
681
+ dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer)
682
+ dataRef.current.startTimer = setTimeout(() => {
683
+ setIsHover(true)
684
+ }, +hoverStartTime)
685
+ }
686
+
687
+ const setStayTimer = () => {
688
+ if (disabled) return
689
+ dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer)
690
+ dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer)
691
+ dataRef.current.stayTimer = setTimeout(() => {
692
+ setIsHover(false)
693
+ }, +hoverStayTime)
694
+ }
695
+ // eslint-disable-next-line react-hooks/rules-of-hooks
696
+ const gesture = useMemo(() => {
697
+ return Gesture.Pan()
698
+ .onTouchesDown(() => {
699
+ 'worklet'
700
+ runOnJS(setStartTimer)()
701
+ })
702
+ .onTouchesUp(() => {
703
+ 'worklet'
704
+ runOnJS(setStayTimer)()
705
+ })
706
+ }, [])
707
+
708
+ if (gestureRef) {
709
+ gesture.simultaneousWithExternalGesture(gestureRef)
710
+ }
711
+
712
+ return {
713
+ isHover,
714
+ gesture
715
+ }
716
+ }
@@ -30,10 +30,10 @@
30
30
  if (this.disabled) {
31
31
  return
32
32
  }
33
+ this.isChecked = !this.isChecked
33
34
  if (this.group) {
34
35
  this.group.notifyChange()
35
36
  }
36
- this.isChecked = !this.isChecked
37
37
  }
38
38
  },
39
39
  force: true