@mpxjs/webpack-plugin 2.9.70 → 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 (60) hide show
  1. package/lib/platform/template/wx/component-config/movable-view.js +8 -1
  2. package/lib/platform/template/wx/component-config/picker-view.js +1 -5
  3. package/lib/platform/template/wx/component-config/scroll-view.js +1 -1
  4. package/lib/platform/template/wx/index.js +0 -4
  5. package/lib/runtime/components/react/context.ts +8 -0
  6. package/lib/runtime/components/react/dist/context.js +2 -0
  7. package/lib/runtime/components/react/dist/getInnerListeners.js +34 -31
  8. package/lib/runtime/components/react/dist/mpx-button.jsx +16 -44
  9. package/lib/runtime/components/react/dist/mpx-movable-view.jsx +93 -58
  10. package/lib/runtime/components/react/dist/mpx-navigator.jsx +1 -1
  11. package/lib/runtime/components/react/dist/mpx-picker-view-column-item.jsx +35 -0
  12. package/lib/runtime/components/react/dist/mpx-picker-view-column.jsx +151 -127
  13. package/lib/runtime/components/react/dist/mpx-picker-view.jsx +38 -34
  14. package/lib/runtime/components/react/dist/mpx-rich-text/index.jsx +10 -11
  15. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +11 -4
  16. package/lib/runtime/components/react/dist/mpx-swiper-item.jsx +31 -8
  17. package/lib/runtime/components/react/dist/mpx-swiper.jsx +670 -0
  18. package/lib/runtime/components/react/dist/mpx-view.jsx +15 -53
  19. package/lib/runtime/components/react/dist/pickerFaces.js +7 -6
  20. package/lib/runtime/components/react/dist/pickerVIewContext.js +14 -0
  21. package/lib/runtime/components/react/dist/pickerViewIndicator.jsx +23 -0
  22. package/lib/runtime/components/react/dist/pickerViewMask.jsx +18 -0
  23. package/lib/runtime/components/react/dist/useAnimationHooks.js +20 -2
  24. package/lib/runtime/components/react/dist/utils.jsx +74 -11
  25. package/lib/runtime/components/react/getInnerListeners.ts +43 -32
  26. package/lib/runtime/components/react/mpx-button.tsx +20 -57
  27. package/lib/runtime/components/react/mpx-movable-view.tsx +119 -74
  28. package/lib/runtime/components/react/mpx-navigator.tsx +1 -1
  29. package/lib/runtime/components/react/mpx-picker-view-column-item.tsx +76 -0
  30. package/lib/runtime/components/react/mpx-picker-view-column.tsx +206 -183
  31. package/lib/runtime/components/react/mpx-picker-view.tsx +49 -48
  32. package/lib/runtime/components/react/mpx-rich-text/index.tsx +12 -18
  33. package/lib/runtime/components/react/mpx-scroll-view.tsx +21 -10
  34. package/lib/runtime/components/react/mpx-swiper-item.tsx +45 -11
  35. package/lib/runtime/components/react/mpx-swiper.tsx +742 -0
  36. package/lib/runtime/components/react/mpx-view.tsx +18 -65
  37. package/lib/runtime/components/react/pickerFaces.ts +10 -7
  38. package/lib/runtime/components/react/pickerVIewContext.ts +27 -0
  39. package/lib/runtime/components/react/pickerViewIndicator.tsx +34 -0
  40. package/lib/runtime/components/react/pickerViewMask.tsx +30 -0
  41. package/lib/runtime/components/react/types/{getInnerListeners.ts → getInnerListeners.d.ts} +4 -5
  42. package/lib/runtime/components/react/types/global.d.ts +10 -0
  43. package/lib/runtime/components/react/useAnimationHooks.ts +24 -3
  44. package/lib/runtime/components/react/utils.tsx +85 -12
  45. package/lib/runtime/components/web/mpx-checkbox.vue +1 -1
  46. package/lib/runtime/components/web/mpx-picker-view-column.vue +9 -4
  47. package/lib/template-compiler/compiler.js +61 -13
  48. package/lib/wxss/loader.js +15 -2
  49. package/package.json +3 -3
  50. package/lib/runtime/components/react/dist/mpx-swiper/carouse.jsx +0 -480
  51. package/lib/runtime/components/react/dist/mpx-swiper/index.jsx +0 -68
  52. package/lib/runtime/components/react/dist/mpx-swiper/type.js +0 -1
  53. package/lib/runtime/components/react/dist/pickerOverlay.jsx +0 -21
  54. package/lib/runtime/components/react/dist/types/common.js +0 -1
  55. package/lib/runtime/components/react/dist/types/getInnerListeners.js +0 -1
  56. package/lib/runtime/components/react/mpx-swiper/carouse.tsx +0 -527
  57. package/lib/runtime/components/react/mpx-swiper/index.tsx +0 -80
  58. package/lib/runtime/components/react/mpx-swiper/type.ts +0 -87
  59. package/lib/runtime/components/react/pickerOverlay.tsx +0 -32
  60. /package/lib/runtime/components/react/types/{common.ts → common.d.ts} +0 -0
