@mpxjs/webpack-plugin 2.10.4-beta.9 → 2.10.5

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 (34) hide show
  1. package/LICENSE +433 -0
  2. package/lib/platform/json/wx/index.js +1 -0
  3. package/lib/platform/style/wx/index.js +22 -21
  4. package/lib/platform/template/wx/component-config/button.js +1 -1
  5. package/lib/platform/template/wx/component-config/index.js +1 -5
  6. package/lib/platform/template/wx/component-config/input.js +1 -1
  7. package/lib/react/processJSON.js +6 -7
  8. package/lib/react/processScript.js +9 -1
  9. package/lib/react/script-helper.js +5 -1
  10. package/lib/runtime/components/react/context.ts +3 -12
  11. package/lib/runtime/components/react/dist/context.js +1 -4
  12. package/lib/runtime/components/react/dist/mpx-picker-view-column/index.jsx +3 -3
  13. package/lib/runtime/components/react/dist/mpx-scroll-view.jsx +6 -17
  14. package/lib/runtime/components/react/dist/mpx-view.jsx +11 -4
  15. package/lib/runtime/components/react/dist/mpx-web-view.jsx +13 -13
  16. package/lib/runtime/components/react/dist/useAnimationHooks.js +27 -4
  17. package/lib/runtime/components/react/dist/utils.jsx +86 -107
  18. package/lib/runtime/components/react/mpx-picker-view-column/index.tsx +3 -3
  19. package/lib/runtime/components/react/mpx-scroll-view.tsx +50 -68
  20. package/lib/runtime/components/react/mpx-view.tsx +15 -5
  21. package/lib/runtime/components/react/mpx-web-view.tsx +12 -12
  22. package/lib/runtime/components/react/useAnimationHooks.ts +30 -9
  23. package/lib/runtime/components/react/utils.tsx +92 -110
  24. package/lib/runtime/components/web/mpx-scroll-view.vue +4 -21
  25. package/lib/template-compiler/compiler.js +1 -1
  26. package/package.json +3 -3
  27. package/lib/platform/template/wx/component-config/sticky-header.js +0 -23
  28. package/lib/platform/template/wx/component-config/sticky-section.js +0 -23
  29. package/lib/runtime/components/react/dist/mpx-sticky-header.jsx +0 -112
  30. package/lib/runtime/components/react/dist/mpx-sticky-section.jsx +0 -45
  31. package/lib/runtime/components/react/mpx-sticky-header.tsx +0 -176
  32. package/lib/runtime/components/react/mpx-sticky-section.tsx +0 -96
  33. package/lib/runtime/components/web/mpx-sticky-header.vue +0 -91
  34. package/lib/runtime/components/web/mpx-sticky-section.vue +0 -15
@@ -32,7 +32,6 @@
32
32
  * ✔ bindscroll
33
33
  */
34
34
  import { ScrollView, RefreshControl, Gesture, GestureDetector } from 'react-native-gesture-handler';
35
- import { Animated as RNAnimated } from 'react-native';
36
35
  import { isValidElement, Children, useRef, useState, useEffect, forwardRef, useContext, useMemo, createElement } from 'react';
37
36
  import Animated, { useAnimatedRef, useSharedValue, withTiming, useAnimatedStyle, runOnJS } from 'react-native-reanimated';
38
37
  import { warn } from '@mpxjs/utils';
@@ -40,11 +39,9 @@ import useInnerProps, { getCustomEvent } from './getInnerListeners';
40
39
  import useNodesRef from './useNodesRef';
41
40
  import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, HIDDEN_STYLE } from './utils';
42
41
  import { IntersectionObserverContext, ScrollViewContext } from './context';
43
- const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView);
44
42
  const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
45
43
  const { textProps, innerProps: props = {} } = splitProps(scrollViewProps);
