@hero-design/rn 8.130.3 → 8.131.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/es/index.js CHANGED
@@ -7137,7 +7137,11 @@ var getTabsTheme = function getTabsTheme(theme) {
7137
7137
  headerBottom: theme.colors.secondaryOutline,
7138
7138
  indicator: theme.colors.primary,
7139
7139
  text: theme.colors.onDefaultGlobalSurface,
7140
- headerBackground: theme.colors.defaultGlobalSurface
7140
+ headerBackground: theme.colors.defaultGlobalSurface,
7141
+ highlightedActiveText: theme.colors.primary,
7142
+ highlightedActiveBorder: theme.colors.primary,
7143
+ highlightedActiveBackground: theme.colors.neutralGlobalSurface,
7144
+ highlightedDisabledText: theme.colors.disabledOnDefaultGlobalSurface
7141
7145
  };
7142
7146
  var space = {
7143
7147
  flatListHorizontalPadding: theme.space.small,
@@ -7145,14 +7149,17 @@ var getTabsTheme = function getTabsTheme(theme) {
7145
7149
  itemVerticalPadding: theme.space.small,
7146
7150
  itemMargin: theme.space.smallMedium,
7147
7151
  outlineHorizontalPadding: theme.space.small,
7148
- outlineVerticalPadding: theme.space.xsmall,
7149
- tabIndicatorBottom: -theme.space.xxsmall
7152
+ tabIndicatorBottom: -theme.space.xxsmall,
7153
+ highlightedItemMargin: theme.space.xsmall,
7154
+ highlightedBarTopPadding: theme.space.xxsmall
7150
7155
  };
7151
7156
  var radii = {
7152
- outline: theme.radii.xlarge
7157
+ highlightedOutline: theme.radii.medium
7153
7158
  };
7154
7159
  var borderWidths = {
7155
- headerBottom: theme.borderWidths.medium
7160
+ headerBottom: theme.borderWidths.medium,
7161
+ highlightedHeaderBottom: theme.borderWidths.base,
7162
+ highlightedActiveBorder: theme.borderWidths.base
7156
7163
  };
7157
7164
  var sizes = {
7158
7165
  indicator: theme.sizes.xxsmall
@@ -28111,43 +28118,85 @@ var HeaderTabWrapper = index$c(View)(function (_ref) {
28111
28118
  backgroundColor: theme.__hd__.tabs.colors.headerBackground
28112
28119
  };
28113
28120
  });
28121
+ var getItemMarginLeft = function getItemMarginLeft(isFirstItem, themeVariant, highlightedMargin, defaultMargin) {
28122
+ if (isFirstItem) return 0;
28123
+ if (themeVariant === 'highlighted') return highlightedMargin;
28124
+ return defaultMargin;
28125
+ };
28114
28126
  var HeaderTabItem = index$c(Animated.View)(function (_ref2) {
28115
28127
  var theme = _ref2.theme,
28116
- isFirstItem = _ref2.isFirstItem;
28128
+ isFirstItem = _ref2.isFirstItem,
28129
+ themeVariant = _ref2.themeVariant;
28117
28130
  return {
28118
- marginLeft: isFirstItem ? 0 : theme.__hd__.tabs.space.itemMargin,
28119
- paddingVertical: theme.__hd__.tabs.space.itemVerticalPadding
28131
+ marginLeft: getItemMarginLeft(isFirstItem, themeVariant, theme.__hd__.tabs.space.highlightedItemMargin, theme.__hd__.tabs.space.itemMargin),
28132
+ paddingVertical: theme.__hd__.tabs.space.itemVerticalPadding,
28133
+ alignItems: 'center',
28134
+ justifyContent: 'center'
28120
28135
  };
28121
28136
  });
28122
- var HeaderTabItemOutlineWrapper = index$c(View)(function (_ref3) {
28137
+ // Three-piece pill: left cap + body + right cap, all native-driver animated.
28138
+ // Splitting into pieces keeps border-radius on fixed-width views so scaleX
28139
+ // never distorts the rounded corners. Cap width equals the border-radius so
28140
+ // the rounded edge is fully contained within the cap piece.
28141
+ var HeaderTabPillLeft = index$c(Animated.View)(function (_ref3) {
28123
28142
  var theme = _ref3.theme;
28124
- return _objectSpread2({
28125
- paddingVertical: theme.__hd__.tabs.space.itemVerticalPadding
28126
- }, StyleSheet$1.absoluteFillObject);
28143
+ return {
28144
+ position: 'absolute',
28145
+ top: 0,
28146
+ bottom: 0,
28147
+ left: 0,
28148
+ width: theme.__hd__.tabs.radii.highlightedOutline,
28149
+ borderTopLeftRadius: theme.__hd__.tabs.radii.highlightedOutline,
28150
+ backgroundColor: theme.__hd__.tabs.colors.highlightedActiveBackground
28151
+ };
28127
28152
  });
28128
- var HeaderTabItemOutline = index$c(Animated.View)(function (_ref4) {
28129
- var theme = _ref4.theme,
28130
- themeActive = _ref4.themeActive;
28153
+ var HeaderTabPillBody = index$c(Animated.View)(function (_ref4) {
28154
+ var theme = _ref4.theme;
28131
28155
  return {
28132
- borderRadius: theme.__hd__.tabs.radii.outline,
28133
- backgroundColor: themeActive ? theme.__hd__.tabs.colors.activeBackground : undefined
28156
+ position: 'absolute',
28157
+ top: 0,
28158
+ bottom: 0,
28159
+ left: 0,
28160
+ width: 1,
28161
+ backgroundColor: theme.__hd__.tabs.colors.highlightedActiveBackground
28134
28162
  };
28135
28163
  });
28136
- var HeaderTabItemWrapper = index$c(View)(function (_ref5) {
28164
+ var HeaderTabPillRight = index$c(Animated.View)(function (_ref5) {
28137
28165
  var theme = _ref5.theme;
28166
+ return {
28167
+ position: 'absolute',
28168
+ top: 0,
28169
+ bottom: 0,
28170
+ left: 0,
28171
+ width: theme.__hd__.tabs.radii.highlightedOutline,
28172
+ borderTopRightRadius: theme.__hd__.tabs.radii.highlightedOutline,
28173
+ backgroundColor: theme.__hd__.tabs.colors.highlightedActiveBackground
28174
+ };
28175
+ });
28176
+ var HeaderTabItemActiveBorder = index$c(Animated.View)(function (_ref6) {
28177
+ var theme = _ref6.theme;
28178
+ return {
28179
+ position: 'absolute',
28180
+ bottom: 0,
28181
+ width: 1,
28182
+ height: theme.__hd__.tabs.borderWidths.highlightedActiveBorder,
28183
+ backgroundColor: theme.__hd__.tabs.colors.highlightedActiveBorder
28184
+ };
28185
+ });
28186
+ var HeaderTabItemWrapper = index$c(View)(function (_ref7) {
28187
+ var theme = _ref7.theme;
28138
28188
  return _objectSpread2({
28139
28189
  paddingHorizontal: theme.__hd__.tabs.space.outlineHorizontalPadding,
28140
- paddingVertical: theme.__hd__.tabs.space.outlineVerticalPadding,
28141
28190
  position: 'relative',
28142
28191
  justifyContent: 'center'
28143
28192
  }, !isAndroid8 && {
28144
28193
  alignItems: 'center'
28145
28194
  });
28146
28195
  });
28147
- var HeaderTabItemIndicator = index$c(Animated.View)(function (_ref6) {
28148
- var theme = _ref6.theme;
28196
+ var HeaderTabItemIndicator = index$c(Animated.View)(function (_ref8) {
28197
+ var theme = _ref8.theme;
28149
28198
  return {
28150
- width: '100%',
28199
+ width: 1,
28151
28200
  height: theme.__hd__.tabs.sizes.indicator,
28152
28201
  position: 'absolute',
28153
28202
  bottom: theme.__hd__.tabs.space.tabIndicatorBottom,
@@ -28228,109 +28277,204 @@ var TabWithBadge = function TabWithBadge(_ref) {
28228
28277
  return /*#__PURE__*/React__default.createElement(View, null, tabItem);
28229
28278
  };
28230
28279
 
28231
- var useAnimatedValueArray = function useAnimatedValueArray(initialValues) {
28232
- var refs = React__default.useRef([]);
28233
- refs.current.length = initialValues.length;
28234
- initialValues.forEach(function (initialValue, i) {
28235
- var _refs$current$i;
28236
- refs.current[i] = (_refs$current$i = refs.current[i]) !== null && _refs$current$i !== void 0 ? _refs$current$i : new Animated.Value(initialValue);
28237
- });
28238
- return refs.current;
28239
- };
28240
-
28241
- var useInitHighlightedAnimation = function useInitHighlightedAnimation(_ref) {
28242
- var _ref$selectedIndex = _ref.selectedIndex,
28243
- selectedIndex = _ref$selectedIndex === void 0 ? 0 : _ref$selectedIndex,
28280
+ /**
28281
+ * Drives two visual layers that slide to the selected tab on every press:
28282
+ *
28283
+ * Layer 1 — bottom border / underline (indicatorStyle)
28284
+ * ─────────────────────────────────────────────────────
28285
+ * Uses the "width:1 + scaleX" trick: the element has a fixed stylesheet
28286
+ * width of 1px and scaleX is set to the target pixel width, giving a visual
28287
+ * width of 1 × scaleX pixels without touching any layout property.
28288
+ * Both translateX and scaleX are transform properties → native driver.
28289
+ * Caveat: scaleX also scales border-radius, so this layer has no border-radius.
28290
+ *
28291
+ * Layer 2 pill background (pillLeftStyle / pillBodyStyle / pillRightStyle)
28292
+ * ─────────────────────────────────────────────────────────────────────────────
28293
+ * The pill is split into three absolutely-positioned children so that
28294
+ * border-radius is never distorted by scale:
28295
+ *
28296
+ * ┌──────────────────────────────────────────────────────┐
28297
+ * │ [cap-left 8px] [body width-1 + scaleX] [cap-right 8px] │
28298
+ * └──────────────────────────────────────────────────────┘
28299
+ *
28300
+ * cap-left — fixed 8px wide, borderTopLeftRadius:8, translateX = pillX
28301
+ * body — width:1 + scaleX trick (scaleX = tabWidth - 16),
28302
+ * transformOrigin 'left center',
28303
+ * translateX = pillX + 8 (via Animated.add)
28304
+ * cap-right — fixed 8px wide, borderTopRightRadius:8,
28305
+ * translateX = pillX + tabWidth - 8 (via Animated.add)
28306
+ *
28307
+ * All four animated values use the native driver (translateX and scaleX are
28308
+ * transform properties). `width` is never animated, so no JS driver needed.
28309
+ *
28310
+ * Driver summary:
28311
+ * indicatorX native translateX — slides the bottom border
28312
+ * indicatorScaleX native scaleX — stretches the bottom border
28313
+ * pillX native translateX — slides all three pill pieces
28314
+ * pillBodyScaleX native scaleX — stretches the pill body
28315
+ * pillRightOffsetX native translateX — positions the right cap
28316
+ * (Animated.add: pillX + tabWidth - 8)
28317
+ */
28318
+ var useIndicatorAnimation = function useIndicatorAnimation(_ref) {
28319
+ var selectedIndex = _ref.selectedIndex,
28244
28320
  tabsLength = _ref.tabsLength,
28245
- variant = _ref.variant;
28246
- var tabsAnims = useAnimatedValueArray(Array.from({
28247
- length: tabsLength
28248
- }).map(function (_, i) {
28249
- return i === selectedIndex ? 1 : 0;
28250
- }));
28321
+ pillCapWidth = _ref.pillCapWidth;
28322
+ // Layer 1 — native driver (bottom border / underline).
28323
+ var indicatorX = React__default.useRef(new Animated.Value(0)).current;
28324
+ var indicatorScaleX = React__default.useRef(new Animated.Value(1)).current;
28325
+ // Layer 2 native driver (pill background, three-piece split).
28326
+ // pillX: left edge of the pill (shared by all three pieces as base).
28327
+ // pillBodyScaleX: scaleX for the body piece (tabWidth - 2 * CAP_WIDTH).
28328
+ // pillRightOffset: additional x offset for the right cap (tabWidth - CAP_WIDTH).
28329
+ var pillX = React__default.useRef(new Animated.Value(0)).current;
28330
+ var pillBodyScaleX = React__default.useRef(new Animated.Value(1)).current;
28331
+ var pillRightOffset = React__default.useRef(new Animated.Value(0)).current;
28332
+ // Stable ref so callbacks don't capture stale closures.
28333
+ var layoutsRef = React__default.useRef([]);
28334
+ var runningAnimRef = React__default.useRef(null);
28335
+ var pendingIndexRef = React__default.useRef(undefined);
28336
+ var initializedRef = React__default.useRef(false);
28337
+ // Resize layout cache when tabsLength changes.
28251
28338
  React__default.useEffect(function () {
28252
- if (variant !== 'highlighted') {
28253
- return;
28254
- }
28255
- var animation = Animated.parallel(_toConsumableArray(Array.from({
28339
+ layoutsRef.current = Array.from({
28256
28340
  length: tabsLength
28257
- }).map(function (_, i) {
28258
- return Animated.timing(tabsAnims[i], {
28259
- toValue: i === selectedIndex ? 1 : 0,
28260
- duration: 150,
28261
- useNativeDriver: Platform.OS !== 'web'
28262
- });
28263
- })));
28264
- animation.start();
28265
- return function () {
28266
- animation.stop();
28267
- };
28268
- }, [selectedIndex]);
28269
- return {
28270
- tabsAnims: tabsAnims
28271
- };
28272
- };
28273
-
28274
- var TRANSLATE_DISTANCE = 30;
28275
- var animateOpacity = function animateOpacity(animatedValue, toValue) {
28276
- return Animated.timing(animatedValue, {
28277
- toValue: toValue,
28278
- duration: 150,
28279
- easing: Easing.ease,
28280
- useNativeDriver: Platform.OS !== 'web'
28281
- });
28282
- };
28283
- var animateTranslateX = function animateTranslateX(animatedValue, toValue) {
28284
- return Animated.spring(animatedValue, {
28285
- toValue: toValue,
28286
- useNativeDriver: Platform.OS !== 'web'
28287
- });
28288
- };
28289
- var useInitUnderlinedAnimation = function useInitUnderlinedAnimation(_ref) {
28290
- var tabsLength = _ref.tabsLength,
28291
- _ref$selectedIndex = _ref.selectedIndex,
28292
- selectedIndex = _ref$selectedIndex === void 0 ? 0 : _ref$selectedIndex,
28293
- variant = _ref.variant;
28294
- var previousIndex = React__default.useRef(0);
28295
- var translateXAnims = useAnimatedValueArray(Array.from({
28296
- length: tabsLength
28297
- }).map(function () {
28298
- return 0;
28299
- }));
28300
- var opacityAnims = useAnimatedValueArray(Array.from({
28301
- length: tabsLength
28302
- }).map(function (_, i) {
28303
- return selectedIndex !== undefined && selectedIndex === i ? 1 : 0;
28304
- }));
28305
- var underlinedTranslateX = translateXAnims.map(function (anim) {
28306
- return anim.interpolate({
28307
- inputRange: [-1, 0, 1],
28308
- outputRange: [-TRANSLATE_DISTANCE, 0, TRANSLATE_DISTANCE]
28341
+ }, function (_, i) {
28342
+ return layoutsRef.current[i];
28309
28343
  });
28310
- });
28311
- var underlinedOpacity = opacityAnims.map(function (anim) {
28312
- return anim.interpolate({
28313
- inputRange: [0, 1],
28314
- outputRange: [0, 1]
28344
+ }, [tabsLength]);
28345
+ var animateTo = React__default.useCallback(function (index, animate) {
28346
+ var _runningAnimRef$curre;
28347
+ var layout = layoutsRef.current[index];
28348
+ if (!layout) return;
28349
+ (_runningAnimRef$curre = runningAnimRef.current) === null || _runningAnimRef$curre === void 0 || _runningAnimRef$curre.stop();
28350
+ runningAnimRef.current = null;
28351
+ // Layer 1: bottom-border element has width:1, so scaleX = pixel width.
28352
+ var indicatorScaleXValue = layout.width;
28353
+ // Layer 2 body: width:1 element, scaleX fills space between the two caps.
28354
+ var bodyScaleX = Math.max(layout.width - pillCapWidth * 2, 0);
28355
+ // Layer 2 right cap: offset from pillX to reach the right edge.
28356
+ // Clamped to 0 so the right cap never slides left of the pill's origin
28357
+ // when the tab is narrower than one cap width.
28358
+ var rightOffset = Math.max(layout.width - pillCapWidth, 0);
28359
+ if (!animate || !initializedRef.current) {
28360
+ // First render — snap all values immediately without animation.
28361
+ indicatorX.setValue(layout.x);
28362
+ indicatorScaleX.setValue(indicatorScaleXValue);
28363
+ pillX.setValue(layout.x);
28364
+ pillBodyScaleX.setValue(bodyScaleX);
28365
+ pillRightOffset.setValue(rightOffset);
28366
+ initializedRef.current = true;
28367
+ return;
28368
+ }
28369
+ // All five animations run on the native driver (UI thread):
28370
+ // indicatorX — slides the bottom border
28371
+ // indicatorScaleX — stretches the bottom border
28372
+ // pillX — slides all three pill pieces together
28373
+ // pillBodyScaleX — resizes the body piece to fill between caps
28374
+ // pillRightOffset — keeps the right cap at the pill's right edge
28375
+ var anim = Animated.parallel([Animated.timing(indicatorX, {
28376
+ toValue: layout.x,
28377
+ useNativeDriver: true
28378
+ }), Animated.timing(indicatorScaleX, {
28379
+ toValue: indicatorScaleXValue,
28380
+ useNativeDriver: true
28381
+ }), Animated.timing(pillX, {
28382
+ toValue: layout.x,
28383
+ useNativeDriver: true
28384
+ }), Animated.timing(pillBodyScaleX, {
28385
+ toValue: bodyScaleX,
28386
+ useNativeDriver: true
28387
+ }), Animated.timing(pillRightOffset, {
28388
+ toValue: rightOffset,
28389
+ useNativeDriver: true
28390
+ })]);
28391
+ runningAnimRef.current = anim;
28392
+ anim.start(function (_ref2) {
28393
+ var finished = _ref2.finished;
28394
+ if (finished) runningAnimRef.current = null;
28315
28395
  });
28316
- });
28396
+ }, [indicatorX, indicatorScaleX, pillX, pillBodyScaleX, pillRightOffset, pillCapWidth]);
28397
+ // Animate to selected tab whenever selectedIndex changes.
28317
28398
  React__default.useEffect(function () {
28318
- if (variant === 'underlined' && selectedIndex !== undefined && previousIndex.current !== selectedIndex) {
28319
- // Prepare for translateX into the right position.
28320
- if (selectedIndex > previousIndex.current) {
28321
- translateXAnims[selectedIndex].setValue(-1);
28322
- } else {
28323
- translateXAnims[selectedIndex].setValue(1);
28324
- }
28325
- // Split animations into 2 sets of parallel animations to prevent race condition.
28326
- Animated.parallel([animateOpacity(opacityAnims[selectedIndex], 1), animateTranslateX(translateXAnims[selectedIndex], 0)]).start();
28327
- Animated.parallel([animateOpacity(opacityAnims[previousIndex.current], 0), animateTranslateX(translateXAnims[previousIndex.current], selectedIndex > previousIndex.current ? 1 : -1)]).start();
28328
- previousIndex.current = selectedIndex;
28399
+ if (selectedIndex === undefined) return;
28400
+ if (layoutsRef.current[selectedIndex]) {
28401
+ animateTo(selectedIndex, initializedRef.current);
28402
+ } else {
28403
+ // Layout not yet measured — store as pending and resolve in onTabLayout.
28404
+ pendingIndexRef.current = selectedIndex;
28329
28405
  }
28330
- }, [selectedIndex, variant, opacityAnims, translateXAnims]);
28406
+ }, [selectedIndex, animateTo]);
28407
+ // Stop any in-flight animation on unmount.
28408
+ React__default.useEffect(function () {
28409
+ return function () {
28410
+ var _runningAnimRef$curre2;
28411
+ (_runningAnimRef$curre2 = runningAnimRef.current) === null || _runningAnimRef$curre2 === void 0 || _runningAnimRef$curre2.stop();
28412
+ };
28413
+ }, []);
28414
+ var onTabLayout = React__default.useCallback(function (index, event) {
28415
+ var _event$nativeEvent$la = event.nativeEvent.layout,
28416
+ x = _event$nativeEvent$la.x,
28417
+ width = _event$nativeEvent$la.width;
28418
+ var prev = layoutsRef.current[index];
28419
+ // Skip if layout hasn't meaningfully changed (sub-pixel tolerance).
28420
+ if (prev && Math.abs(prev.x - x) < 0.5 && Math.abs(prev.width - width) < 0.5) {
28421
+ return;
28422
+ }
28423
+ layoutsRef.current[index] = {
28424
+ x: x,
28425
+ width: width
28426
+ };
28427
+ // Animate if this tab is the selected one (covers the pending case where
28428
+ // selectedIndex was set before the layout was measured).
28429
+ if (index === selectedIndex || index === pendingIndexRef.current) {
28430
+ if (index === pendingIndexRef.current) pendingIndexRef.current = undefined;
28431
+ animateTo(index, initializedRef.current);
28432
+ }
28433
+ // If no tab is selected yet, snap indicators to tab 0 on its first
28434
+ // layout so they appear at a sensible default position.
28435
+ if (!initializedRef.current && index === 0 && selectedIndex === undefined) {
28436
+ indicatorScaleX.setValue(width);
28437
+ pillX.setValue(x);
28438
+ pillBodyScaleX.setValue(Math.max(width - pillCapWidth * 2, 0));
28439
+ pillRightOffset.setValue(Math.max(width - pillCapWidth, 0));
28440
+ initializedRef.current = true;
28441
+ }
28442
+ }, [animateTo, selectedIndex, pillCapWidth]);
28443
+ // Layer 1: transformOrigin 'left center' pins scaleX expansion to left edge.
28444
+ var indicatorStyle = {
28445
+ transformOrigin: 'left center',
28446
+ transform: [{
28447
+ translateX: indicatorX
28448
+ }, {
28449
+ scaleX: indicatorScaleX
28450
+ }]
28451
+ };
28452
+ // Layer 2: three pieces, all absolutely positioned, all native driver.
28453
+ // Animated.add computes derived positions without creating JS-driver nodes.
28454
+ var pillLeftStyle = {
28455
+ transform: [{
28456
+ translateX: pillX
28457
+ }]
28458
+ };
28459
+ var pillBodyStyle = {
28460
+ transformOrigin: 'left center',
28461
+ transform: [{
28462
+ translateX: Animated.add(pillX, pillCapWidth)
28463
+ }, {
28464
+ scaleX: pillBodyScaleX
28465
+ }]
28466
+ };
28467
+ var pillRightStyle = {
28468
+ transform: [{
28469
+ translateX: Animated.add(pillX, pillRightOffset)
28470
+ }]
28471
+ };
28331
28472
  return {
28332
- underlinedTranslateX: underlinedTranslateX,
28333
- underlinedOpacity: underlinedOpacity
28473
+ indicatorStyle: indicatorStyle,
28474
+ pillLeftStyle: pillLeftStyle,
28475
+ pillBodyStyle: pillBodyStyle,
28476
+ pillRightStyle: pillRightStyle,
28477
+ onTabLayout: onTabLayout
28334
28478
  };
28335
28479
  };
28336
28480
 
@@ -28350,7 +28494,7 @@ var getTabItem$1 = function getTabItem(_ref) {
28350
28494
  }
28351
28495
  if (typeof item === 'string') {
28352
28496
  return /*#__PURE__*/React__default.createElement(Typography.Body, {
28353
- variant: active ? 'regular-bold' : 'regular',
28497
+ variant: active ? 'small-bold' : 'small',
28354
28498
  numberOfLines: 1,
28355
28499
  style: {
28356
28500
  color: color
@@ -28361,130 +28505,167 @@ var getTabItem$1 = function getTabItem(_ref) {
28361
28505
  color: color
28362
28506
  });
28363
28507
  };
28364
- var ScrollableTabHeader = function ScrollableTabHeader(_ref2) {
28365
- var onTabPress = _ref2.onTabPress,
28366
- selectedIndex = _ref2.selectedIndex,
28367
- tabs = _ref2.tabs,
28368
- barStyle = _ref2.barStyle,
28369
- testID = _ref2.testID,
28370
- _ref2$insets = _ref2.insets,
28371
- insets = _ref2$insets === void 0 ? {
28508
+ var TabItemComponent = /*#__PURE__*/React__default.memo(function (_ref2) {
28509
+ var _tab$inactiveItem;
28510
+ var tab = _ref2.tab,
28511
+ index = _ref2.index,
28512
+ active = _ref2.active,
28513
+ variant = _ref2.variant,
28514
+ onTabPress = _ref2.onTabPress,
28515
+ onLayout = _ref2.onLayout;
28516
+ var theme = useTheme$1();
28517
+ var isHighlighted = variant === 'highlighted';
28518
+ var getTextColor = function getTextColor() {
28519
+ if (isHighlighted) {
28520
+ if (tab.disabled) return theme.__hd__.tabs.colors.highlightedDisabledText;
28521
+ if (active) return theme.__hd__.tabs.colors.highlightedActiveText;
28522
+ }
28523
+ return active ? theme.__hd__.tabs.colors.active : theme.__hd__.tabs.colors.inactive;
28524
+ };
28525
+ var inactiveItem = (_tab$inactiveItem = tab.inactiveItem) !== null && _tab$inactiveItem !== void 0 ? _tab$inactiveItem : tab.activeItem;
28526
+ var tabItem = getTabItem$1({
28527
+ item: active ? tab.activeItem : inactiveItem,
28528
+ color: getTextColor(),
28529
+ active: active
28530
+ });
28531
+ var handlePress = React__default.useCallback(function () {
28532
+ return onTabPress(tab.key);
28533
+ }, [onTabPress, tab.key]);
28534
+ return /*#__PURE__*/React__default.createElement(TouchableWithoutFeedback, {
28535
+ key: tab.key,
28536
+ onPress: handlePress,
28537
+ testID: tab.testID,
28538
+ disabled: tab.disabled
28539
+ }, /*#__PURE__*/React__default.createElement(HeaderTabItem, {
28540
+ testID: "tab-item-".concat(index),
28541
+ isFirstItem: index === 0,
28542
+ themeVariant: variant,
28543
+ onLayout: onLayout
28544
+ }, /*#__PURE__*/React__default.createElement(HeaderTabItemWrapper, null, /*#__PURE__*/React__default.createElement(TabWithBadge, {
28545
+ config: tab.badge,
28546
+ tabItem: tabItem
28547
+ }))));
28548
+ });
28549
+ var ScrollableTabHeader = function ScrollableTabHeader(_ref3) {
28550
+ var onTabPress = _ref3.onTabPress,
28551
+ rawSelectedIndex = _ref3.selectedIndex,
28552
+ tabs = _ref3.tabs,
28553
+ barStyle = _ref3.barStyle,
28554
+ testID = _ref3.testID,
28555
+ _ref3$insets = _ref3.insets,
28556
+ insets = _ref3$insets === void 0 ? {
28372
28557
  top: 0,
28373
28558
  bottom: 0,
28374
28559
  right: 0,
28375
28560
  left: 0
28376
- } : _ref2$insets,
28377
- _ref2$variant = _ref2.variant,
28378
- variant = _ref2$variant === void 0 ? 'highlighted' : _ref2$variant;
28561
+ } : _ref3$insets,
28562
+ _ref3$variant = _ref3.variant,
28563
+ variant = _ref3$variant === void 0 ? 'highlighted' : _ref3$variant;
28564
+ var selectedIndex = rawSelectedIndex !== undefined && rawSelectedIndex >= 0 ? rawSelectedIndex : undefined;
28379
28565
  var theme = useTheme$1();
28380
- var flatListRef = React__default.useRef(null);
28381
- // Init underlined animation data
28382
- var _useInitUnderlinedAni = useInitUnderlinedAnimation({
28383
- tabsLength: tabs.length,
28384
- selectedIndex: selectedIndex,
28385
- variant: variant
28386
- }),
28387
- underlinedTranslateX = _useInitUnderlinedAni.underlinedTranslateX,
28388
- underlinedOpacity = _useInitUnderlinedAni.underlinedOpacity;
28389
- // Init highlighted animation data
28390
- var _useInitHighlightedAn = useInitHighlightedAnimation({
28566
+ var scrollViewRef = React__default.useRef(null);
28567
+ var isHighlighted = variant === 'highlighted';
28568
+ var _useIndicatorAnimatio = useIndicatorAnimation({
28391
28569
  selectedIndex: selectedIndex,
28392
28570
  tabsLength: tabs.length,
28393
- variant: variant
28571
+ pillCapWidth: theme.__hd__.tabs.radii.highlightedOutline
28394
28572
  }),
28395
- tabsAnims = _useInitHighlightedAn.tabsAnims;
28396
- React__default.useEffect(function () {
28397
- if (selectedIndex !== undefined && selectedIndex !== -1) {
28398
- var _flatListRef$current;
28399
- (_flatListRef$current = flatListRef.current) === null || _flatListRef$current === void 0 || _flatListRef$current.scrollToIndex({
28400
- index: selectedIndex,
28401
- viewPosition: 0.5
28573
+ indicatorStyle = _useIndicatorAnimatio.indicatorStyle,
28574
+ pillLeftStyle = _useIndicatorAnimatio.pillLeftStyle,
28575
+ pillBodyStyle = _useIndicatorAnimatio.pillBodyStyle,
28576
+ pillRightStyle = _useIndicatorAnimatio.pillRightStyle,
28577
+ onTabLayout = _useIndicatorAnimatio.onTabLayout;
28578
+ // Scroll to the selected tab after its layout is known.
28579
+ var handleTabLayout = React__default.useCallback(function (index, event) {
28580
+ if (index === selectedIndex) {
28581
+ var _scrollViewRef$curren;
28582
+ (_scrollViewRef$curren = scrollViewRef.current) === null || _scrollViewRef$curren === void 0 || _scrollViewRef$curren.scrollTo({
28583
+ x: event.nativeEvent.layout.x,
28584
+ animated: true
28402
28585
  });
28403
28586
  }
28404
- return function () {
28587
+ onTabLayout(index, event);
28588
+ }, [selectedIndex, onTabLayout]);
28589
+ // Memoize per-tab layout handlers so TabItemComponent memo is not broken.
28590
+ var tabLayoutHandlers = React__default.useMemo(function () {
28591
+ return tabs.map(function (_, i) {
28592
+ return function (event) {
28593
+ return handleTabLayout(i, event);
28594
+ };
28595
+ });
28596
+ },
28597
+ // Handlers only need to change when tab count or selection changes.
28598
+ [tabs, handleTabLayout]);
28599
+ var scrollViewStyle = React__default.useMemo(function () {
28600
+ return {
28601
+ borderBottomColor: theme.__hd__.tabs.colors.headerBottom,
28602
+ borderBottomWidth: isHighlighted ? theme.__hd__.tabs.borderWidths.highlightedHeaderBottom : theme.__hd__.tabs.sizes.indicator
28405
28603
  };
28406
- }, [selectedIndex]);
28604
+ }, [theme, isHighlighted]);
28605
+ var contentContainerStyle = React__default.useMemo(function () {
28606
+ return _objectSpread2({
28607
+ paddingHorizontal: theme.__hd__.tabs.space.flatListHorizontalPadding,
28608
+ position: 'relative'
28609
+ }, Platform.OS === 'android' && {
28610
+ borderBottomColor: theme.__hd__.tabs.colors.headerBottom,
28611
+ borderBottomWidth: isHighlighted ? theme.__hd__.tabs.borderWidths.highlightedHeaderBottom : theme.__hd__.tabs.sizes.indicator
28612
+ });
28613
+ }, [theme, isHighlighted]);
28614
+ var wrapperStyle = React__default.useMemo(function () {
28615
+ return [isHighlighted && {
28616
+ paddingTop: theme.__hd__.tabs.space.highlightedBarTopPadding
28617
+ }, barStyle];
28618
+ }, [isHighlighted, theme, barStyle]);
28407
28619
  return /*#__PURE__*/React__default.createElement(HeaderTabWrapper, {
28620
+ testID: "tab-header-wrapper",
28408
28621
  themeInsets: insets,
28409
- style: barStyle
28410
- }, /*#__PURE__*/React__default.createElement(FlatList, {
28622
+ style: wrapperStyle
28623
+ }, /*#__PURE__*/React__default.createElement(View, {
28624
+ style: isHighlighted ? {
28625
+ overflow: 'hidden'
28626
+ } : undefined
28627
+ }, /*#__PURE__*/React__default.createElement(ScrollView, {
28628
+ ref: scrollViewRef,
28411
28629
  testID: testID,
28412
- ref: flatListRef,
28413
28630
  horizontal: true,
28414
- data: tabs,
28415
- keyExtractor: function keyExtractor(tab) {
28416
- return String(tab.key);
28417
- },
28418
28631
  showsHorizontalScrollIndicator: false,
28419
- onScrollToIndexFailed: function onScrollToIndexFailed(_ref3) {
28420
- var index = _ref3.index;
28421
- setTimeout(function () {
28422
- var _flatListRef$current2;
28423
- return (_flatListRef$current2 = flatListRef.current) === null || _flatListRef$current2 === void 0 ? void 0 : _flatListRef$current2.scrollToIndex({
28424
- index: index,
28425
- viewPosition: 0.5
28426
- });
28427
- }, 100);
28428
- },
28632
+ contentContainerStyle: contentContainerStyle,
28633
+ style: scrollViewStyle
28634
+ }, /*#__PURE__*/React__default.createElement(View, {
28429
28635
  style: {
28430
- borderBottomColor: theme.__hd__.tabs.colors.headerBottom,
28431
- borderBottomWidth: theme.__hd__.tabs.sizes.indicator
28432
- },
28433
- contentContainerStyle: _objectSpread2({
28434
- paddingHorizontal: theme.__hd__.tabs.space.flatListHorizontalPadding
28435
- }, Platform.OS === 'android' && {
28436
- borderBottomColor: theme.__hd__.tabs.colors.headerBottom,
28437
- borderBottomWidth: theme.__hd__.tabs.sizes.indicator
28438
- }),
28439
- renderItem: function renderItem(_ref4) {
28440
- var tab = _ref4.item,
28441
- index = _ref4.index;
28442
- var key = tab.key,
28443
- tabItemTestID = tab.testID,
28444
- activeItem = tab.activeItem,
28445
- originalInactiveItem = tab.inactiveItem,
28446
- badge = tab.badge;
28447
- var active = selectedIndex === index;
28448
- var activeAnimated = tabsAnims[index];
28449
- var outlineScale = activeAnimated.interpolate({
28450
- inputRange: [0, 1],
28451
- outputRange: [0.5, 1]
28452
- });
28453
- var inactiveItem = originalInactiveItem !== null && originalInactiveItem !== void 0 ? originalInactiveItem : activeItem;
28454
- var tabItem = getTabItem$1({
28455
- item: active ? activeItem : inactiveItem,
28456
- color: active ? theme.__hd__.tabs.colors.active : theme.__hd__.tabs.colors.inactive,
28457
- active: active
28458
- });
28459
- return /*#__PURE__*/React__default.createElement(TouchableWithoutFeedback, {
28460
- key: key,
28461
- onPress: function onPress() {
28462
- onTabPress(key);
28463
- },
28464
- testID: tabItemTestID
28465
- }, /*#__PURE__*/React__default.createElement(HeaderTabItem, {
28466
- isFirstItem: index === 0
28467
- }, variant === 'highlighted' && /*#__PURE__*/React__default.createElement(HeaderTabItemOutlineWrapper, null, /*#__PURE__*/React__default.createElement(HeaderTabItemOutline, {
28468
- themeActive: active,
28469
- style: {
28470
- flex: 1,
28471
- transform: [{
28472
- scaleX: outlineScale
28473
- }]
28474
- }
28475
- })), /*#__PURE__*/React__default.createElement(HeaderTabItemWrapper, null, /*#__PURE__*/React__default.createElement(TabWithBadge, {
28476
- config: badge,
28477
- tabItem: tabItem
28478
- })), variant === 'underlined' && /*#__PURE__*/React__default.createElement(HeaderTabItemIndicator, {
28479
- style: {
28480
- opacity: underlinedOpacity[index],
28481
- transform: [{
28482
- translateX: underlinedTranslateX[index]
28483
- }]
28484
- }
28485
- })));
28636
+ flexDirection: 'row',
28637
+ position: 'relative'
28486
28638
  }
28487
- }));
28639
+ }, isHighlighted && /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(HeaderTabPillLeft, {
28640
+ testID: "tab-pill-background",
28641
+ style: pillLeftStyle
28642
+ }), /*#__PURE__*/React__default.createElement(HeaderTabPillBody, {
28643
+ style: pillBodyStyle
28644
+ }), /*#__PURE__*/React__default.createElement(HeaderTabPillRight, {
28645
+ testID: "tab-pill-background-right",
28646
+ style: pillRightStyle
28647
+ })), tabs.map(function (tab, index) {
28648
+ return /*#__PURE__*/React__default.createElement(TabItemComponent, {
28649
+ key: tab.key,
28650
+ tab: tab,
28651
+ index: index,
28652
+ active: selectedIndex === index,
28653
+ variant: variant,
28654
+ onTabPress: onTabPress,
28655
+ onLayout: tabLayoutHandlers[index]
28656
+ });
28657
+ }), isHighlighted ? /*#__PURE__*/React__default.createElement(HeaderTabItemActiveBorder, {
28658
+ testID: "tab-active-border",
28659
+ style: _objectSpread2({
28660
+ position: 'absolute',
28661
+ bottom: 0
28662
+ }, indicatorStyle)
28663
+ }) : /*#__PURE__*/React__default.createElement(HeaderTabItemIndicator, {
28664
+ testID: "tab-underline-indicator",
28665
+ style: _objectSpread2({
28666
+ position: 'absolute'
28667
+ }, indicatorStyle)
28668
+ })))));
28488
28669
  };
28489
28670
 
28490
28671
  var useHandlePageScroll = function useHandlePageScroll() {