zkit-ui 1.0.2 → 1.0.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/Button/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/Button/index.tsx"],"names":[],"mappings":"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"}
@@ -53,6 +53,7 @@ const LOADING_TIMING = {
53
53
  easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.cubic),
54
54
  reduceMotion: react_native_reanimated_1.ReduceMotion.System,
55
55
  };
56
+ const ANIMATED_EXIT_RESIDENCY_MS = LOADING_TIMING.duration + 40;
56
57
  const DISABLED_OPACITY = 0.52;
57
58
  const PRESSED_OPACITY = 0.82;
58
59
  const PRESSED_SCALE = 0.985;
@@ -373,7 +374,7 @@ function resolvePressEffect(effect, interactionDisabled) {
373
374
  return 'none';
374
375
  if (effect !== 'auto')
375
376
  return effect;
376
- return 'scale-opacity';
377
+ return 'opacity';
377
378
  }
378
379
  function resolvePressEffectFlags(effect) {
379
380
  return {
@@ -544,12 +545,10 @@ function requiresAnimatedPressEffect(effect) {
544
545
  effect === 'highlight' ||
545
546
  effect === 'scale-highlight');
546
547
  }
547
- function shouldUseStaticButtonPath({ disabled, loading, pressEffect, }) {
548
- if (loading)
549
- return false;
548
+ function shouldRequestAnimatedPressPath({ disabled, pressEffect, }) {
550
549
  if (disabled)
551
- return true;
552
- return !requiresAnimatedPressEffect(pressEffect);
550
+ return false;
551
+ return requiresAnimatedPressEffect(pressEffect);
553
552
  }
554
553
  function useButtonFrame({ variant = 'solid', tone = 'primary', size = 'md', shape = 'rounded', block = false, disabled = false, loading = false, icon, iconPlacement = 'start', iconOnly = false, color, colors, border, layout, gradient, shadow = 'none', children, textStyle, accessibilityLabel, }) {
555
554
  var _a, _b;
@@ -741,6 +740,101 @@ function ButtonStaticImpl({ variant = 'solid', tone = 'primary', size = 'md', sh
741
740
  contentStyle,
742
741
  ], children: [LinearGradientComponent && gradientCfg ? ((0, jsx_runtime_1.jsx)(LinearGradientComponent, { pointerEvents: "none", colors: gradientCfg.colors, start: gradientCfg.start, end: gradientCfg.end, style: [react_native_1.StyleSheet.absoluteFillObject, radiusStyle] })) : null, iconOnly ? ((icon !== null && icon !== void 0 ? icon : resolvedText)) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [icon && iconPlacement === 'start' ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [iconBaseBoxStyle, iconPlacedBoxStyle], children: icon })) : null, resolvedText, icon && iconPlacement === 'end' ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [iconBaseBoxStyle, iconPlacedBoxStyle], children: icon })) : null] }))] }) }));
743
742
  }