46
- const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'refresher-threshold': refresherThreshold = 45, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, 'enable-sticky': enableSticky, 'scroll-event-throttle': scrollEventThrottle = 0, __selectRef } = props;
47
- const scrollOffset = useRef(new RNAnimated.Value(0)).current;
44
+ const { enhanced = false, bounces = true, style = {}, binddragstart, binddragging, binddragend, bindtouchstart, bindtouchmove, bindtouchend, 'scroll-x': scrollX = false, 'scroll-y': scrollY = false, 'enable-back-to-top': enableBackToTop = false, 'enable-trigger-intersection-observer': enableTriggerIntersectionObserver = false, 'paging-enabled': pagingEnabled = false, 'upper-threshold': upperThreshold = 50, 'lower-threshold': lowerThreshold = 50, 'scroll-with-animation': scrollWithAnimation = false, 'refresher-enabled': refresherEnabled, 'refresher-default-style': refresherDefaultStyle, 'refresher-background': refresherBackground, 'refresher-threshold': refresherThreshold = 45, 'show-scrollbar': showScrollbar = true, 'scroll-into-view': scrollIntoView = '', 'scroll-top': scrollTop = 0, 'scroll-left': scrollLeft = 0, 'refresher-triggered': refresherTriggered, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight, 'simultaneous-handlers': originSimultaneousHandlers, 'wait-for': waitFor, 'scroll-event-throttle': scrollEventThrottle = 0, __selectRef } = props;
48
45
  const simultaneousHandlers = flatGesture(originSimultaneousHandlers);
49
46
  const waitForHandlers = flatGesture(waitFor);
50
47
  const snapScrollTop = useRef(0);
@@ -92,13 +89,12 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
92
89
  },
93
90
  gestureRef: scrollViewRef
94
91
  });
95
- const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
96
92
  const contextValue = useMemo(() => {
97
93
  return {
98
- gestureRef: scrollViewRef,
99
- scrollOffset
94
+ gestureRef: scrollViewRef
100
95
  };
101
96
  }, []);
97
+ const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout });
102
98
  const hasRefresherLayoutRef = useRef(false);
103
99
  // layout 完成前先隐藏,避免安卓闪烁问题
104
100
  const refresherLayoutStyle = useMemo(() => { return !hasRefresherLayoutRef.current ? HIDDEN_STYLE : {}; }, [hasRefresherLayoutRef.current]);
@@ -324,12 +320,6 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
324
320
  updateScrollOptions(e, { scrollLeft, scrollTop });
325
321
  updateIntersection();
326
322
  }
