@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/CHANGELOG.md +12 -0
- package/es/index.js +407 -225
- package/lib/index.js +407 -225
- package/package.json +1 -1
- package/src/components/Tabs/ScrollableTabsHeader/ScrollableTabsHeader.tsx +217 -131
- package/src/components/Tabs/ScrollableTabsHeader/hooks/useIndicatorAnimation.ts +242 -0
- package/src/components/Tabs/StyledScrollableTabs.tsx +68 -21
- package/src/components/Tabs/index.tsx +2 -0
- package/src/theme/components/tabs.ts +9 -2
- package/src/theme/global/colors/ehJobs.ts +1 -0
- package/src/theme/global/colors/ehWork.ts +1 -1
- package/types/components/Checkbox/StyledInlineCheckBox.d.ts +1 -1
- package/types/components/Tabs/ScrollableTabsHeader/ScrollableTabsHeader.d.ts +1 -1
- package/types/components/Tabs/ScrollableTabsHeader/hooks/useIndicatorAnimation.d.ts +75 -0
- package/types/components/Tabs/StyledScrollableTabs.d.ts +13 -8
- package/types/components/Tabs/index.d.ts +3 -1
- package/types/theme/components/tabs.d.ts +9 -2
- package/src/components/Tabs/ScrollableTabsHeader/hooks/useInitHighlightedAnimation.ts +0 -45
- package/types/components/Tabs/ScrollableTabsHeader/hooks/useInitHighlightedAnimation.d.ts +0 -9
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: '#
|
|
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
|
-
|
|
7177
|
-
|
|
7181
|
+
tabIndicatorBottom: -theme.space.xxsmall,
|
|
7182
|
+
highlightedItemMargin: theme.space.xsmall,
|
|
7183
|
+
highlightedBarTopPadding: theme.space.xxsmall
|
|
7178
7184
|
};
|
|
7179
7185
|
var radii = {
|
|
7180
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
28153
|
-
|
|
28154
|
-
|
|
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
|
|
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
|
-
|
|
28161
|
-
|
|
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
|
|
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 (
|
|
28176
|
-
var theme =
|
|
28225
|
+
var HeaderTabItemIndicator = index$c(reactNative.Animated.View)(function (_ref8) {
|
|
28226
|
+
var theme = _ref8.theme;
|
|
28177
28227
|
return {
|
|
28178
|
-
width:
|
|
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
|
-
|
|
28260
|
-
|
|
28261
|
-
|
|
28262
|
-
|
|
28263
|
-
|
|
28264
|
-
|
|
28265
|
-
|
|
28266
|
-
|
|
28267
|
-
|
|
28268
|
-
|
|
28269
|
-
|
|
28270
|
-
|
|
28271
|
-
|
|
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
|
-
|
|
28274
|
-
|
|
28275
|
-
|
|
28276
|
-
|
|
28277
|
-
|
|
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
|
-
|
|
28281
|
-
return;
|
|
28282
|
-
}
|
|
28283
|
-
var animation = reactNative.Animated.parallel(_toConsumableArray(Array.from({
|
|
28368
|
+
layoutsRef.current = Array.from({
|
|
28284
28369
|
length: tabsLength
|
|
28285
|
-
}
|
|
28286
|
-
return
|
|
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
|
|
28340
|
-
|
|
28341
|
-
|
|
28342
|
-
|
|
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 (
|
|
28347
|
-
|
|
28348
|
-
|
|
28349
|
-
|
|
28350
|
-
|
|
28351
|
-
|
|
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,
|
|
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
|
-
|
|
28361
|
-
|
|
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 ? '
|
|
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
|
|
28393
|
-
var
|
|
28394
|
-
|
|
28395
|
-
|
|
28396
|
-
|
|
28397
|
-
|
|
28398
|
-
|
|
28399
|
-
|
|
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
|
-
} :
|
|
28405
|
-
|
|
28406
|
-
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
|
|
28409
|
-
|
|
28410
|
-
var
|
|
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
|
-
|
|
28600
|
+
pillCapWidth: theme.__hd__.tabs.radii.highlightedOutline
|
|
28422
28601
|
}),
|
|
28423
|
-
|
|
28424
|
-
|
|
28425
|
-
|
|
28426
|
-
|
|
28427
|
-
|
|
28428
|
-
|
|
28429
|
-
|
|
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
|
-
|
|
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
|
-
}, [
|
|
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:
|
|
28438
|
-
}, /*#__PURE__*/React__namespace.default.createElement(reactNative.
|
|
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
|
-
|
|
28448
|
-
|
|
28449
|
-
|
|
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
|
-
|
|
28459
|
-
|
|
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() {
|