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