327
- const scrollHandler = RNAnimated.event([{ nativeEvent: { contentOffset: { y: scrollOffset } } }], {
328
- useNativeDriver: true,
329
- listener: (event) => {
330
- onScroll(event);
331
- }
332
- });
333
323
  function onScrollDragStart(e) {
334
324
  hasCallScrollToLower.current = false;
335
325
  hasCallScrollToUpper.current = false;
@@ -486,7 +476,7 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
486
476
  scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
487
477
  bounces: false,
488
478
  ref: scrollViewRef,
489
- onScroll: enableSticky ? scrollHandler : onScroll,
479
+ onScroll: onScroll,
490
480
  onContentSizeChange: onContentSizeChange,
491
481
  bindtouchstart: ((enhanced && binddragstart) || bindtouchstart) && onScrollTouchStart,
492
482
  bindtouchmove: ((enhanced && binddragging) || bindtouchmove) && onScrollTouchMove,
@@ -528,14 +518,13 @@ const _ScrollView = forwardRef((scrollViewProps = {}, ref) => {
528
518
  'bindscrolltolower',
529
519
  'bindrefresherrefresh'
530
520
  ], { layoutRef });
531
- const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView;
532
- const withRefresherScrollView = createElement(GestureDetector, { gesture: panGesture }, createElement(ScrollViewComponent, innerProps, createElement(Animated.View, { style: [refresherAnimatedStyle, refresherLayoutStyle], onLayout: onRefresherLayout }, refresherContent), createElement(Animated.View, { style: contentAnimatedStyle }, createElement(ScrollViewContext.Provider, { value: contextValue }, wrapChildren(extendObject({}, props, { children: otherContent }), {
521
+ const withRefresherScrollView = createElement(GestureDetector, { gesture: panGesture }, createElement(ScrollView, innerProps, createElement(Animated.View, { style: [refresherAnimatedStyle, refresherLayoutStyle], onLayout: onRefresherLayout }, refresherContent), createElement(Animated.View, { style: contentAnimatedStyle }, createElement(ScrollViewContext.Provider, { value: contextValue }, wrapChildren(extendObject({}, props, { children: otherContent }), {
533
522
  hasVarDec,
534
523
  varContext: varContextRef.current,
535
524
  textStyle,
536
525
  textProps
537
526
  })))));
538
- const commonScrollView = createElement(ScrollViewComponent, extendObject({}, innerProps, {
527
+ const commonScrollView = createElement(ScrollView, extendObject(innerProps, {
539
528
  refreshControl: refresherEnabled
540
529
  ? createElement(RefreshControl, extendObject({
541
530
  progressBackgroundColor: refresherBackground,
@@ -11,7 +11,7 @@ import Animated from 'react-native-reanimated';
11
11
  import useAnimationHooks from './useAnimationHooks';
12
12
  import useNodesRef from './useNodesRef';
13
13
  import { parseUrl, PERCENT_REGEX, splitStyle, splitProps, useTransformStyle, wrapChildren, useLayout, renderImage, pickStyle, extendObject, useHover } from './utils';
14
- import { error } from '@mpxjs/utils';
14
+ import { error, isFunction } from '@mpxjs/utils';
15
15
  import LinearGradient from 'react-native-linear-gradient';
16
16
  import { GestureDetector } from 'react-native-gesture-handler';
17
17
  import Portal from './mpx-portal';
@@ -543,7 +543,7 @@ function wrapWithChildren(props, { hasVarDec, enableBackground, textStyle, backg
543
543
  }
544
544
  const _View = forwardRef((viewProps, ref) => {
545
545
  const { textProps, innerProps: props = {} } = splitProps(viewProps);
546
- 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;
546
+ 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, catchtransitionend, bindtransitionend } = props;
547
547
  // 默认样式
548
548
  const defaultStyle = style.display === 'flex'
549
549
  ? {
@@ -575,10 +575,17 @@ const _View = forwardRef((viewProps, ref) => {
575
575
  });
576
576
  const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef });
577
577
  const viewStyle = extendObject({}, innerStyle, layoutStyle);
578
+ const transitionend = isFunction(catchtransitionend)
579
+ ? catchtransitionend
580
+ : isFunction(bindtransitionend)
581
+ ? bindtransitionend
582
+ : undefined;
578
583
  const { enableStyleAnimation, animationStyle } = useAnimationHooks({
579
- enableAnimation,
584
+ layoutRef,
580
585
  animation,
581
- style: viewStyle
586
+ enableAnimation,
587
+ style: viewStyle,
588
+ transitionend
582
589
  });
583
590
  const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
584
591
  ref: nodeRef,
@@ -1,4 +1,4 @@
1
- import { forwardRef, useRef, useContext, useMemo, useState } from 'react';
1
+ import { forwardRef, useRef, useContext, useMemo, useState, useEffect } from 'react';
2
2
  import { warn, isFunction } from '@mpxjs/utils';
3
3
  import Portal from './mpx-portal/index';
4
4
  import { getCustomEvent } from './getInnerListeners';
@@ -74,17 +74,17 @@ const _WebView = forwardRef((props, ref) => {
74
74
  isNavigateBack.current = false;
75
75
  };
76
76
  const navigation = useNavigation();
77
- // useEffect(() => {
78
- // let beforeRemoveSubscription:any
79
- // if (__mpx_mode__ !== 'ios') {
80
- // beforeRemoveSubscription = navigation?.addListener?.('beforeRemove', beforeRemoveHandle)
81
- // }
82
- // return () => {
83
- // if (isFunction(beforeRemoveSubscription)) {
84
- // beforeRemoveSubscription()
85
- // }
86
- // }
87
- // }, [])
77
+ useEffect(() => {
78
+ let beforeRemoveSubscription;
79
+ if (__mpx_mode__ !== 'ios') {
80
+ beforeRemoveSubscription = navigation?.addListener?.('beforeRemove', beforeRemoveHandle);
81
+ }
82
+ return () => {
83
+ if (isFunction(beforeRemoveSubscription)) {
84
+ beforeRemoveSubscription();
85
+ }
86
+ };
87
+ }, []);
88
88
  useNodesRef(props, ref, webViewRef, {
89
89
  style: defaultWebViewStyle
90
90
  });
@@ -160,7 +160,7 @@ const _WebView = forwardRef((props, ref) => {
160
160
  { // case下不允许直接声明,包个块解决该问题
161
161
  const title = postData._documentTitle?.trim();
162
162
  if (title !== undefined) {
163
- navigation && navigation.setPageConfig({ navigationBarTitleText: title });
163
+ navigation && navigation.setOptions({ title });
164
164
  }
165
165
  }
166
166
  break;
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useMemo, useRef } from 'react';
2
- import { Easing, useSharedValue, withTiming, useAnimatedStyle, withSequence, withDelay, makeMutable, cancelAnimation } from 'react-native-reanimated';
3
- import { error, hasOwn } from '@mpxjs/utils';
2
+ import { Easing, useSharedValue, withTiming, useAnimatedStyle, withSequence, withDelay, makeMutable, cancelAnimation, runOnJS } from 'react-native-reanimated';
3
+ import { error, hasOwn, collectDataset } from '@mpxjs/utils';
4
4
  // 微信 timingFunction 和 RN Easing 对应关系
5
5
  const EasingKey = {
6
6
  linear: Easing.linear,
@@ -140,7 +140,7 @@ function getTransformObj(transforms) {
140
140
  }, {});
141
141
  }
142
142
  export default function useAnimationHooks(props) {
143
- const { style = {}, animation, enableAnimation } = props;
143
+ const { style = {}, animation, enableAnimation, transitionend, layoutRef } = props;
144
144
  const enableStyleAnimation = enableAnimation || !!animation;
145
145
  const enableAnimationRef = useRef(enableStyleAnimation);
146
146
  if (enableAnimationRef.current !== enableStyleAnimation) {
@@ -249,10 +249,33 @@ export default function useAnimationHooks(props) {
249
249
  });
250
250
  });
251
251
  }
252
+ function withTimingCallback(finished, current, duration) {
253
+ if (!transitionend)
254
+ return;
255
+ const target = {
256
+ id: animation?.id || -1,
257
+ dataset: collectDataset(props),
258
+ offsetLeft: layoutRef?.current?.offsetLeft || 0,
259
+ offsetTop: layoutRef?.current?.offsetTop || 0
260
+ };
261
+ transitionend({
262
+ type: 'transitionend',
263
+ // elapsedTime 对齐wx 单位s
264
+ detail: { elapsedTime: duration ? duration / 1000 : 0, finished, current },
265
+ target,
266
+ currentTarget: target,
267
+ timeStamp: Date.now()
268
+ });
269
+ }
252
270
  // 创建单个animation
253
271
  function getAnimation({ key, value }, { delay, duration, easing }, callback) {
254
272
  const animation = typeof callback === 'function'
255
- ? withTiming(value, { duration, easing }, callback)
273
+ ? withTiming(value, { duration, easing }, (finished, current) => {
274
+ callback(finished, current);
275
+ if (transitionend && finished) {
276
+ runOnJS(withTimingCallback)(finished, current, duration);
277
+ }
278
+ })
256
279
  : withTiming(value, { duration, easing });
257
280
  return delay ? withDelay(delay, animation) : animation;
258
281
  }
@@ -26,6 +26,7 @@ const unoVarDecRegExp = /^--un-/;
26
26
  const unoVarUseRegExp = /var\(--un-/;
27
27
  const calcUseRegExp = /calc\(/;
28
28
  const envUseRegExp = /env\(/;
29
+ const filterRegExp = /(calc|env|%)/;
29
30
  const safeAreaInsetMap = {
30
31
  'safe-area-inset-top': 'top',
31
32
  'safe-area-inset-right': 'right',
@@ -181,10 +182,11 @@ function resolveVar(input, varContext) {
181
182
  });
182
183
  return global.__formatValue(replaced.source());
183
184
  }
184
- function transformVar(styleObj, varKeyPaths, varContext) {
185
+ function transformVar(styleObj, varKeyPaths, varContext, visitOther) {
185
186
  varKeyPaths.forEach((varKeyPath) => {
186
187
  setStyle(styleObj, varKeyPath, ({ target, key, value }) => {
187
188
  target[key] = resolveVar(value, varContext);
189
+ visitOther({ target, key, value: target[key], keyPath: varKeyPath });
188
190
  });
189
191
  });
190
192
  }
@@ -224,28 +226,33 @@ function transformCalc(styleObj, calcKeyPaths, formatter) {
224
226
  });
225
227
  });
226
228
  }
227
- const stringifyProps = ['fontWeight'];
228
229
  function transformStringify(styleObj) {
229
- stringifyProps.forEach((prop) => {
230
- if (isNumber(styleObj[prop])) {
231
- styleObj[prop] = '' + styleObj[prop];
232
- }
233
- });
230
+ if (isNumber(styleObj.fontWeight)) {
231
+ styleObj.fontWeight = '' + styleObj.fontWeight;
232
+ }
233
+ }
234
+ function transformPosition(styleObj, meta) {
235
+ if (styleObj.position === 'fixed') {
236
+ styleObj.position = 'absolute';
237
+ meta.hasPositionFixed = true;
238
+ }
234
239
  }
235
240
  export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight }) {
236
241
  const varStyle = {};
237
242
  const unoVarStyle = {};
238
243
  const normalStyle = {};
239
- const normalStyleRef = useRef({});
240
- const normalStyleChangedRef = useRef(false);
241
244
  let hasVarDec = false;
242
245
  let hasVarUse = false;
246
+ let hasSelfPercent = false;
243
247
  const varKeyPaths = [];
244
248
  const unoVarKeyPaths = [];
249
+ const percentKeyPaths = [];
250
+ const calcKeyPaths = [];
251
+ const envKeyPaths = [];
245
252
  const [width, setWidth] = useState(0);
246
253
  const [height, setHeight] = useState(0);
247
254
  const navigation = useNavigation();
248
- function varVisitor({ key, value, keyPath }) {
255
+ function varVisitor({ target, key, value, keyPath }) {
249
256
  if (keyPath.length === 1) {
250
257
  if (unoVarDecRegExp.test(key)) {
251
258
  unoVarStyle[key] = value;
@@ -269,6 +276,33 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
269
276
  hasVarUse = true;
270
277
  varKeyPaths.push(keyPath.slice());
271
278
  }
279
+ else {
280
+ visitOther({ target, key, value, keyPath });
281
+ }
282
+ }
283
+ }
284
+ function envVisitor({ value, keyPath }) {
285
+ if (envUseRegExp.test(value)) {
286
+ envKeyPaths.push(keyPath.slice());
287
+ }
288
+ }
289
+ function calcVisitor({ value, keyPath }) {
290
+ if (calcUseRegExp.test(value)) {
291
+ calcKeyPaths.push(keyPath.slice());
292
+ }
293
+ }
294
+ function percentVisitor({ key, value, keyPath }) {
295
+ if (hasOwn(selfPercentRule, key) && PERCENT_REGEX.test(value)) {
296
+ hasSelfPercent = true;
297
+ percentKeyPaths.push(keyPath.slice());
298
+ }
299
+ else if ((key === 'fontSize' || key === 'lineHeight') && PERCENT_REGEX.test(value)) {
300
+ percentKeyPaths.push(keyPath.slice());
301
+ }
302
+ }
303
+ function visitOther({ target, key, value, keyPath }) {
304
+ if (filterRegExp.test(value)) {
305
+ [envVisitor, percentVisitor, calcVisitor].forEach(visitor => visitor({ target, key, value, keyPath }));
272
306
  }
273
307
  }
274
308
  // traverse var & generate normalStyle
@@ -289,96 +323,57 @@ export function useTransformStyle(styleObj = {}, { enableVar, externalVarContext
289
323
  if (diffAndCloneA(varContextRef.current, newVarContext).diff) {
290
324
  varContextRef.current = newVarContext;
291
325
  }
292
- transformVar(normalStyle, varKeyPaths, varContextRef.current);
326
+ transformVar(normalStyle, varKeyPaths, varContextRef.current, visitOther);
293
327
  }
294
328
  // apply unocss var
295
329
  if (unoVarKeyPaths.length) {
296
- transformVar(normalStyle, unoVarKeyPaths, unoVarStyle);
297
- }
298
- const { clone, diff } = diffAndCloneA(normalStyle, normalStyleRef.current);
299
- if (diff) {
300
- normalStyleRef.current = clone;
301
- normalStyleChangedRef.current = !normalStyleChangedRef.current;
330
+ transformVar(normalStyle, unoVarKeyPaths, unoVarStyle, visitOther);
302
331
  }
303
- const memoResult = useMemo(() => {
304
- let hasSelfPercent = false;
305
- let hasPositionFixed = false;
306
- const percentKeyPaths = [];
307
- const calcKeyPaths = [];
308
- const envKeyPaths = [];
309
- // transform can be memoized
310
- function envVisitor({ value, keyPath }) {
311
- if (envUseRegExp.test(value)) {
312
- envKeyPaths.push(keyPath.slice());
313
- }
314
- }
315
- function calcVisitor({ value, keyPath }) {
316
- if (calcUseRegExp.test(value)) {
317
- calcKeyPaths.push(keyPath.slice());
318
- }
319
- }
320
- function percentVisitor({ key, value, keyPath }) {
321
- if (hasOwn(selfPercentRule, key) && PERCENT_REGEX.test(value)) {
322
- hasSelfPercent = true;
323
- percentKeyPaths.push(keyPath.slice());
324
- }
325
- else if ((key === 'fontSize' || key === 'lineHeight') && PERCENT_REGEX.test(value)) {
326
- percentKeyPaths.push(keyPath.slice());
327
- }
328
- }
329
- function transformPosition(styleObj) {
330
- if (styleObj.position === 'fixed') {
331
- hasPositionFixed = true;
332
- styleObj.position = 'absolute';
333
- }
332
+ const percentConfig = {
333
+ width,
334
+ height,
335
+ fontSize: normalStyle.fontSize,
336
+ parentWidth,
337
+ parentHeight,
338
+ parentFontSize
339
+ };
340
+ const positionMeta = {
341
+ hasPositionFixed: false
342
+ };
343
+ // apply env
344
+ transformEnv(normalStyle, envKeyPaths, navigation);
345
+ // apply percent
346
+ transformPercent(normalStyle, percentKeyPaths, percentConfig);
347
+ // apply calc
348
+ transformCalc(normalStyle, calcKeyPaths, (value, key) => {
349
+ if (PERCENT_REGEX.test(value)) {
350
+ const resolved = resolvePercent(value, key, percentConfig);
351
+ return typeof resolved === 'number' ? resolved : 0;
334
352
  }
335
- // traverse env & calc & percent
336
- traverseStyle(normalStyle, [envVisitor, percentVisitor, calcVisitor]);
337
- const percentConfig = {
338
- width,
339
- height,
340
- fontSize: normalStyle.fontSize,
341
- parentWidth,
342
- parentHeight,
343
- parentFontSize
344
- };
345
- // apply env
346
- transformEnv(normalStyle, envKeyPaths, navigation);
347
- // apply percent
348
- transformPercent(normalStyle, percentKeyPaths, percentConfig);
349
- // apply calc
350
- transformCalc(normalStyle, calcKeyPaths, (value, key) => {
351
- if (PERCENT_REGEX.test(value)) {
352
- const resolved = resolvePercent(value, key, percentConfig);
353
- return typeof resolved === 'number' ? resolved : 0;
353
+ else {
354
+ const formatted = global.__formatValue(value);
355
+ if (typeof formatted === 'number') {
356
+ return formatted;
354
357
  }
355
358
  else {
356
- const formatted = global.__formatValue(value);
357
- if (typeof formatted === 'number') {
358
- return formatted;
359
- }
360
- else {
361
- warn('calc() only support number, px, rpx, % temporarily.');
362
- return 0;
363
- }
359
+ warn('calc() only support number, px, rpx, % temporarily.');
360
+ return 0;
364
361
  }
365
- });
366
- // apply position
367
- transformPosition(normalStyle);
368
- // transform number enum stringify
369
- transformStringify(normalStyle);
370
- return {
371
- normalStyle,
372
- hasSelfPercent,
373
- hasPositionFixed
374
- };
375
- }, [normalStyleChangedRef.current, width, height, parentWidth, parentHeight, parentFontSize]);
376
- return extendObject({
362
+ }
363
+ });
364
+ // apply position
365
+ transformPosition(normalStyle, positionMeta);
366
+ // transform number enum stringify
367
+ transformStringify(normalStyle);
368
+ return {
377
369
  hasVarDec,
378
370
  varContextRef,
379
371
  setWidth,
380
- setHeight
381
- }, memoResult);
372
+ setHeight,
373
+ normalStyle,
374
+ hasSelfPercent,
375
+ hasPositionFixed: positionMeta.hasPositionFixed
376
+ };
382
377
  }
383
378
  export function traverseStyle(styleObj, visitors) {
384
379
  const keyPath = [];
@@ -387,12 +382,7 @@ export function traverseStyle(styleObj, visitors) {
387
382
  target.forEach((value, index) => {
388
383
  const key = String(index);
389
384
  keyPath.push(key);
390
- visitors.forEach(visitor => visitor({
391
- target,
392
- key,
393
- value,
394
- keyPath
395
- }));
385
+ visitors.forEach(visitor => visitor({ target, key, value, keyPath }));
396
386
  traverse(value);
397
387
  keyPath.pop();
398
388
  });
@@ -451,18 +441,7 @@ export const useLayout = ({ props, hasSelfPercent, setWidth, setHeight, onLayout
451
441
  if (enableOffset) {
452
442
  nodeRef.current?.measure((x, y, width, height, offsetLeft, offsetTop) => {
453
443
  const { y: navigationY = 0 } = navigation?.layout || {};
454
- layoutRef.current = {
455
- x,
456
- y: y - navigationY,
457
- width,
458
- height,
459
- offsetLeft,
460
- offsetTop: offsetTop - navigationY,
461
- _x: x,
462
- _y: y,
463
- _offsetLeft: offsetLeft,
464
- _offsetTop: offsetTop
465
- };
444
+ layoutRef.current = { x, y: y - navigationY, width, height, offsetLeft, offsetTop: offsetTop - navigationY };
466
445
  });
467
446
  }
468
447
  onLayout && onLayout(e);
@@ -1,7 +1,7 @@
1
1
  import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react'
2
2
  import { GestureResponderEvent, LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, ScrollView, StyleSheet, View } from 'react-native'
3
3
  import Reanimated, { AnimatedRef, useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated'
4
- import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS } from '../utils'
4
+ import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, isHarmony } from '../utils'
5
5
  import useNodesRef, { HandlerRef } from '../useNodesRef'
6
6
  import PickerIndicator from './pickerViewIndicator'
7
7
  import PickerMask from './pickerViewMask'
@@ -209,9 +209,9 @@ const _PickerViewColumn = forwardRef<HandlerRef<ScrollView & View, ColumnProps>,
209
209
 
210
210
  const onScrollEndDrag = useCallback((e: NativeSyntheticEvent<NativeScrollEvent>) => {
211
211
  dragging.current = false
212
- if (isIOS) {
212
+ if (!isAndroid) {
213
213
  const { y } = e.nativeEvent.contentOffset
214
- if (y % itemRawH === 0) {
214
+ if (y % itemRawH === 0 || (isHarmony && y > snapToOffsets[maxIndex])) {
215
215
  onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } })
216
216
  } else if (y > 0 && y < snapToOffsets[maxIndex]) {
217
217
  timerResetPosition.current = setTimeout(() => {