743
+ function ButtonPressAnimatedImpl({ variant = 'solid', tone = 'primary', size = 'md', shape = 'rounded', block = false, disabled = false, loading = false, loadingMode: _loadingMode = 'inline', pressEffect = 'auto', icon, iconPlacement = 'start', iconOnly = false, color, colors, border, layout, gradient, shadow = 'none', children, style, contentStyle, textStyle, accessibilityLabel, accessibilityState, hitSlop, onPress, onPressIn, onPressOut, testID, ...pressableProps }, ref) {
744
+ const frame = useButtonFrame({
745
+ accessibilityLabel,
746
+ block,
747
+ border,
748
+ children,
749
+ color,
750
+ colors,
751
+ disabled,
752
+ gradient,
753
+ icon,
754
+ iconOnly,
755
+ iconPlacement,
756
+ layout,
757
+ loading,
758
+ shadow,
759
+ shape,
760
+ size,
761
+ textStyle,
762
+ tone,
763
+ variant,
764
+ });
765
+ const { accentColor, borderStyle, contentSizeStyle, defaultHitSlop, gradientCfg, hasGradientBackground, hasIcon, iconBaseBoxStyle, iconPlacedBoxStyle, interactionDisabled, LinearGradientComponent, paddingStyle, radiusStyle, resolvedAccessibilityLabel, resolvedText, rootSizeStyle, shadowStyle, visualColors, visualDisabled, webCursorStyle, } = frame;
766
+ const pressSv = (0, react_native_reanimated_1.useSharedValue)(0);
767
+ const resolvedPressEffect = React.useMemo(() => resolvePressEffect(pressEffect, interactionDisabled), [interactionDisabled, pressEffect]);
768
+ const pressEffectFlags = React.useMemo(() => resolvePressEffectFlags(resolvedPressEffect), [resolvedPressEffect]);
769
+ const pressHighlightEnabled = pressEffectFlags.highlight;
770
+ const pressOpacityEnabled = pressEffectFlags.opacity;
771
+ const pressScaleEnabled = pressEffectFlags.scale;
772
+ const pressOverlay = React.useMemo(() => resolveOverlay({
773
+ accentColor,
774
+ colors,
775
+ hasGradientBackground,
776
+ variant,
777
+ }), [accentColor, colors, hasGradientBackground, variant]);
778
+ React.useEffect(() => {
779
+ if (interactionDisabled || resolvedPressEffect === 'none') {
780
+ pressSv.value = 0;
781
+ }
782
+ }, [interactionDisabled, pressSv, resolvedPressEffect]);
783
+ const rootAnimatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
784
+ const baseOpacity = visualDisabled ? DISABLED_OPACITY : 1;
785
+ const scale = pressScaleEnabled
786
+ ? (0, react_native_reanimated_1.interpolate)(pressSv.value, [0, 1], [1, PRESSED_SCALE])
787
+ : 1;
788
+ const opacity = pressOpacityEnabled
789
+ ? (0, react_native_reanimated_1.interpolate)(pressSv.value, [0, 1], [baseOpacity, PRESSED_OPACITY])
790
+ : baseOpacity;
791
+ return {
792
+ opacity,
793
+ transform: [{ scale }],
794
+ };
795
+ }, [pressOpacityEnabled, pressScaleEnabled, visualDisabled]);
796
+ const overlayAnimatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
797
+ const opacity = pressHighlightEnabled
798
+ ? (0, react_native_reanimated_1.interpolate)(pressSv.value, [0, 1], [0, pressOverlay.strength])
799
+ : 0;
800
+ return { opacity };
801
+ }, [pressHighlightEnabled, pressOverlay.strength]);
802
+ const handlePress = React.useCallback((event) => {
803
+ if (interactionDisabled)
804
+ return;
805
+ onPress === null || onPress === void 0 ? void 0 : onPress(event);
806
+ }, [interactionDisabled, onPress]);
807
+ const handlePressIn = React.useCallback((event) => {
808
+ if (!interactionDisabled && resolvedPressEffect !== 'none') {
809
+ pressSv.value = (0, react_native_reanimated_1.withTiming)(1, PRESS_TIMING);
810
+ }
811
+ onPressIn === null || onPressIn === void 0 ? void 0 : onPressIn(event);
812
+ }, [interactionDisabled, onPressIn, pressSv, resolvedPressEffect]);
813
+ const handlePressOut = React.useCallback((event) => {
814
+ if (!interactionDisabled && resolvedPressEffect !== 'none') {
815
+ pressSv.value = (0, react_native_reanimated_1.withTiming)(0, PRESS_TIMING);
816
+ }
817
+ onPressOut === null || onPressOut === void 0 ? void 0 : onPressOut(event);
818
+ }, [interactionDisabled, onPressOut, pressSv, resolvedPressEffect]);
819
+ return ((0, jsx_runtime_1.jsx)(AnimatedPressable, { ...pressableProps, ref: ref, accessibilityLabel: resolvedAccessibilityLabel, accessibilityRole: "button", accessibilityState: {
820
+ ...accessibilityState,
821
+ busy: Boolean(loading || (accessibilityState === null || accessibilityState === void 0 ? void 0 : accessibilityState.busy)),
822
+ disabled: Boolean(interactionDisabled || (accessibilityState === null || accessibilityState === void 0 ? void 0 : accessibilityState.disabled)),
823
+ }, disabled: interactionDisabled, hitSlop: hitSlop !== null && hitSlop !== void 0 ? hitSlop : defaultHitSlop, onPress: handlePress, onPressIn: handlePressIn, onPressOut: handlePressOut, style: [styles.root, rootSizeStyle, radiusStyle, shadowStyle, rootAnimatedStyle, webCursorStyle, style], testID: testID, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { pointerEvents: "none", style: [
824
+ styles.content,
825
+ contentSizeStyle,
826
+ paddingStyle,
827
+ radiusStyle,
828
+ borderStyle,
829
+ { backgroundColor: visualColors.backgroundColor },
830
+ contentStyle,
831
+ ], children: [LinearGradientComponent && gradientCfg ? ((0, jsx_runtime_1.jsx)(LinearGradientComponent, { pointerEvents: "none", colors: gradientCfg.colors, start: gradientCfg.start, end: gradientCfg.end, style: [react_native_1.StyleSheet.absoluteFillObject, radiusStyle] })) : null, pressHighlightEnabled ? ((0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { pointerEvents: "none", style: [
832
+ react_native_1.StyleSheet.absoluteFillObject,
833
+ radiusStyle,
834
+ { backgroundColor: pressOverlay.color },
835
+ overlayAnimatedStyle,
836
+ ] })) : null, iconOnly ? ((icon !== null && icon !== void 0 ? icon : resolvedText)) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [icon && iconPlacement === 'start' ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [iconBaseBoxStyle, iconPlacedBoxStyle], children: icon })) : null, resolvedText, icon && iconPlacement === 'end' ? ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [iconBaseBoxStyle, iconPlacedBoxStyle], children: icon })) : null] }))] }) }));
837
+ }
744
838
  function ButtonAnimatedImpl({ variant = 'solid', tone = 'primary', size = 'md', shape = 'rounded', block = false, disabled = false, loading = false, loadingMode = 'inline', pressEffect = 'auto', icon, iconPlacement = 'start', iconOnly = false, color, colors, border, layout, gradient, shadow = 'none', children, style, contentStyle, textStyle, accessibilityLabel, accessibilityState, hitSlop, onPress, onPressIn, onPressOut, testID, ...pressableProps }, ref) {
745
839
  const resolvedLoadingMode = iconOnly ? 'overlay' : loadingMode;
746
840
  const centeredLoading = resolvedLoadingMode !== 'inline';
@@ -773,7 +867,7 @@ function ButtonAnimatedImpl({ variant = 'solid', tone = 'primary', size = 'md',
773
867
  variant,
774
868
  }), [iconOnly, layout, metrics, variant]);
