@vellira-ui/react-native 2.39.5 → 2.40.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/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export type { RadioGroupProps } from './components/RadioGroup';
6
6
  export { RadioGroup } from './components/RadioGroup';
7
7
  export type { SelectOption, SelectProps } from './components/Select';
8
8
  export { Select } from './components/Select';
9
- export type { TabProps, TabsListProps, TabsPanelProps, TabsProps, } from './components/Tabs';
9
+ export type { TabsContentProps, TabsListProps, TabsProps, TabsTriggerProps, } from './components/Tabs';
10
10
  export { Tabs } from './components/Tabs';
11
11
  export type { TooltipProps } from './components/Tooltip';
12
12
  export { Tooltip } from './components/Tooltip';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Children, cloneElement, createContext, forwardRef, isValidElement, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
2
2
  import { Check, ChevronDown, Close, Search } from "@vellira-ui/icons";
3
- import { AccessibilityInfo, ActivityIndicator, Animated, Dimensions, FlatList, Modal as Modal$1, Pressable, StyleSheet, Text, TextInput, View, findNodeHandle, useWindowDimensions } from "react-native";
3
+ import { AccessibilityInfo, ActivityIndicator, Animated, Dimensions, FlatList, Modal as Modal$1, Pressable, ScrollView, StyleSheet, Text, TextInput, View, findNodeHandle, useWindowDimensions } from "react-native";
4
4
  import { darkTheme, highContrastTheme, lightTheme } from "@vellira-ui/tokens";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/hooks/useControllableState.ts