@@ -10,9 +10,10 @@ import useInnerProps from './getInnerListeners';
10
10
  import Animated from 'react-native-reanimated';
11
11
  import useAnimationHooks from './useAnimationHooks';
12
12
  import useNodesRef from './useNodesRef';
13
- import { parseUrl, PERCENT_REGEX, splitStyle, splitProps, useTransformStyle, wrapChildren, useLayout, renderImage, pickStyle, extendObject } from './utils';
13
+ import { parseUrl, PERCENT_REGEX, splitStyle, splitProps, useTransformStyle, wrapChildren, useLayout, renderImage, pickStyle, extendObject, useHover } from './utils';
14
14
  import { error } from '@mpxjs/utils';
15
15
  import LinearGradient from 'react-native-linear-gradient';
16
+ import { GestureDetector } from 'react-native-gesture-handler';
16
17
  const linearMap = new Map([
17
18
  ['top', 0],
18
19
  ['bottom', 180],
@@ -542,7 +543,6 @@ function wrapWithChildren(props, { hasVarDec, enableBackground, textStyle, backg
542
543
  const _View = forwardRef((viewProps, ref) => {
543
544
  const { textProps, innerProps: props = {} } = splitProps(viewProps);
544
545
  let { style = {}, 'hover-style': hoverStyle, 'hover-start-time': hoverStartTime = 50, 'hover-stay-time': hoverStayTime = 400, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'enable-background': enableBackground, 'enable-fast-image': enableFastImage, 'enable-animation': enableAnimation, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, animation } = props;
545
- const [isHover, setIsHover] = useState(false);
546
546
  // 默认样式
547
547
  const defaultStyle = style.display === 'flex'
548
548
  ? {
@@ -552,6 +552,8 @@ const _View = forwardRef((viewProps, ref) => {
552
552
  flexWrap: 'nowrap'
553
553
  }
554
554
  : {};
555
+ const enableHover = !!hoverStyle;
556
+ const { isHover, gesture } = useHover({ enableHover, hoverStartTime, hoverStayTime });
555
557
  const styleObj = extendObject({}, defaultStyle, style, isHover ? hoverStyle : {});
556
558
  const { normalStyle, hasSelfPercent, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, {
557
559
  enableVar,
@@ -570,60 +572,17 @@ const _View = forwardRef((viewProps, ref) => {
570
572
  useNodesRef(props, ref, nodeRef, {
571
573
  style: normalStyle
572
574
  });
573
- const dataRef = useRef({});
574
- useEffect(() => {
575
- return () => {
576
- dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer);
577
- dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer);
578
- };
579
- }, []);
580
- const setStartTimer = () => {
581
- dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer);
582
- dataRef.current.startTimer = setTimeout(() => {
583
- setIsHover(true);
584
- }, +hoverStartTime);
585
- };
586
- const setStayTimer = () => {
587
- dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer);
588
- dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer);
589
- dataRef.current.stayTimer = setTimeout(() => {
590
- setIsHover(false);
591
- }, +hoverStayTime);
592
- };
593
- function onTouchStart(e) {
594
- const { bindtouchstart } = props;
595
- bindtouchstart && bindtouchstart(e);
596
- setStartTimer();
597
- }
598
- function onTouchEnd(e) {
599
- const { bindtouchend } = props;
600
- bindtouchend && bindtouchend(e);
601
- setStayTimer();
602
- }
603
575
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef });
604
576
  const viewStyle = extendObject({}, innerStyle, layoutStyle);
605
- enableAnimation = enableAnimation || !!animation;
606
- const enableAnimationRef = useRef(enableAnimation);
607
- if (enableAnimationRef.current !== enableAnimation) {
608
- error('[Mpx runtime error]: animation use should be stable in the component lifecycle, or you can set [enable-animation] with true.');
609
- }
610
- const finalStyle = enableAnimationRef.current
611
- ? [viewStyle,
612
- // eslint-disable-next-line react-hooks/rules-of-hooks
613
- useAnimationHooks({
614
- animation,
615
- style: viewStyle
616
- })]
617
- : viewStyle;
577
+ const { enableStyleAnimation, animationStyle } = useAnimationHooks({
578
+ enableAnimation,
579
+ animation,
580
+ style: viewStyle
581
+ });
618
582
  const innerProps = useInnerProps(props, extendObject({
619
583
  ref: nodeRef,
620
- style: finalStyle
621
- }, layoutProps, hoverStyle
622
- ? {
623
- bindtouchstart: onTouchStart,
624
- bindtouchend: onTouchEnd
625
- }
626
- : {}), [
584
+ style: enableStyleAnimation ? [viewStyle, animationStyle] : viewStyle
585
+ }, layoutProps), [
627
586
  'hover-start-time',
628
587
  'hover-stay-time',
629
588
  'hover-style',
@@ -641,9 +600,12 @@ const _View = forwardRef((viewProps, ref) => {
641
600
  innerStyle,
642
601
  enableFastImage
643
602
  });
644
- return enableAnimation
603
+ const BaseComponent = enableStyleAnimation
645
604
  ? createElement(Animated.View, innerProps, childNode)
646
605
  : createElement(View, innerProps, childNode);
606
+ return enableHover
607
+ ? createElement(GestureDetector, { gesture: gesture }, BaseComponent)
608
+ : BaseComponent;
647
609
  });
648
610
  _View.displayName = 'MpxView';
649
611
  export default _View;
@@ -37,15 +37,14 @@ export const createFaces = (itemHeight, visibleCount) => {
37
37
  const getOpacity = (index) => {
38
38
  const map = {
39
39
  0: 0,
40
- 1: 0.2,
41
- 2: 0.35,
42
- 3: 0.45,
43
- 4: 0.5
40
+ 1: 0.8,
41
+ 2: 0.9
44
42
  };
45
- return map[index] ?? Math.min(1, map[4] + index * 0.5);
43
+ return map[index] ?? Math.min(1, map[2] + index * 0.05);
46
44
  };
47
45
  const degrees = getDegreesRelativeCenter();
48
46
  const [screenHeight, offsets] = getScreenHeightsAndOffsets(degrees);
47
+ const scales = [0.973, 0.9, 0.8];
49
48
  return [
50
49
  // top items
51
50
  ...degrees
@@ -55,12 +54,13 @@ export const createFaces = (itemHeight, visibleCount) => {
55
54
  deg: degree,
56
55
  opacity: getOpacity(degrees.length - 1 - index),
57
56
  offsetY: -1 * offsets[index],
57
+ scale: scales[index],
58
58
  screenHeight: screenHeight[index]
59
59
  };
60
60
  })
61
61
  .reverse(),
62
62
  // center item
63
- { index: 0, deg: 0, opacity: 1, offsetY: 0, screenHeight: itemHeight },
63
+ { index: 0, deg: 0, opacity: 1, offsetY: 0, scale: 1, screenHeight: itemHeight },
64
64
  // bottom items
65
65
  ...degrees.map((degree, index) => {
66
66
  return {
@@ -68,6 +68,7 @@ export const createFaces = (itemHeight, visibleCount) => {
68
68
  deg: -1 * degree,
69
69
  opacity: getOpacity(degrees.length - 1 - index),
70
70
  offsetY: offsets[index],
71
+ scale: scales[index],
71
72
  screenHeight: screenHeight[index]
72
73
  };
73
74
  })
@@ -0,0 +1,14 @@
1
+ import { createContext, useContext } from 'react';
2
+ export const PickerViewColumnAnimationContext = createContext(undefined);
3
+ export const usePickerViewColumnAnimationContext = () => {
4
+ const value = useContext(PickerViewColumnAnimationContext);
5
+ if (value === undefined) {
6
+ throw new Error('usePickerViewColumnAnimationContext must be called from within PickerViewColumnAnimationContext.Provider!');
7
+ }
8
+ return value;
9
+ };
10
+ export const PickerViewStyleContext = createContext(undefined);
11
+ export const usePickerViewStyleContext = () => {
12
+ const value = useContext(PickerViewStyleContext);
13
+ return value;
14
+ };
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { StyleSheet, View } from 'react-native';
3
+ const _PickerViewIndicator = ({ itemHeight, indicatorItemStyle, indicatorContainerStyle }) => {
4
+ return (<View style={[styles.indicatorContainer, indicatorContainerStyle]} pointerEvents={'none'}>
5
+ <View style={[styles.selection, { height: itemHeight }, indicatorItemStyle]}/>
6
+ </View>);
7
+ };
8
+ const styles = StyleSheet.create({
9
+ indicatorContainer: {
10
+ ...StyleSheet.absoluteFillObject,
11
+ justifyContent: 'center',
12
+ alignItems: 'center',
13
+ zIndex: 200
14
+ },
15
+ selection: {
16
+ borderTopWidth: 1,
17
+ borderBottomWidth: 1,
18
+ borderColor: 'rgba(0, 0, 0, 0.05)',
19
+ alignSelf: 'stretch'
20
+ }
21
+ });
22
+ _PickerViewIndicator.displayName = 'MpxPickerViewIndicator';
23
+ export default _PickerViewIndicator;
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { StyleSheet, View } from 'react-native';
3
+ import LinearGradient from 'react-native-linear-gradient';
4
+ const _PickerViewMask = ({ itemHeight, maskContainerStyle }) => {
5
+ return (<View style={[styles.maskContainer, maskContainerStyle]} pointerEvents={'none'}>
6
+ <LinearGradient colors={['rgba(255,255,255,1)', 'rgba(255,255,255,0.5)']} style={{ flex: 1 }}/>
7
+ <View style={{ height: itemHeight }}/>
8
+ <LinearGradient colors={['rgba(255,255,255,0.5)', 'rgba(255,255,255,1)']} style={{ flex: 1 }}/>
9
+ </View>);
10
+ };
11
+ const styles = StyleSheet.create({
12
+ maskContainer: {
13
+ ...StyleSheet.absoluteFillObject,
14
+ zIndex: 100
15
+ }
16
+ });
17
+ _PickerViewMask.displayName = 'MpxPickerViewMask';
18
+ export default _PickerViewMask;
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useMemo, useRef } from 'react';
2
2
  import { Easing, useSharedValue, withTiming, useAnimatedStyle, withSequence, withDelay, makeMutable, cancelAnimation } from 'react-native-reanimated';
3
+ import { error } from '@mpxjs/utils';
3
4
  // 微信 timingFunction 和 RN Easing 对应关系
4
5
  const EasingKey = {
5
6
  linear: Easing.linear,
@@ -132,19 +133,29 @@ const formatStyle = (style) => {
132
133
  });
133
134
  };
134
135
  export default function useAnimationHooks(props) {
135
- const { style = {}, animation } = props;
136
+ const { style = {}, animation, enableAnimation } = props;
137
+ const enableStyleAnimation = enableAnimation || !!animation;
138
+ const enableAnimationRef = useRef(enableStyleAnimation);
139
+ if (enableAnimationRef.current !== enableStyleAnimation) {
140
+ error('[Mpx runtime error]: animation use should be stable in the component lifecycle, or you can set [enable-animation] with true.');
141
+ }
142
+ if (!enableAnimationRef.current)
143
+ return { enableStyleAnimation: false };
136
144
  const originalStyle = formatStyle(style);
137
145
  // id 标识
138
146
  const id = animation?.id || -1;
139
147
  // 有动画样式的 style key
148
+ // eslint-disable-next-line react-hooks/rules-of-hooks
140
149
  const animatedStyleKeys = useSharedValue([]);
141
150
  // 记录动画key的style样式值 没有的话设置为false
151
+ // eslint-disable-next-line react-hooks/rules-of-hooks
142
152
  const animatedKeys = useRef({});
143
153
  // const animatedKeys = useRef({} as {[propName: keyof ExtendedViewStyle]: boolean|number|string})
144
154
  // ** 全量 style prop sharedValue
145
155
  // 不能做增量的原因:
146
156
  // 1 尝试用 useRef,但 useAnimatedStyle 访问后的 ref 不能在增加新的值,被冻结
147
157
  // 2 尝试用 useSharedValue,因为实际触发的 style prop 需要是 sharedValue 才能驱动动画,若外层 shareValMap 也是 sharedValue,动画无法驱动。
158
+ // eslint-disable-next-line react-hooks/rules-of-hooks
148
159
  const shareValMap = useMemo(() => {
149
160
  return Object.keys(InitialValue).reduce((valMap, key) => {
150
161
  const defaultVal = getInitialVal(key, isTransform(key));
@@ -153,6 +164,7 @@ export default function useAnimationHooks(props) {
153
164
  }, {});
154
165
  }, []);
155
166
  // ** 获取动画样式prop & 驱动动画
167
+ // eslint-disable-next-line react-hooks/rules-of-hooks
156
168
  useEffect(() => {
157
169
  if (id === -1)
158
170
  return;
@@ -174,6 +186,7 @@ export default function useAnimationHooks(props) {
174
186
  // })
175
187
  // }, [style])
176
188
  // ** 清空动画
189
+ // eslint-disable-next-line react-hooks/rules-of-hooks
177
190
  useEffect(() => {
178
191
  return () => {
179
192
  Object.values(shareValMap).forEach((value) => {
@@ -306,7 +319,8 @@ export default function useAnimationHooks(props) {
306
319
  }, {});
307
320
  }
308
321
  // ** 生成动画样式
309
- return useAnimatedStyle(() => {
322
+ // eslint-disable-next-line react-hooks/rules-of-hooks
323
+ const animationStyle = useAnimatedStyle(() => {
310
324
  // console.info(`useAnimatedStyle styles=`, originalStyle)
311
325
  return animatedStyleKeys.value.reduce((styles, key) => {
312
326
  // console.info('getAnimationStyles', key, shareValMap[key].value)
@@ -325,4 +339,8 @@ export default function useAnimationHooks(props) {
325
339
  return styles;
326
340
  }, {});
327
341
  });
342
+ return {
343
+ enableStyleAnimation: enableAnimationRef.current,
344
+ animationStyle
345
+ };
328
346
  }
@@ -1,10 +1,13 @@
1
1
  import { useEffect, useCallback, useMemo, useRef, isValidElement, useContext, useState, Children, cloneElement } from 'react';
2
2
  import { Image } from 'react-native';
3
- import { isObject, isFunction, isNumber, hasOwn, diffAndCloneA, error, warn, getFocusedNavigation } from '@mpxjs/utils';
4
- import { VarContext } from './context';
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 from '@d11/react-native-fast-image';
9
+ import { runOnJS } from 'react-native-reanimated';
10
+ import { Gesture } from 'react-native-gesture-handler';
8
11
  export const TEXT_STYLE_REGEX = /color|font.*|text.*|letterSpacing|lineHeight|includeFontPadding|writingDirection/;
9
12
  export const PERCENT_REGEX = /^\s*-?\d+(\.\d+)?%\s*$/;
10
13
  export const URL_REGEX = /^\s*url\(["']?(.*?)["']?\)\s*$/;
@@ -15,6 +18,8 @@ export const DEFAULT_FONT_SIZE = 16;
15
18
  export const HIDDEN_STYLE = {
16
19
  opacity: 0
17
20
  };
21
+ export const isIOS = __mpx_mode__ === 'ios';
22
+ export const isAndroid = __mpx_mode__ === 'android';
18
23
  const varDecRegExp = /^--/;
19
24
  const varUseRegExp = /var\(/;
20
25
  const unoVarDecRegExp = /^--un-/;
@@ -27,8 +32,7 @@ const safeAreaInsetMap = {
27
32
  'safe-area-inset-bottom': 'bottom',
28
33
  'safe-area-inset-left': 'left'
29
34
  };
30
- function getSafeAreaInset(name) {
31
- const navigation = getFocusedNavigation();
35
+ function getSafeAreaInset(name, navigation) {
32
36
  const insets = extendObject({}, initialWindowMetrics?.insets, navigation?.insets);
33
37
  return insets[safeAreaInsetMap[name]];
34
38
  }
@@ -194,7 +198,7 @@ function transformVar(styleObj, varKeyPaths, varContext) {
194
198
  });
195
199
  });
196
200
  }
197
- function transformEnv(styleObj, envKeyPaths) {
201
+ function transformEnv(styleObj, envKeyPaths, navigation) {
198
202
  envKeyPaths.forEach((envKeyPath) => {
199
203
  setStyle(styleObj, envKeyPath, ({ target, key, value }) => {
200
204
  const parsed = parseFunc(value, 'env');
@@ -202,7 +206,7 @@ function transformEnv(styleObj, envKeyPaths) {
202
206
  parsed.forEach(({ start, end, args }) => {
203
207
  const name = args[0];
204
208
  const fallback = args[1] || '';
205
- const value = '' + (getSafeAreaInset(name) ?? global.__formatValue(fallback));
209
+ const value = '' + (getSafeAreaInset(name, navigation) ?? global.__formatValue(fallback));
206
210
  replaced.replace(start, end - 1, value);
207
211
  });
208
212
  target[key] = global.__formatValue(replaced.source());
@@ -254,6 +258,7 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
254
258
  const envKeyPaths = [];
255
259
  const [width, setWidth] = useState(0);
256
260
  const [height, setHeight] = useState(0);
261
+ const navigation = useNavigation();
257
262
  function varVisitor({ key, value, keyPath }) {
258
263
  if (keyPath.length === 1) {
259
264
  if (unoVarDecRegExp.test(key)) {
@@ -341,7 +346,7 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
341
346
  parentFontSize
342
347
  };
343
348
  // apply env
344
- transformEnv(normalStyle, envKeyPaths);
349
+ transformEnv(normalStyle, envKeyPaths, navigation);
345
350
  // apply percent
346
351
  transformPercent(normalStyle, percentKeyPaths, percentConfig);
347
352
  // apply calc
@@ -476,13 +481,14 @@ export function wrapChildren(props = {}, { hasVarDec, varContext, textStyle, tex
476
481
  export const debounce = (func, delay) => {
477
482
  let timer;
478
483
  const wrapper = (...args) => {
479
- clearTimeout(timer);
484
+ timer && clearTimeout(timer);
480
485
  timer = setTimeout(() => {
481
486
  func(...args);
482
487
  }, delay);
483
488
  };
484
489
  wrapper.clear = () => {
485
- clearTimeout(timer);
490
+ timer && clearTimeout(timer);
491
+ timer = null;
486
492
  };
487
493
  return wrapper;
488
494
  };
@@ -495,12 +501,12 @@ export const useStableCallback = (callback) => {
495
501
  ref.current = callback;
496
502
  return useCallback((...args) => ref.current?.(...args), []);
497
503
  };
498
- export const usePrevious = (value) => {
504
+ export function usePrevious(value) {
499
505
  const ref = useRef();
500
506
  const prev = ref.current;
501
507
  ref.current = value;
502
508
  return prev;
503
- };
509
+ }
504
510
  export function flatGesture(gestures = []) {
505
511
  return (gestures && gestures.flatMap((gesture) => {
506
512
  if (gesture && gesture.nodeRefs) {
@@ -529,3 +535,60 @@ export function pickStyle(styleObj = {}, pickedKeys, callback) {
529
535
  return acc;
530
536
  }, {});
531
537
  }
538
+ export function useHover({ enableHover, hoverStartTime, hoverStayTime, disabled }) {
539
+ const enableHoverRef = useRef(enableHover);
540
+ if (enableHoverRef.current !== enableHover) {
541
+ error('[Mpx runtime error]: hover-class use should be stable in the component lifecycle.');
542
+ }
543
+ if (!enableHoverRef.current)
544
+ return { isHover: false };
545
+ // eslint-disable-next-line react-hooks/rules-of-hooks
546
+ const gestureRef = useContext(ScrollViewContext).gestureRef;
547
+ // eslint-disable-next-line react-hooks/rules-of-hooks
548
+ const [isHover, setIsHover] = useState(false);
549
+ // eslint-disable-next-line react-hooks/rules-of-hooks
550
+ const dataRef = useRef({});
551
+ // eslint-disable-next-line react-hooks/rules-of-hooks
552
+ useEffect(() => {
553
+ return () => {
554
+ dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer);
555
+ dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer);
556
+ };
557
+ }, []);
558
+ const setStartTimer = () => {
559
+ if (disabled)
560
+ return;
561
+ dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer);
562
+ dataRef.current.startTimer = setTimeout(() => {
563
+ setIsHover(true);
564
+ }, +hoverStartTime);
565
+ };
566
+ const setStayTimer = () => {
567
+ if (disabled)
568
+ return;
569
+ dataRef.current.stayTimer && clearTimeout(dataRef.current.stayTimer);
570
+ dataRef.current.startTimer && clearTimeout(dataRef.current.startTimer);
571
+ dataRef.current.stayTimer = setTimeout(() => {
572
+ setIsHover(false);
573
+ }, +hoverStayTime);
574
+ };
575
+ // eslint-disable-next-line react-hooks/rules-of-hooks
576
+ const gesture = useMemo(() => {
577
+ return Gesture.Pan()
578
+ .onTouchesDown(() => {
579
+ 'worklet';
580
+ runOnJS(setStartTimer)();
581
+ })
582
+ .onTouchesUp(() => {
583
+ 'worklet';
584
+ runOnJS(setStayTimer)();
585
+ });
586
+ }, []);
587
+ if (gestureRef) {
588
+ gesture.simultaneousWithExternalGesture(gestureRef);
589
+ }
590
+ return {
591
+ isHover,
592
+ gesture
593
+ };
594
+ }
@@ -1,5 +1,6 @@
1
1
  import { useRef, useMemo, RefObject } from 'react'
2
2
  import { hasOwn, collectDataset } from '@mpxjs/utils'
3
+ import { useNavigation } from '@react-navigation/native'
3
4
  import { omit, extendObject } from './utils'
4
5
  import eventConfigMap from './event.config'
5
6
  import {
@@ -10,15 +11,22 @@ import {
10
11
  InnerRef,
11
12
  SetTimeoutReturnType,
12
13
  LayoutRef,
13
- NativeTouchEvent
14
+ NativeTouchEvent,
15
+ Navigation
14
16
  } from './types/getInnerListeners'
15
17
 
18
+ const globalEventState = {
19
+ needPress: true
20
+ }
21
+
16
22
  const getTouchEvent = (
17
23
  type: string,
18
24
  event: NativeTouchEvent,
19
25
  props: Props,
20
- config: UseInnerPropsConfig
26
+ config: UseInnerPropsConfig,
27
+ navigation: Navigation
21
28
  ) => {
29
+ const { y: navigationY = 0 } = navigation?.layout || {}
22
30
  const nativeEvent = event.nativeEvent
23
31
  const { timestamp, pageX, pageY, touches, changedTouches } = nativeEvent
24
32
  const { id } = props
@@ -49,24 +57,24 @@ const getTouchEvent = (
49
57
  target,
50
58
  detail: {
51
59
  x: pageX,
52
- y: pageY
60
+ y: pageY - navigationY
53
61
  },
54
62
  touches: touches.map((item) => {
55
63
  return {
56
64
  identifier: item.identifier,
57
65
  pageX: item.pageX,
58
- pageY: item.pageY,
59
- clientX: item.locationX,
60
- clientY: item.locationY
66
+ pageY: item.pageY - navigationY,
67
+ clientX: item.pageX,
68
+ clientY: item.pageY - navigationY
61
69
  }
62
70
  }),
63
71
  changedTouches: changedTouches.map((item) => {
64
72
  return {
65
73
  identifier: item.identifier,
66
74
  pageX: item.pageX,
67
- pageY: item.pageY,
68
- clientX: item.locationX,
69
- clientY: item.locationY
75
+ pageY: item.pageY - navigationY,
76
+ clientX: item.pageX,
77
+ clientY: item.pageY - navigationY
70
78
  }
71
79
  }),
72
80
  persist: event.persist,
@@ -105,7 +113,8 @@ function handleEmitEvent (
105
113
  type: string,
106
114
  oe: NativeTouchEvent,
107
115
  propsRef: Record<string, any>,
108
- config: UseInnerPropsConfig
116
+ config: UseInnerPropsConfig,
117
+ navigation: Navigation
109
118
  ) {
110
119
  events.forEach((event) => {
111
120
  if (propsRef.current[event]) {
@@ -114,7 +123,7 @@ function handleEmitEvent (
114
123
  oe.stopPropagation()
115
124
  }
116
125
  propsRef.current[event](
117
- getTouchEvent(type, oe, propsRef.current, config)
126
+ getTouchEvent(type, oe, propsRef.current, config, navigation)
118
127
  )
119
128
  }
120
129
  })
@@ -129,14 +138,14 @@ function checkIsNeedPress (e: NativeTouchEvent, type: 'bubble' | 'capture', ref:
129
138
  Math.abs(currentPageX - tapDetailInfo.x) > 3 ||
130
139
  Math.abs(currentPageY - tapDetailInfo.y) > 3
131
140
  ) {
132
- ref.current!.needPress[type] = false
141
+ globalEventState.needPress = false
133
142
  ref.current!.startTimer[type] &&
134
143
  clearTimeout(ref.current!.startTimer[type] as SetTimeoutReturnType)
135
144
  ref.current!.startTimer[type] = null
136
145
  }
137
146
  }
138
147
 
139
- function handleTouchstart (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig) {
148
+ function handleTouchstart (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) {
140
149
  e.persist()
141
150
  const bubbleTouchEvent = ['catchtouchstart', 'bindtouchstart']
142
151
  const bubblePressEvent = ['catchlongpress', 'bindlongpress']
@@ -149,7 +158,7 @@ function handleTouchstart (e: NativeTouchEvent, type: 'bubble' | 'capture', ref:
149
158
  'capture-bindlongpress'
150
159
  ]
151
160
  ref.current!.startTimer[type] = null
152
- ref.current!.needPress[type] = true
161
+ globalEventState.needPress = true
153
162
  const nativeEvent = e.nativeEvent
154
163
  ref.current!.mpxPressInfo.detail = {
155
164
  x: nativeEvent.changedTouches[0].pageX,
@@ -159,7 +168,7 @@ function handleTouchstart (e: NativeTouchEvent, type: 'bubble' | 'capture', ref:
159
168
  type === 'bubble' ? bubbleTouchEvent : captureTouchEvent
160
169
  const currentPressEvent =
161
170
  type === 'bubble' ? bubblePressEvent : capturePressEvent
162
- handleEmitEvent(currentTouchEvent, 'touchstart', e, propsRef, config)
171
+ handleEmitEvent(currentTouchEvent, 'touchstart', e, propsRef, config, navigation)
163
172
  const {
164
173
  catchlongpress,
165
174
  bindlongpress,
@@ -173,13 +182,14 @@ function handleTouchstart (e: NativeTouchEvent, type: 'bubble' | 'capture', ref:
173
182
  captureBindlongpress
174
183
  ) {
175
184
  ref.current!.startTimer[type] = setTimeout(() => {
176
- ref.current!.needPress[type] = false
177
- handleEmitEvent(currentPressEvent, 'longpress', e, propsRef, config)
185
+ // 只要触发过longpress, 全局就不再触发tap
186
+ globalEventState.needPress = false
187
+ handleEmitEvent(currentPressEvent, 'longpress', e, propsRef, config, navigation)
178
188
  }, 350)
179
189
  }
180
190
  }
181
191
 
182
- function handleTouchmove (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig) {
192
+ function handleTouchmove (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) {
183
193
  const bubbleTouchEvent = ['catchtouchmove', 'bindtouchmove']
184
194
  const captureTouchEvent = [
185
195
  'capture-catchtouchmove',
@@ -187,11 +197,11 @@ function handleTouchmove (e: NativeTouchEvent, type: 'bubble' | 'capture', ref:
187
197
  ]
188
198
  const currentTouchEvent =
189
199
  type === 'bubble' ? bubbleTouchEvent : captureTouchEvent
190
- handleEmitEvent(currentTouchEvent, 'touchmove', e, propsRef, config)
200
+ handleEmitEvent(currentTouchEvent, 'touchmove', e, propsRef, config, navigation)
191
201
  checkIsNeedPress(e, type, ref)
192
202
  }
193
203
 
194
- function handleTouchend (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig) {
204
+ function handleTouchend (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) {
195
205
  // move event may not be triggered
196
206
  checkIsNeedPress(e, type, ref)
197
207
  const bubbleTouchEvent = ['catchtouchend', 'bindtouchend']
@@ -208,19 +218,22 @@ function handleTouchend (e: NativeTouchEvent, type: 'bubble' | 'capture', ref: R
208
218
  ref.current!.startTimer[type] &&
209
219
  clearTimeout(ref.current!.startTimer[type] as SetTimeoutReturnType)
210
220
  ref.current!.startTimer[type] = null
211
- handleEmitEvent(currentTouchEvent, 'touchend', e, propsRef, config)
212
- if (ref.current!.needPress[type]) {
221
+ handleEmitEvent(currentTouchEvent, 'touchend', e, propsRef, config, navigation)
222
+ if (globalEventState.needPress) {
213
223
  if (type === 'bubble' && config.disableTap) {
214
224
  return
215
225
  }
216
- handleEmitEvent(currentTapEvent, 'tap', e, propsRef, config)
226
+ handleEmitEvent(currentTapEvent, 'tap', e, propsRef, config, navigation)
217
227
  }
218
228
  }
219
229
 
220
230
  function handleTouchcancel (
221
231
  e: NativeTouchEvent,
222
232
  type: 'bubble' | 'capture',
223
- ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig
233
+ ref: RefObject<InnerRef>,
234
+ propsRef: Record<string, any>,
235
+ config: UseInnerPropsConfig,
236
+ navigation: Navigation
224
237
  ) {
225
238
  const bubbleTouchEvent = ['catchtouchcancel', 'bindtouchcancel']
226
239
  const captureTouchEvent = [
@@ -232,11 +245,11 @@ function handleTouchcancel (
232
245
  ref.current!.startTimer[type] &&
233
246
  clearTimeout(ref.current!.startTimer[type] as SetTimeoutReturnType)
234
247
  ref.current!.startTimer[type] = null
235
- handleEmitEvent(currentTouchEvent, 'touchcancel', e, propsRef, config)
248
+ handleEmitEvent(currentTouchEvent, 'touchcancel', e, propsRef, config, navigation)
236
249
  }
237
250
 
238
251
  function createTouchEventHandler (eventName: 'onTouchStart'|'onTouchMove'|'onTouchEnd'|'onTouchCancel', type: 'bubble' | 'capture') {
239
- return (e: NativeTouchEvent, ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig) => {
252
+ return (e: NativeTouchEvent, ref: RefObject<InnerRef>, propsRef: Record<string, any>, config: UseInnerPropsConfig, navigation: Navigation) => {
240
253
  const handlerMap = {
241
254
  onTouchStart: handleTouchstart,
242
255
  onTouchMove: handleTouchmove,
@@ -246,7 +259,7 @@ function createTouchEventHandler (eventName: 'onTouchStart'|'onTouchMove'|'onTou
246
259
 
247
260
  const handler = handlerMap[eventName]
248
261
  if (handler) {
249
- handler(e, type, ref, propsRef, config)
262
+ handler(e, type, ref, propsRef, config, navigation)
250
263
  }
251
264
  }
252
265
  }
@@ -273,10 +286,6 @@ const useInnerProps = (
273
286
  bubble: null,
274
287
  capture: null
275
288
  },
276
- needPress: {
277
- bubble: false,
278
- capture: false
279
- },
280
289
  mpxPressInfo: {
281
290
  detail: {
282
291
  x: 0,
@@ -291,6 +300,8 @@ const useInnerProps = (
291
300
  layoutRef: { current: {} },
292
301
  disableTap: false
293
302
  }
303
+ const navigation = useNavigation()
304
+
294
305
  const removeProps = [
295
306
  'children',
296
307
  'enable-background',
@@ -332,7 +343,7 @@ const useInnerProps = (
332
343
  touchEventList.forEach((item) => {
333
344
  if (finalEventKeys.includes(item.eventName)) {
334
345
  events[item.eventName] = (e: NativeTouchEvent) =>
335
- item.handler(e, ref, propsRef, config)
346
+ item.handler(e, ref, propsRef, config, navigation)
336
347
  }
337
348
  })
338
349