775
869
  const pressSv = (0, react_native_reanimated_1.useSharedValue)(0);
776
- const loadingSv = (0, react_native_reanimated_1.useSharedValue)(loading ? 1 : 0);
870
+ const loadingSv = (0, react_native_reanimated_1.useSharedValue)(0);
777
871
  const [spinnerMounted, setSpinnerMounted] = React.useState(loading);
778
872
  const hideSpinnerTimerRef = React.useRef(null);
779
873
  const resolvedPressEffect = React.useMemo(() => resolvePressEffect(pressEffect, interactionDisabled), [interactionDisabled, pressEffect]);
@@ -924,13 +1018,32 @@ function ButtonAnimatedImpl({ variant = 'solid', tone = 'primary', size = 'md',
924
1018
  }
925
1019
  const ButtonStaticWithRef = React.forwardRef(ButtonStaticImpl);
926
1020
  ButtonStaticWithRef.displayName = 'ButtonStatic';
1021
+ const ButtonPressAnimatedWithRef = React.forwardRef(ButtonPressAnimatedImpl);
1022
+ ButtonPressAnimatedWithRef.displayName = 'ButtonPressAnimated';
927
1023
  const ButtonAnimatedWithRef = React.forwardRef(ButtonAnimatedImpl);
928
- ButtonAnimatedWithRef.displayName = 'ButtonAnimated';
1024
+ ButtonAnimatedWithRef.displayName = 'ButtonLoadingAnimated';
929
1025
  function ButtonImpl(props, ref) {
930
- if (shouldUseStaticButtonPath(props)) {
931
- return (0, jsx_runtime_1.jsx)(ButtonStaticWithRef, { ...props, ref: ref });
1026
+ const loadingRequested = Boolean(props.loading);
1027
+ const animatedPressRequested = shouldRequestAnimatedPressPath(props);
1028
+ const [retainLoadingAnimated, setRetainLoadingAnimated] = React.useState(loadingRequested);
1029
+ React.useEffect(() => {
1030
+ if (loadingRequested) {
1031
+ setRetainLoadingAnimated(true);
1032
+ return undefined;
1033
+ }
1034
+ if (!retainLoadingAnimated)
1035
+ return undefined;
1036
+ const timer = setTimeout(() => {
1037
+ setRetainLoadingAnimated(false);
1038
+ }, ANIMATED_EXIT_RESIDENCY_MS);
1039
+ return () => {
1040
+ clearTimeout(timer);
1041
+ };
1042
+ }, [loadingRequested, retainLoadingAnimated]);
1043
+ if (loadingRequested || retainLoadingAnimated) {
1044
+ return (0, jsx_runtime_1.jsx)(ButtonAnimatedWithRef, { ...props, ref: ref });
932
1045
  }
933
- return (0, jsx_runtime_1.jsx)(ButtonAnimatedWithRef, { ...props, ref: ref });
1046
+ return animatedPressRequested ? ((0, jsx_runtime_1.jsx)(ButtonPressAnimatedWithRef, { ...props, ref: ref })) : ((0, jsx_runtime_1.jsx)(ButtonStaticWithRef, { ...props, ref: ref }));
934
1047
  }
935
1048
  const ButtonWithRef = React.forwardRef(ButtonImpl);
936
1049
  ButtonWithRef.displayName = 'Button';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zkit-ui",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -154,7 +154,7 @@ export function Demo() {
154
154
  }
155
155
  ```
156
156
 
157
- 渐变依赖 `expo-linear-gradient`,缺失时会使用第一段颜色作为稳定兜底。默认 `pressEffect="auto"` 走普通 `Pressable` 静态路径,并用原生 pressed style 做轻量透明度反馈;只有 `scale`、`scale-opacity`、`highlight`、`scale-highlight` 和 loading 过渡会升级到 Reanimated 动画路径。
157
+ 渐变依赖 `expo-linear-gradient`,缺失时会使用第一段颜色作为稳定兜底。默认 `pressEffect="auto"` 走普通 `Pressable` 静态路径,并用原生 pressed style 做轻量透明度反馈;只有 `scale`、`scale-opacity`、`highlight`、`scale-highlight` 和 loading 过渡会升级到 Reanimated 动画路径。动静态切换由 Button 内部自动管理,loading 退出动画结束后才会回到静态路径。
158
158
 
159
159
  ## 列表高频渲染
160
160
 
@@ -202,6 +202,7 @@ const LOADING_TIMING = {
202
202
  easing: Easing.out(Easing.cubic),
203
203
  reduceMotion: ReduceMotion.System,
204
204
  } as const;
205
+ const ANIMATED_EXIT_RESIDENCY_MS = LOADING_TIMING.duration + 40;
205
206
 
206
207
  const DISABLED_OPACITY = 0.52;
207
208
  const PRESSED_OPACITY = 0.82;
@@ -575,7 +576,7 @@ function resolveVisualColors({
575
576
  function resolvePressEffect(effect: ButtonPressEffect, interactionDisabled: boolean): ButtonPressEffect {
576
577
  if (interactionDisabled) return 'none';
577
578
  if (effect !== 'auto') return effect;
578
- return 'scale-opacity';
579
+ return 'opacity';
579
580
  }
580
581
 
581
582
  function resolvePressEffectFlags(effect: ButtonPressEffect): PressEffectFlags {
@@ -847,18 +848,15 @@ function requiresAnimatedPressEffect(effect: ButtonPressEffect | undefined) {
847
848
  );
848
849
  }
849
850
 
850
- function shouldUseStaticButtonPath({
851
+ function shouldRequestAnimatedPressPath({
851
852
  disabled,
852
- loading,
853
853
  pressEffect,
854
854
  }: {
855
855
  disabled?: boolean;
856
- loading?: boolean;
857
856
  pressEffect?: ButtonPressEffect;
858
857
  }) {
859
- if (loading) return false;
860
- if (disabled) return true;
861
- return !requiresAnimatedPressEffect(pressEffect);
858
+ if (disabled) return false;
859
+ return requiresAnimatedPressEffect(pressEffect);
862
860
  }
863
861
 
864
862
  function useButtonFrame({
@@ -1243,6 +1241,235 @@ function ButtonStaticImpl(
1243
1241
  );
1244
1242
  }
1245
1243
 
1244
+ function ButtonPressAnimatedImpl(
1245
+ {
1246
+ variant = 'solid',
1247
+ tone = 'primary',
1248
+ size = 'md',
1249
+ shape = 'rounded',
1250
+ block = false,
1251
+ disabled = false,
1252
+ loading = false,
1253
+ loadingMode: _loadingMode = 'inline',
1254
+ pressEffect = 'auto',
1255
+ icon,
1256
+ iconPlacement = 'start',
1257
+ iconOnly = false,
1258
+ color,
1259
+ colors,
1260
+ border,
1261
+ layout,
1262
+ gradient,
1263
+ shadow = 'none',
1264
+ children,
1265
+ style,
1266
+ contentStyle,
1267
+ textStyle,
1268
+ accessibilityLabel,
1269
+ accessibilityState,
1270
+ hitSlop,
1271
+ onPress,
1272
+ onPressIn,
1273
+ onPressOut,
1274
+ testID,
1275
+ ...pressableProps
1276
+ }: ButtonProps,
1277
+ ref: React.ForwardedRef<ButtonRef>
1278
+ ) {
1279
+ const frame = useButtonFrame({
1280
+ accessibilityLabel,
1281
+ block,
1282
+ border,
1283
+ children,
1284
+ color,
1285
+ colors,
1286
+ disabled,
1287
+ gradient,
1288
+ icon,
1289
+ iconOnly,
1290
+ iconPlacement,
1291
+ layout,
1292
+ loading,
1293
+ shadow,
1294
+ shape,
1295
+ size,
1296
+ textStyle,
1297
+ tone,
1298
+ variant,
1299
+ });
1300
+ const {
1301
+ accentColor,
1302
+ borderStyle,
1303
+ contentSizeStyle,
1304
+ defaultHitSlop,
1305
+ gradientCfg,
1306
+ hasGradientBackground,
1307
+ hasIcon,
1308
+ iconBaseBoxStyle,
1309
+ iconPlacedBoxStyle,
1310
+ interactionDisabled,
1311
+ LinearGradientComponent,
1312
+ paddingStyle,
1313
+ radiusStyle,
1314
+ resolvedAccessibilityLabel,
1315
+ resolvedText,
1316
+ rootSizeStyle,
1317
+ shadowStyle,
1318
+ visualColors,
1319
+ visualDisabled,
1320
+ webCursorStyle,
1321
+ } = frame;
1322
+
1323
+ const pressSv = useSharedValue(0);
1324
+ const resolvedPressEffect = React.useMemo(
1325
+ () => resolvePressEffect(pressEffect, interactionDisabled),
1326
+ [interactionDisabled, pressEffect]
1327
+ );
1328
+ const pressEffectFlags = React.useMemo(
1329
+ () => resolvePressEffectFlags(resolvedPressEffect),
1330
+ [resolvedPressEffect]
1331
+ );
1332
+ const pressHighlightEnabled = pressEffectFlags.highlight;
1333
+ const pressOpacityEnabled = pressEffectFlags.opacity;
1334
+ const pressScaleEnabled = pressEffectFlags.scale;
1335
+ const pressOverlay = React.useMemo(
1336
+ () =>
1337
+ resolveOverlay({
1338
+ accentColor,
1339
+ colors,
1340
+ hasGradientBackground,
1341
+ variant,
1342
+ }),
1343
+ [accentColor, colors, hasGradientBackground, variant]
1344
+ );
1345
+
1346
+ React.useEffect(() => {
1347
+ if (interactionDisabled || resolvedPressEffect === 'none') {
1348
+ pressSv.value = 0;
1349
+ }
1350
+ }, [interactionDisabled, pressSv, resolvedPressEffect]);
1351
+
1352
+ const rootAnimatedStyle = useAnimatedStyle(() => {
1353
+ const baseOpacity = visualDisabled ? DISABLED_OPACITY : 1;
1354
+ const scale = pressScaleEnabled
1355
+ ? interpolate(pressSv.value, [0, 1], [1, PRESSED_SCALE])
1356
+ : 1;
1357
+ const opacity = pressOpacityEnabled
1358
+ ? interpolate(pressSv.value, [0, 1], [baseOpacity, PRESSED_OPACITY])
1359
+ : baseOpacity;
1360
+
1361
+ return {
1362
+ opacity,
1363
+ transform: [{ scale }],
1364
+ };
1365
+ }, [pressOpacityEnabled, pressScaleEnabled, visualDisabled]);
1366
+
1367
+ const overlayAnimatedStyle = useAnimatedStyle(() => {
1368
+ const opacity = pressHighlightEnabled
1369
+ ? interpolate(pressSv.value, [0, 1], [0, pressOverlay.strength])
1370
+ : 0;
1371
+ return { opacity };
1372
+ }, [pressHighlightEnabled, pressOverlay.strength]);
1373
+
1374
+ const handlePress = React.useCallback(
1375
+ (event: GestureResponderEvent) => {
1376
+ if (interactionDisabled) return;
1377
+ onPress?.(event);
1378
+ },
1379
+ [interactionDisabled, onPress]
1380
+ );
1381
+
1382
+ const handlePressIn = React.useCallback(
1383
+ (event: GestureResponderEvent) => {
1384
+ if (!interactionDisabled && resolvedPressEffect !== 'none') {
1385
+ pressSv.value = withTiming(1, PRESS_TIMING);
1386
+ }
1387
+ onPressIn?.(event);
1388
+ },
1389
+ [interactionDisabled, onPressIn, pressSv, resolvedPressEffect]
1390
+ );
1391
+
1392
+ const handlePressOut = React.useCallback(
1393
+ (event: GestureResponderEvent) => {
1394
+ if (!interactionDisabled && resolvedPressEffect !== 'none') {
1395
+ pressSv.value = withTiming(0, PRESS_TIMING);
1396
+ }
1397
+ onPressOut?.(event);
1398
+ },
1399
+ [interactionDisabled, onPressOut, pressSv, resolvedPressEffect]
1400
+ );
1401
+
1402
+ return (
1403
+ <AnimatedPressable
1404
+ {...pressableProps}
1405
+ ref={ref}
1406
+ accessibilityLabel={resolvedAccessibilityLabel}
1407
+ accessibilityRole="button"
1408
+ accessibilityState={{
1409
+ ...accessibilityState,
1410
+ busy: Boolean(loading || accessibilityState?.busy),
1411
+ disabled: Boolean(interactionDisabled || accessibilityState?.disabled),
1412
+ }}
1413
+ disabled={interactionDisabled}
1414
+ hitSlop={hitSlop ?? defaultHitSlop}
1415
+ onPress={handlePress}
1416
+ onPressIn={handlePressIn}
1417
+ onPressOut={handlePressOut}
1418
+ style={[styles.root, rootSizeStyle, radiusStyle, shadowStyle, rootAnimatedStyle, webCursorStyle, style]}
1419
+ testID={testID}
1420
+ >
1421
+ <View
1422
+ pointerEvents="none"
1423
+ style={[
1424
+ styles.content,
1425
+ contentSizeStyle,
1426
+ paddingStyle,
1427
+ radiusStyle,
1428
+ borderStyle,
1429
+ { backgroundColor: visualColors.backgroundColor },
1430
+ contentStyle,
1431
+ ]}
1432
+ >
1433
+ {LinearGradientComponent && gradientCfg ? (
1434
+ <LinearGradientComponent
1435
+ pointerEvents="none"
1436
+ colors={gradientCfg.colors}
1437
+ start={gradientCfg.start}
1438
+ end={gradientCfg.end}
1439
+ style={[StyleSheet.absoluteFillObject, radiusStyle]}
1440
+ />
1441
+ ) : null}
1442
+
1443
+ {pressHighlightEnabled ? (
1444
+ <Animated.View
1445
+ pointerEvents="none"
1446
+ style={[
1447
+ StyleSheet.absoluteFillObject,
1448
+ radiusStyle,
1449
+ { backgroundColor: pressOverlay.color },
1450
+ overlayAnimatedStyle,
1451
+ ]}
1452
+ />
1453
+ ) : null}
1454
+
1455
+ {iconOnly ? (
1456
+ (icon ?? resolvedText)
1457
+ ) : (
1458
+ <>
1459
+ {icon && iconPlacement === 'start' ? (
1460
+ <View style={[iconBaseBoxStyle, iconPlacedBoxStyle]}>{icon}</View>
1461
+ ) : null}
1462
+ {resolvedText}
1463
+ {icon && iconPlacement === 'end' ? (
1464
+ <View style={[iconBaseBoxStyle, iconPlacedBoxStyle]}>{icon}</View>
1465
+ ) : null}
1466
+ </>
1467
+ )}
1468
+ </View>
1469
+ </AnimatedPressable>
1470
+ );
1471
+ }
1472
+
1246
1473
  function ButtonAnimatedImpl(
1247
1474
  {
1248
1475
  variant = 'solid',
@@ -1340,7 +1567,7 @@ function ButtonAnimatedImpl(
1340
1567
  );
1341
1568
 
1342
1569
  const pressSv = useSharedValue(0);
1343
- const loadingSv = useSharedValue(loading ? 1 : 0);
1570
+ const loadingSv = useSharedValue(0);
1344
1571
  const [spinnerMounted, setSpinnerMounted] = React.useState(loading);
1345
1572
  const hideSpinnerTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
1346
1573
 
@@ -1628,15 +1855,43 @@ function ButtonAnimatedImpl(
1628
1855
  const ButtonStaticWithRef = React.forwardRef<ButtonRef, ButtonProps>(ButtonStaticImpl);
1629
1856
  ButtonStaticWithRef.displayName = 'ButtonStatic';
1630
1857
 
1858
+ const ButtonPressAnimatedWithRef = React.forwardRef<ButtonRef, ButtonProps>(ButtonPressAnimatedImpl);
1859
+ ButtonPressAnimatedWithRef.displayName = 'ButtonPressAnimated';
1860
+
1631
1861
  const ButtonAnimatedWithRef = React.forwardRef<ButtonRef, ButtonProps>(ButtonAnimatedImpl);
1632
- ButtonAnimatedWithRef.displayName = 'ButtonAnimated';
1862
+ ButtonAnimatedWithRef.displayName = 'ButtonLoadingAnimated';
1633
1863
 
1634
1864
  function ButtonImpl(props: ButtonProps, ref: React.ForwardedRef<ButtonRef>) {
1635
- if (shouldUseStaticButtonPath(props)) {
1636
- return <ButtonStaticWithRef {...props} ref={ref} />;
1865
+ const loadingRequested = Boolean(props.loading);
1866
+ const animatedPressRequested = shouldRequestAnimatedPressPath(props);
1867
+ const [retainLoadingAnimated, setRetainLoadingAnimated] = React.useState(loadingRequested);
1868
+
1869
+ React.useEffect(() => {
1870
+ if (loadingRequested) {
1871
+ setRetainLoadingAnimated(true);
1872
+ return undefined;
1873
+ }
1874
+
1875
+ if (!retainLoadingAnimated) return undefined;
1876
+
1877
+ const timer = setTimeout(() => {
1878
+ setRetainLoadingAnimated(false);
1879
+ }, ANIMATED_EXIT_RESIDENCY_MS);
1880
+
1881
+ return () => {
1882
+ clearTimeout(timer);
1883
+ };
1884
+ }, [loadingRequested, retainLoadingAnimated]);
1885
+
1886
+ if (loadingRequested || retainLoadingAnimated) {
1887
+ return <ButtonAnimatedWithRef {...props} ref={ref} />;
1637
1888
  }
1638
1889
 
1639
- return <ButtonAnimatedWithRef {...props} ref={ref} />;
1890
+ return animatedPressRequested ? (
1891
+ <ButtonPressAnimatedWithRef {...props} ref={ref} />
1892
+ ) : (
1893
+ <ButtonStaticWithRef {...props} ref={ref} />
1894
+ );
1640
1895
  }
1641
1896
 
1642
1897
  const ButtonWithRef = React.forwardRef<ButtonRef, ButtonProps>(ButtonImpl);