@@ -378,15 +378,15 @@ const useSelect = ({ value, defaultValue, onValueChange, onChange, options, mult
378
378
  };
379
379
  //#endregion
380
380
  //#region src/hooks/useTabs.ts
381
- const useTabs$1 = ({ activeIndex: controlledActiveIndex, defaultActiveIndex = 0, onChange }) => {
382
- const [activeIndex, setActiveIndex] = useControllableState({
383
- value: controlledActiveIndex,
384
- defaultValue: defaultActiveIndex,
385
- onChange
381
+ const useTabs$1 = ({ value: controlledValue, defaultValue = "", onValueChange }) => {
382
+ const [value, setValue] = useControllableState({
383
+ value: controlledValue,
384
+ defaultValue,
385
+ onChange: onValueChange
386
386
  });
387
387
  return {
388
- activeIndex,
389
- setActiveIndex
388
+ value,
389
+ setValue
390
390
  };
391
391
  };
392
392
  //#endregion
@@ -3650,51 +3650,8 @@ const useTabs = () => {
3650
3650
  };
3651
3651
  TabsContext.displayName = "TabsContext";
3652
3652
  //#endregion
3653
- //#region src/components/Tabs/List/TabsList.styles.ts
3653
+ //#region src/components/Tabs/Content/TabsContent.styles.ts
3654
3654
  const createStyles$7 = (theme) => StyleSheet.create({
3655
- list: {
3656
- flexDirection: "row",
3657
- alignSelf: "stretch",
3658
- width: "100%",
3659
- gap: theme.tokens.spacing[5],
3660
- marginBottom: theme.tokens.spacing[4]
3661
- },
3662
- listPills: {
3663
- backgroundColor: theme.components.tabs.pills.default.bg,
3664
- borderRadius: theme.tokens.radius.lg
3665
- },
3666
- listVertical: {
3667
- flexDirection: "column",
3668
- alignSelf: "flex-start",
3669
- width: 140,
3670
- minWidth: 140,
3671
- maxWidth: 140,
3672
- flexGrow: 0,
3673
- flexShrink: 0,
3674
- gap: theme.tokens.spacing[1],
3675
- marginBottom: 0
3676
- }
3677
- });
3678
- //#endregion
3679
- //#region src/components/Tabs/List/TabsList.tsx
3680
- const TabsList = ({ children, style }) => {
3681
- const styles = useThemeStyles(createStyles$7);
3682
- const { orientation, appearance } = useTabs();
3683
- return /* @__PURE__ */ jsx(View, {
3684
- accessibilityRole: "tablist",
3685
- style: [
3686
- styles.list,
3687
- appearance === "pills" && styles.listPills,
3688
- orientation === "vertical" && styles.listVertical,
3689
- style
3690
- ],
3691
- children
3692
- });
3693
- };
3694
- TabsList.displayName = "TabsList";
3695
- //#endregion
3696
- //#region src/components/Tabs/Panel/TabsPanel.styles.ts
3697
- const createStyles$6 = (theme) => StyleSheet.create({
3698
3655
  panel: {
3699
3656
  width: "100%",
3700
3657
  minWidth: 0,
@@ -3718,39 +3675,135 @@ const createStyles$6 = (theme) => StyleSheet.create({
3718
3675
  }
3719
3676
  });
3720
3677
  //#endregion
3721
- //#region src/components/Tabs/Panel/TabsPanel.tsx
3722
- const TabsPanel = ({ index, children, style }) => {
3723
- const styles = useThemeStyles(createStyles$6);
3724
- const { activeIndex, orientation } = useTabs();
3725
- if (activeIndex !== index) return null;
3678
+ //#region src/components/Tabs/Content/TabsContent.tsx
3679
+ const TabsContent = ({ value, children, forceMount = false, style }) => {
3680
+ const styles = useThemeStyles(createStyles$7);
3681
+ const { value: selectedValue, orientation, keepMounted, lazyMount } = useTabs();
3682
+ const isActive = selectedValue === value;
3683
+ const [hasMounted, setHasMounted] = useState(isActive);
3684
+ useEffect(() => {
3685
+ if (isActive) setHasMounted(true);
3686
+ }, [isActive]);
3687
+ if (!(forceMount || isActive || keepMounted && !lazyMount || keepMounted && lazyMount && hasMounted)) return null;
3726
3688
  return /* @__PURE__ */ jsx(View, {
3727
3689
  style: [
3728
3690
  styles.panel,
3729
3691
  orientation === "vertical" && styles.panelVertical,
3730
3692
  style
3731
3693
  ],
3694
+ accessibilityElementsHidden: !isActive,
3695
+ importantForAccessibility: isActive ? "auto" : "no-hide-descendants",
3696
+ children
3697
+ });
3698
+ };
3699
+ TabsContent.displayName = "Tabs.Content";
3700
+ //#endregion
3701
+ //#region src/components/Tabs/List/TabsIndicator.tsx
3702
+ const TabsIndicator = ({ children, style }) => /* @__PURE__ */ jsx(View, {
3703
+ accessibilityElementsHidden: true,
3704
+ importantForAccessibility: "no-hide-descendants",
3705
+ pointerEvents: "none",
3706
+ style,
3707
+ children
3708
+ });
3709
+ TabsIndicator.displayName = "Tabs.Indicator";
3710
+ //#endregion
3711
+ //#region src/components/Tabs/List/TabsList.styles.ts
3712
+ const createStyles$6 = (theme) => StyleSheet.create({
3713
+ list: {
3714
+ flexDirection: "row",
3715
+ alignSelf: "stretch",
3716
+ width: "100%",
3717
+ gap: theme.tokens.spacing[5],
3718
+ marginBottom: theme.tokens.spacing[6]
3719
+ },
3720
+ listSegmented: {
3721
+ padding: 2,
3722
+ backgroundColor: theme.components.tabs.list.segmentedBg,
3723
+ borderColor: theme.components.tabs.list.border,
3724
+ borderRadius: theme.tokens.radius.xl,
3725
+ borderWidth: 1
3726
+ },
3727
+ listVertical: {
3728
+ flexDirection: "column",
3729
+ alignSelf: "flex-start",
3730
+ width: 156,
3731
+ minWidth: 156,
3732
+ maxWidth: 156,
3733
+ flexGrow: 0,
3734
+ flexShrink: 0,
3735
+ gap: theme.tokens.spacing[1],
3736
+ marginBottom: 0
3737
+ }
3738
+ });
3739
+ //#endregion
3740
+ //#region src/components/Tabs/List/TabsList.tsx
3741
+ const TabsList = ({ children, scrollable: scrollableProp, style }) => {
3742
+ const styles = useThemeStyles(createStyles$6);
3743
+ const { orientation, variant } = useTabs();
3744
+ const scrollable = scrollableProp ?? false;
3745
+ const listStyle = [
3746
+ styles.list,
3747
+ variant === "segmented" && styles.listSegmented,
3748
+ orientation === "vertical" && styles.listVertical,
3749
+ style
3750
+ ];
3751
+ if (scrollable && orientation === "horizontal") return /* @__PURE__ */ jsx(ScrollView, {
3752
+ horizontal: true,
3753
+ accessibilityRole: "tablist",
3754
+ showsHorizontalScrollIndicator: false,
3755
+ contentContainerStyle: listStyle,
3756
+ children
3757
+ });
3758
+ return /* @__PURE__ */ jsx(View, {
3759
+ accessibilityRole: "tablist",
3760
+ style: listStyle,
3732
3761
  children
3733
3762
  });
3734
3763
  };
3735
- TabsPanel.displayName = "TabsPanel";
3764
+ TabsList.displayName = "TabsList";
3736
3765
  //#endregion
3737
- //#region src/components/Tabs/Tab/Tab.styles.ts
3766
+ //#region src/components/Tabs/Trigger/TabsTrigger.styles.ts
3738
3767
  const fontWeight$1 = (value) => value;
3739
3768
  const createStyles$5 = (theme) => StyleSheet.create({
3740
3769
  tab: {
3741
- minHeight: 40,
3770
+ minHeight: 44,
3771
+ minWidth: 44,
3742
3772
  alignItems: "center",
3743
3773
  flexDirection: "row",
3744
3774
  gap: theme.tokens.spacing[2],
3745
3775
  justifyContent: "center",
3746
3776
  paddingHorizontal: theme.tokens.spacing[4],
3747
3777
  paddingVertical: theme.tokens.spacing[2],
3748
- backgroundColor: theme.components.tabs.trigger.default.bg
3778
+ borderWidth: 1
3779
+ },
3780
+ tabSm: {
3781
+ minHeight: 36,
3782
+ paddingHorizontal: theme.tokens.spacing[3],
3783
+ paddingVertical: 6
3784
+ },
3785
+ tabLg: {
3786
+ minHeight: 52,
3787
+ paddingHorizontal: theme.tokens.spacing[5],
3788
+ paddingVertical: theme.tokens.spacing[3]
3789
+ },
3790
+ tabSegmented: {
3791
+ minHeight: 32,
3792
+ paddingVertical: 5,
3793
+ borderRadius: theme.tokens.radius.lg
3794
+ },
3795
+ tabSegmentedSm: {
3796
+ minHeight: 30,
3797
+ paddingVertical: 4
3798
+ },
3799
+ tabSegmentedLg: {
3800
+ minHeight: 40,
3801
+ paddingVertical: 8
3749
3802
  },
3750
3803
  tabVertical: {
3751
3804
  position: "relative",
3752
3805
  width: "100%",
3753
- minHeight: 40,
3806
+ minHeight: 44,
3754
3807
  flexGrow: 0,
3755
3808
  flexShrink: 0,
3756
3809
  justifyContent: "flex-start"
@@ -3763,7 +3816,7 @@ const createStyles$5 = (theme) => StyleSheet.create({
3763
3816
  height: 3,
3764
3817
  backgroundColor: "transparent"
3765
3818
  },
3766
- horizontalIndicatorActive: { backgroundColor: theme.components.tabs.indicator.active.bg },
3819
+ horizontalIndicatorActive: { backgroundColor: "transparent" },
3767
3820
  verticalIndicator: {
3768
3821
  position: "absolute",
3769
3822
  top: 0,
@@ -3772,27 +3825,35 @@ const createStyles$5 = (theme) => StyleSheet.create({
3772
3825
  width: 3,
3773
3826
  backgroundColor: "transparent"
3774
3827
  },
3775
- verticalIndicatorActive: { backgroundColor: theme.components.tabs.indicator.active.bg },
3776
- verticalIndicatorPressed: { backgroundColor: theme.components.tabs.indicator.hover.bg },
3777
- tabHovered: { backgroundColor: theme.components.tabs.trigger.hover.bg },
3778
- tabPressed: { backgroundColor: theme.components.tabs.trigger.active.bg },
3828
+ verticalIndicatorActive: { backgroundColor: "transparent" },
3829
+ verticalIndicatorPressed: { backgroundColor: "transparent" },
3830
+ tabHovered: { backgroundColor: "transparent" },
3831
+ tabPressed: { backgroundColor: "transparent" },
3779
3832
  tabFocused: { borderColor: theme.semantic.focus.ring.color },
3780
- tabDefaultActive: { backgroundColor: theme.components.tabs.trigger.active.bg },
3781
- tabDisabled: { backgroundColor: theme.components.tabs.trigger.disabled.bg },
3833
+ tabDefaultActive: { backgroundColor: "transparent" },
3834
+ tabDisabled: { backgroundColor: theme.components.tabs.disabled.bg },
3782
3835
  tabText: {
3783
3836
  flexShrink: 0,
3784
3837
  textAlign: "center",
3785
3838
  lineHeight: theme.tokens.typography.lineHeight.md,
3786
- color: theme.components.tabs.trigger.default.fg,
3839
+ color: theme.components.tabs.primary.trigger.default.fg,
3787
3840
  fontFamily: theme.tokens.typography.family.regular,
3788
3841
  fontSize: theme.tokens.typography.size.md,
3789
3842
  fontWeight: fontWeight$1(theme.tokens.typography.weight.regular)
3790
3843
  },
3791
- tabTextHover: { color: theme.components.tabs.trigger.hover.fg },
3792
- tabTextPressed: { color: theme.components.tabs.trigger.active.fg },
3793
- tabTextDisabled: { color: theme.components.tabs.trigger.disabled.fg },
3794
- tabTextActive: { color: theme.components.tabs.trigger.active.fg },
3795
- tabTextPillsActive: { color: theme.components.tabs.pills.active.fg },
3844
+ tabTextSm: {
3845
+ fontSize: theme.tokens.typography.size.sm,
3846
+ lineHeight: theme.tokens.typography.lineHeight.sm
3847
+ },
3848
+ tabTextLg: {
3849
+ fontSize: theme.tokens.typography.size.lg,
3850
+ lineHeight: theme.tokens.typography.lineHeight.lg
3851
+ },
3852
+ tabTextHover: { color: theme.components.tabs.primary.trigger.hover.fg },
3853
+ tabTextPressed: { color: theme.components.tabs.primary.trigger.active.fg },
3854
+ tabTextDisabled: { color: theme.components.tabs.disabled.fg },
3855
+ tabTextActive: { color: theme.components.tabs.primary.trigger.active.fg },
3856
+ tabTextPillsActive: { color: theme.components.tabs.primary.pills.active.fg },
3796
3857
  tabPills: {
3797
3858
  minHeight: 36,
3798
3859
  paddingHorizontal: theme.tokens.spacing[3],
@@ -3800,64 +3861,145 @@ const createStyles$5 = (theme) => StyleSheet.create({
3800
3861
  borderRadius: theme.tokens.radius.md
3801
3862
  },
3802
3863
  tabPillsActive: {
3803
- backgroundColor: theme.components.tabs.pills.active.bg,
3864
+ backgroundColor: "transparent",
3804
3865
  borderRadius: theme.tokens.radius.md
3805
3866
  },
3806
- tabPillsHover: { backgroundColor: theme.components.tabs.pills.hover.bg },
3807
- tabPillsPressed: { backgroundColor: theme.components.tabs.pills.active.bg },
3867
+ tabPillsHover: { backgroundColor: "transparent" },
3868
+ tabPillsPressed: { backgroundColor: "transparent" },
3808
3869
  tabIcon: {
3809
3870
  alignItems: "center",
3810
3871
  justifyContent: "center"
3811
3872
  },
3812
- tabIconHover: { color: theme.components.tabs.trigger.hover.fg },
3813
- tabIconPressed: { color: theme.components.tabs.trigger.active.fg },
3814
- tabIconActive: { color: theme.components.tabs.trigger.active.fg },
3815
- tabIconPillsActive: { color: theme.components.tabs.pills.active.fg }
3873
+ tabDescription: {
3874
+ fontSize: theme.tokens.typography.size.sm,
3875
+ color: theme.components.tabs.primary.trigger.default.fg
3876
+ },
3877
+ tabBadge: {
3878
+ minHeight: 20,
3879
+ minWidth: 20,
3880
+ alignItems: "center",
3881
+ justifyContent: "center",
3882
+ paddingHorizontal: theme.tokens.spacing[2],
3883
+ backgroundColor: theme.components.tabs.primary.pills.active.bg,
3884
+ borderRadius: theme.tokens.radius.full
3885
+ },
3886
+ tabBadgeLg: {
3887
+ minHeight: 24,
3888
+ minWidth: 24
3889
+ },
3890
+ tabBadgeText: {
3891
+ color: theme.components.tabs.primary.pills.active.fg,
3892
+ fontSize: theme.tokens.typography.size.sm,
3893
+ fontWeight: fontWeight$1(theme.tokens.typography.weight.medium)
3894
+ },
3895
+ tabIconHover: { color: theme.components.tabs.primary.trigger.hover.fg },
3896
+ tabIconPressed: { color: theme.components.tabs.primary.trigger.active.fg },
3897
+ tabIconActive: { color: theme.components.tabs.primary.trigger.active.fg },
3898
+ tabIconPillsActive: { color: theme.components.tabs.primary.pills.active.fg }
3816
3899
  });
3817
3900
  //#endregion
3818
- //#region src/components/Tabs/Tab/Tab.tsx
3819
- const Tab = ({ index, children, icon, disabled, style, textStyle }) => {
3901
+ //#region src/components/Tabs/Trigger/TabsBadge.tsx
3902
+ const TabsBadge = ({ children, style }) => {
3903
+ const styles = useThemeStyles(createStyles$5);
3904
+ const { color } = useTabs();
3905
+ const { theme } = useTheme();
3906
+ const palette = theme.components.tabs[color];
3907
+ return /* @__PURE__ */ jsx(View, {
3908
+ style: [
3909
+ styles.tabBadge,
3910
+ { backgroundColor: palette.pills.active.bg },
3911
+ style
3912
+ ],
3913
+ children: /* @__PURE__ */ jsx(Text, {
3914
+ style: [styles.tabBadgeText, { color: palette.pills.active.fg }],
3915
+ children
3916
+ })
3917
+ });
3918
+ };
3919
+ TabsBadge.displayName = "Tabs.Badge";
3920
+ //#endregion
3921
+ //#region src/components/Tabs/Trigger/TabsIcon.tsx
3922
+ const TabsIcon = ({ children, style }) => {
3923
+ return /* @__PURE__ */ jsx(View, {
3924
+ style: [useThemeStyles(createStyles$5).tabIcon, style],
3925
+ children
3926
+ });
3927
+ };
3928
+ TabsIcon.displayName = "Tabs.Icon";
3929
+ //#endregion
3930
+ //#region src/components/Tabs/Trigger/TabsTrigger.tsx
3931
+ const TabsTrigger = ({ value, children, icon, badge, description, disabled, style, textStyle }) => {
3820
3932
  const { theme } = useTheme();
3821
3933
  const styles = useThemeStyles(createStyles$5);
3822
- const { activeIndex, appearance, orientation, setActiveIndex } = useTabs();
3823
- const isActive = activeIndex === index;
3824
- const isPills = appearance === "pills";
3825
- const isUnderline = appearance === "underline";
3826
- const isDefault = appearance === "default";
3934
+ const { value: selectedValue, variant, color, size, orientation, disabled: rootDisabled, setValue, registerTrigger } = useTabs();
3935
+ const isDisabled = rootDisabled || disabled;
3936
+ const isActive = selectedValue === value;
3937
+ const isPills = variant === "pills";
3938
+ const isLine = variant === "line";
3939
+ const isSegmented = variant === "segmented";
3827
3940
  const isVertical = orientation === "vertical";
3828
- const iconColor = isPills && isActive ? theme.components.tabs.pills.active.fg : isActive ? theme.components.tabs.trigger.active.fg : theme.components.tabs.trigger.default.fg;
3941
+ const isSm = size === "sm";
3942
+ const isLg = size === "lg";
3943
+ const palette = theme.components.tabs[color];
3944
+ const state = isDisabled ? theme.components.tabs.disabled : isPills ? isActive ? palette.pills.active : palette.pills.default : isActive ? palette.trigger.active : palette.trigger.default;
3945
+ const pressedState = isDisabled ? state : isPills ? isActive ? palette.pills.active : palette.pills.hover : isActive ? palette.trigger.active : palette.trigger.hover;
3946
+ useEffect(() => {
3947
+ registerTrigger(value, Boolean(isDisabled), true);
3948
+ return () => {
3949
+ registerTrigger(value, Boolean(isDisabled), false);
3950
+ };
3951
+ }, [
3952
+ isDisabled,
3953
+ registerTrigger,
3954
+ value
3955
+ ]);
3956
+ const iconColor = isPills && isActive ? palette.pills.active.fg : isActive ? palette.trigger.active.fg : state.fg;
3829
3957
  const renderedIcon = isValidElement(icon) ? cloneElement(icon, { color: iconColor }) : icon;
3830
3958
  return /* @__PURE__ */ jsx(Pressable, {
3831
- disabled,
3959
+ disabled: isDisabled,
3832
3960
  accessibilityRole: "tab",
3833
3961
  accessibilityState: {
3834
3962
  selected: isActive,
3835
- disabled
3963
+ disabled: isDisabled
3836
3964
  },
3837
3965
  onPress: () => {
3838
- if (!disabled && !isActive) setActiveIndex(index);
3966
+ if (!isDisabled && !isActive) setValue(value);
3839
3967
  },
3840
3968
  style: ({ pressed }) => [
3841
3969
  styles.tab,
3970
+ isSm && styles.tabSm,
3971
+ isLg && styles.tabLg,
3842
3972
  isVertical && styles.tabVertical,
3843
3973
  isPills && styles.tabPills,
3844
- isPills && isActive && styles.tabPillsActive,
3845
- pressed && !disabled && styles.tabPressed,
3846
- isDefault && isActive && styles.tabDefaultActive,
3847
- disabled && styles.tabDisabled,
3974
+ isSegmented && styles.tabSegmented,
3975
+ isSegmented && isSm && styles.tabSegmentedSm,
3976
+ isSegmented && isLg && styles.tabSegmentedLg,
3977
+ isPills && isActive && {
3978
+ borderColor: palette.pills.active.border,
3979
+ backgroundColor: palette.pills.active.bg
3980
+ },
3981
+ {
3982
+ borderColor: state.border,
3983
+ backgroundColor: pressed ? pressedState.bg : state.bg
3984
+ },
3985
+ isSegmented && isActive && {
3986
+ borderColor: palette.segmented.active.border,
3987
+ backgroundColor: palette.segmented.active.bg
3988
+ },
3989
+ isDisabled && styles.tabDisabled,
3848
3990
  style
3849
3991
  ],
3850
3992
  children: ({ pressed }) => /* @__PURE__ */ jsxs(Fragment, { children: [
3851
- isUnderline && !isVertical && /* @__PURE__ */ jsx(View, {
3993
+ isLine && !isVertical && /* @__PURE__ */ jsx(View, {
3852
3994
  pointerEvents: "none",
3853
- style: [styles.horizontalIndicator, isActive && styles.horizontalIndicatorActive]
3995
+ style: [styles.horizontalIndicator, isActive && { backgroundColor: palette.indicator.bg }]
3854
3996
  }),
3855
- isUnderline && isVertical && /* @__PURE__ */ jsx(View, {
3997
+ isLine && isVertical && /* @__PURE__ */ jsx(View, {
3856
3998
  pointerEvents: "none",
3857
3999
  style: [
3858
4000
  styles.verticalIndicator,
3859
- isActive && styles.verticalIndicatorActive,
3860
- pressed && !isActive && !disabled && styles.verticalIndicatorPressed
4001
+ isActive && { backgroundColor: palette.indicator.bg },
4002
+ pressed && !isActive && !isDisabled && { backgroundColor: palette.indicator.hoverBg }
3861
4003
  ]
3862
4004
  }),
3863
4005
  icon != null && /* @__PURE__ */ jsx(View, {
@@ -3869,17 +4011,40 @@ const Tab = ({ index, children, icon, disabled, style, textStyle }) => {
3869
4011
  ellipsizeMode: "tail",
3870
4012
  style: [
3871
4013
  styles.tabText,
3872
- isPills && isActive && styles.tabTextPillsActive,
3873
- !isPills && isActive && styles.tabTextActive,
3874
- disabled && styles.tabTextDisabled,
4014
+ isSm && styles.tabTextSm,
4015
+ isLg && styles.tabTextLg,
4016
+ { color: isSegmented && isActive ? palette.segmented.active.fg : state.fg },
3875
4017
  textStyle
3876
4018
  ],
3877
4019
  children
4020
+ }),
4021
+ description != null && /* @__PURE__ */ jsx(Text, {
4022
+ numberOfLines: 2,
4023
+ ellipsizeMode: "tail",
4024
+ style: [
4025
+ styles.tabText,
4026
+ styles.tabDescription,
4027
+ isSm && styles.tabTextSm,
4028
+ isLg && styles.tabTextLg,
4029
+ { color: isDisabled ? theme.components.tabs.disabled.fg : state.fg }
4030
+ ],
4031
+ children: description
4032
+ }),
4033
+ badge != null && /* @__PURE__ */ jsx(View, {
4034
+ style: [
4035
+ styles.tabBadge,
4036
+ isLg && styles.tabBadgeLg,
4037
+ { backgroundColor: palette.pills.active.bg }
4038
+ ],
4039
+ children: /* @__PURE__ */ jsx(Text, {
4040
+ style: [styles.tabBadgeText, { color: palette.pills.active.fg }],
4041
+ children: badge
4042
+ })
3878
4043
  })
3879
4044
  ] })
3880
4045
  });
3881
4046
  };
3882
- Tab.displayName = "Tab";
4047
+ TabsTrigger.displayName = "Tabs.Trigger";
3883
4048
  //#endregion
3884
4049
  //#region src/components/Tabs/Tabs.styles.ts
3885
4050
  const createStyles$4 = (theme) => StyleSheet.create({
@@ -3896,26 +4061,76 @@ const createStyles$4 = (theme) => StyleSheet.create({
3896
4061
  }
3897
4062
  });
3898
4063
  //#endregion
3899
- //#region src/components/Tabs/Tabs.tsx
3900
- const TabsRoot = ({ children, activeIndex: controlledActiveIndex, defaultActiveIndex = 0, onChange, orientation = "horizontal", appearance = "pills", style }) => {
4064
+ //#region src/components/Tabs/Root/TabsRoot.tsx
4065
+ const TabsRoot = ({ children, value: controlledValue, defaultValue, onValueChange, orientation = "horizontal", activationMode = "automatic", variant = "line", color = "primary", size = "md", keepMounted = false, lazyMount = false, disabled = false, style }) => {
3901
4066
  const styles = useThemeStyles(createStyles$4);
3902
- const { activeIndex, setActiveIndex } = useTabs$1({
3903
- activeIndex: controlledActiveIndex,
3904
- defaultActiveIndex,
3905
- onChange,
3906
- orientation
4067
+ const isControlled = controlledValue !== void 0;
4068
+ const [version, setVersion] = useState(0);
4069
+ const triggersRef = useRef([]);
4070
+ const { value, setValue } = useTabs$1({
4071
+ value: controlledValue,
4072
+ defaultValue,
4073
+ onValueChange
3907
4074
  });
4075
+ const registerTrigger = useCallback((triggerValue, triggerDisabled, mounted) => {
4076
+ const existingIndex = triggersRef.current.findIndex((tab) => tab.value === triggerValue);
4077
+ if (!mounted) {
4078
+ if (existingIndex >= 0) {
4079
+ triggersRef.current.splice(existingIndex, 1);
4080
+ setVersion((current) => current + 1);
4081
+ }
4082
+ return;
4083
+ }
4084
+ const nextTab = {
4085
+ value: triggerValue,
4086
+ disabled: triggerDisabled
4087
+ };
4088
+ if (existingIndex >= 0) triggersRef.current[existingIndex] = nextTab;
4089
+ else triggersRef.current.push(nextTab);
4090
+ setVersion((current) => current + 1);
4091
+ }, []);
4092
+ useEffect(() => {
4093
+ const enabledTabs = triggersRef.current.filter((tab) => !tab.disabled);
4094
+ const selectedExists = triggersRef.current.some((tab) => tab.value === value);
4095
+ if (isControlled) {
4096
+ if (value && !selectedExists && triggersRef.current.length > 0) console.warn(`Tabs: value "${value}" does not match any Tabs.Trigger.`);
4097
+ return;
4098
+ }
4099
+ if (!value || !selectedExists) {
4100
+ const firstEnabledValue = enabledTabs[0]?.value;
4101
+ if (firstEnabledValue) setValue(firstEnabledValue);
4102
+ }
4103
+ }, [
4104
+ isControlled,
4105
+ setValue,
4106
+ value,
4107
+ version
4108
+ ]);
3908
4109
  return /* @__PURE__ */ jsx(TabsProvider, {
3909
4110
  value: useMemo(() => ({
3910
- activeIndex,
3911
- appearance,
4111
+ value,
4112
+ setValue,
3912
4113
  orientation,
3913
- setActiveIndex
4114
+ activationMode,
4115
+ variant,
4116
+ color,
4117
+ size,
4118
+ keepMounted,
4119
+ lazyMount,
4120
+ disabled,
4121
+ registerTrigger
3914
4122
  }), [
3915
- activeIndex,
3916
- appearance,
4123
+ activationMode,
4124
+ color,
4125
+ disabled,
4126
+ keepMounted,
4127
+ lazyMount,
3917
4128
  orientation,
3918
- setActiveIndex
4129
+ registerTrigger,
4130
+ setValue,
4131
+ size,
4132
+ value,
4133
+ variant
3919
4134
  ]),
3920
4135
  children: /* @__PURE__ */ jsx(View, {
3921
4136
  style: [
@@ -3927,14 +4142,18 @@ const TabsRoot = ({ children, activeIndex: controlledActiveIndex, defaultActiveI
3927
4142
  })
3928
4143
  });
3929
4144
  };
3930
- TabsRoot.displayName = "TabsRoot";
4145
+ TabsRoot.displayName = "Tabs";
3931
4146
  //#endregion
3932
- //#region src/components/Tabs/index.ts
4147
+ //#region src/components/Tabs/Tabs.tsx
3933
4148
  const Tabs = Object.assign(TabsRoot, {
3934
4149
  List: TabsList,
3935
- Tab,
3936
- Panel: TabsPanel
4150
+ Trigger: TabsTrigger,
4151
+ Content: TabsContent,
4152
+ Indicator: TabsIndicator,
4153
+ Icon: TabsIcon,
4154
+ Badge: TabsBadge
3937
4155
  });
4156
+ Tabs.displayName = "Tabs";
3938
4157
  //#endregion
3939
4158
  //#region src/components/Tooltip/Tooltip.styles.ts
3940
4159
  const createStyles$3 = (theme) => StyleSheet.create({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellira-ui/react-native",
3
- "version": "2.39.5",
3
+ "version": "2.40.0",
4
4
  "description": "React Native components for Vellira Design System",
5
5
  "author": "Roman Bakurov",
6
6
  "license": "MIT",