Skip to content

Commit

Permalink
Merge pull request #222 from BouyguesTelecom/fix/tabs-native
Browse files Browse the repository at this point in the history
init tabs native
  • Loading branch information
JulienMora authored Dec 16, 2024
2 parents 81f083e + f32be6d commit 44f4c6f
Show file tree
Hide file tree
Showing 16 changed files with 310 additions and 40 deletions.
28 changes: 8 additions & 20 deletions examples/react-template/screens/Tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,17 @@ export const TabScreen = (): JSX.Element => {

<Tabs>
<TabList>
<Tab
active={index === 0}
label='Tab 1'
iconName={IconName.ALERT}
href='/hello'
onClick={(e) => setIndex(e.target.dataset.index)}
/>
<Tab
active={index === 1}
label='Tab 2'
iconName={IconName.ALERT}
onClick={(e) => setIndex(e.target.dataset.index)}
/>
<Tab
active={index === 2}
label='Tab 3'
iconName={IconName.ALERT}
onClick={(e) => setIndex(e.target.dataset.index)}
/>
<Tab active={index === 0} label='Tab 1' iconName={IconName.ALERT} href='/hello' onClick={() => setIndex(0)} />
<Tab active={index === 1} label='Tab 2' iconName={IconName.ALERT} onClick={() => setIndex(1)} />
<Tab active={index === 2} label='Tab 3' iconName={IconName.ALERT} onClick={() => setIndex(2)} disabled />
</TabList>
<TabPanels>
<TabPanel>
<Title>Tab 1</Title>
<Title>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nisi ratione impedit ipsam quidem autem ipsum
tempora magnam dignissimos nulla consequuntur molestias architecto soluta at qui, delectus, repellat ea
obcaecati numquam.
</Title>
</TabPanel>
<TabPanel>
<Title>Tab 2</Title>
Expand Down
5 changes: 4 additions & 1 deletion packages/react/components/enumsComponentsName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@ export enum ComponentName {
TableTh = 'TableTh',
TableTd = 'TableTd',
Tabs = 'Tabs',
TabsItem = 'TabsItem',
Tab = 'Tab',
TabList = 'TabList',
TabPanel = 'TabPanel',
TabPanels = 'TabPanels',
Tag = 'Tag',
TagVariant = 'TagVariant',
TagList = 'TagList',
Expand Down
40 changes: 40 additions & 0 deletions packages/react/components/tabs/Tabs.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ComponentName } from '@/components/enumsComponentsName'
import { TabsProps } from '@/components/tabs/TabsProps'
import { TabsContext } from '@/components/tabs/context'
import React from 'react'
import { View } from 'react-native'

/**
* Tabs Component
* @param children {ReactNode} Children for tabs
* @param activeIndex {number} default active tab index
* @param inverted {boolean} Inverted style
*/
const Tabs = ({ children, activeIndex, inverted }: TabsProps) => {
const [currentIndex, setCurrentIndex] = React.useState<number>(activeIndex || 0)
const [isInverted, setIsInverted] = React.useState<boolean>(inverted || false)

React.useEffect(() => {
activeIndex !== undefined && setCurrentIndex(activeIndex)
}, [activeIndex])

React.useEffect(() => {
setIsInverted(inverted || false)
}, [inverted])

return (
<TabsContext.Provider
value={{
activeIndex: currentIndex,
inverted: isInverted,
setInverted: setIsInverted,
setActiveIndex: setCurrentIndex,
}}
>
<View>{children}</View>
</TabsContext.Provider>
)
}

Tabs.displayName = ComponentName.Tabs
export default Tabs
16 changes: 13 additions & 3 deletions packages/react/components/tabs/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,32 @@ import React, { useEffect, useState } from 'react'
* Tabs Component
* @param children {ReactNode} Children for tabs
* @param activeIndex {number} default active tab index
* @param inverted {boolean} Inverted style
* - -------------------------- WEB PROPERTIES -------------------------------
* @param className {string} Additionnal CSS Classes
* @param fullwidth {boolean} Fullwidth tabs
* @param id
*/
const Tabs = ({ children, className, id, activeIndex, fullwidth }: TabsProps) => {
const Tabs = ({ children, className, id, activeIndex, fullwidth, inverted }: TabsProps) => {
const [currentIndex, setCurrentIndex] = useState<number>(activeIndex || 0)
const [isInverted, setIsInverted] = React.useState<boolean>(inverted || false)

const { styled } = useTrilogyContext()
const classes = hashClass(styled, clsx('tabs', fullwidth && is('fullwidth'), className))
const classes = hashClass(styled, clsx('tabs', fullwidth && is('fullwidth'), inverted && is('inverted'), className))

useEffect(() => {
activeIndex !== undefined && setCurrentIndex(activeIndex)
}, [activeIndex])

return (
<TabsContext.Provider value={{ activeIndex: currentIndex, setActiveIndex: setCurrentIndex }}>
<TabsContext.Provider
value={{
activeIndex: currentIndex,
inverted: isInverted,
setInverted: setIsInverted,
setActiveIndex: setCurrentIndex,
}}
>
<div id={id} className={classes} data-tabs-context=''>
{children}
</div>
Expand Down
4 changes: 2 additions & 2 deletions packages/react/components/tabs/TabsProps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Accessibility, AlignableProps, Clickable, Dev } from '../../objects'
import { CommonProps } from '../../objects/facets/CommonProps'
import { Accessibility, AlignableProps, Clickable, Dev } from '@/objects'
import { CommonProps } from '@/objects/facets/CommonProps'

/**
* Tabs Interface
Expand Down
4 changes: 4 additions & 0 deletions packages/react/components/tabs/context/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import React from 'react'
interface IContext {
activeIndex: number
setActiveIndex: React.Dispatch<React.SetStateAction<number>>
inverted: boolean
setInverted: React.Dispatch<React.SetStateAction<boolean>>
}

export const TabsContext = React.createContext<IContext>({
activeIndex: 0,
inverted: false,
setActiveIndex: () => 0,
setInverted: () => false,
})
44 changes: 44 additions & 0 deletions packages/react/components/tabs/tab-list/TabList.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ComponentName } from '@/components/enumsComponentsName'
import { TabsContext } from '@/components/tabs/context'
import Tab from '@/components/tabs/tab-list/tab/Tab'
import { TabListProps } from '@/components/tabs/tab-list/TabListProps'
import { getColorStyle, TrilogyColor } from '@/objects'
import React from 'react'
import { ScrollView, StyleSheet } from 'react-native'

/**
* Tabs Nav Component
* @param children {ReactChild} React Child Element
* @param className
* @param id
* @param testId
* @param align
* @param others
*/
const TabList = ({ children, ...others }: TabListProps) => {
const { inverted } = React.useContext(TabsContext)

const styles = React.useMemo(
() =>
StyleSheet.create({
tabList: {
flexDirection: 'row',
overflow: 'visible',
backgroundColor: inverted ? getColorStyle(TrilogyColor.MAIN) : undefined,
},
}),
[inverted],
)

return (
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.tabList} {...others}>
{React.Children.map(children, (child, index) => {
if (!React.isValidElement(child)) return false
return <Tab {...child.props} index={index} />
})}
</ScrollView>
)
}

TabList.displayName = ComponentName.TabList
export default TabList
4 changes: 2 additions & 2 deletions packages/react/components/tabs/tab-list/TabListProps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Alignable, AlignableValues, Dev } from '../../../objects'
import { CommonProps } from '../../../objects/facets/CommonProps'
import { Alignable, AlignableValues, Dev } from '@/objects'
import { CommonProps } from '@/objects/facets/CommonProps'

/**
* Tabs Item Interface
Expand Down
79 changes: 79 additions & 0 deletions packages/react/components/tabs/tab-list/tab/Tab.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { ComponentName } from '@/components/enumsComponentsName'
import { Icon } from '@/components/icon'
import { TabsContext } from '@/components/tabs/context'
import { TabProps } from '@/components/tabs/tab-list/tab/TabProps'
import { Text } from '@/components/text'
import { getColorStyle, TrilogyColor } from '@/objects/facets/Color'
import React from 'react'
import { GestureResponderEvent, Linking, StyleSheet, TouchableOpacity, View } from 'react-native'

/**
* Tabs Item Component
* @param active {boolean} active tab item
* @param children {ReactChild} React Child Element
* @param onClick onClick Event
* @param iconName {IconNameValues | IconName} add icon name
* @param disabled {boolean} disable tab item
* @param label {string} Tab content
* @param to {string} Link
* @param href {string} Link
*/
const Tab = ({ active, onClick, to, href, iconName, label, disabled, ...others }: TabProps) => {
const { index, ...props } = others as any
const { activeIndex, setActiveIndex, inverted } = React.useContext(TabsContext)
const isActive = React.useMemo(() => activeIndex === index, [activeIndex, index])

const handleClick = React.useCallback(
(e: GestureResponderEvent) => {
if (!disabled) {
if (onClick) onClick(e)
if (!to && !href) setActiveIndex(index)
if (to || href) Linking.openURL(to || href || '')
}
},
[disabled, onClick, index, setActiveIndex, to, href],
)

const styles = React.useMemo(
() =>
StyleSheet.create({
tab: {
paddingVertical: 4,
paddingHorizontal: 12,
borderBottomWidth: 2,
borderBottomColor: isActive
? getColorStyle(disabled ? TrilogyColor.DISABLED : inverted ? TrilogyColor.BACKGROUND : TrilogyColor.MAIN)
: 'transparent',
},
text: {
textAlign: 'center',
color: getColorStyle(
disabled ? TrilogyColor.DISABLED : inverted ? TrilogyColor.BACKGROUND : TrilogyColor.MAIN,
),
},
}),
[isActive, disabled, inverted],
)

React.useEffect(() => {
if (active) setActiveIndex(index)
}, [active, setActiveIndex, index])

return (
<TouchableOpacity activeOpacity={1} style={styles.tab} onPress={handleClick} {...props}>
{iconName && (
<View style={{ alignSelf: 'center' }}>
<Icon
color={disabled ? TrilogyColor.DISABLED : inverted ? TrilogyColor.BACKGROUND : TrilogyColor.MAIN}
size='small'
name={iconName}
/>
</View>
)}
<Text style={styles.text}>{label && label}</Text>
</TouchableOpacity>
)
}

Tab.displayName = ComponentName.Tab
export default Tab
12 changes: 7 additions & 5 deletions packages/react/components/tabs/tab-list/tab/Tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import React from 'react'
* @param iconName {IconNameValues | IconName} add icon name
* @param disabled {boolean} disable tab item
* @param label {string} Tab content
* @param to {string} Link
* @param href {string} <a />
* - -------------------------- WEB PROPERTIES -------------------------------
* @param className {string} Additionnal CSS Classes
* @param testId {string} Test Id for Test Integration
* @param to {string} Link
* @param href {string} <a />
* @param routerLink Custom Router Link as props
*/
const Tab = ({
Expand All @@ -44,10 +44,12 @@ const Tab = ({

const handleClick = React.useCallback(
(e: React.MouseEvent) => {
setActiveIndex(index)
if (!disabled && onClick) onClick(e)
if (!disabled) {
if (!routerLink) setActiveIndex(index)
if (onClick) onClick(e)
}
},
[disabled, onClick, index, setActiveIndex],
[disabled, onClick, index, setActiveIndex, routerLink],
)

React.useEffect(() => {
Expand Down
6 changes: 3 additions & 3 deletions packages/react/components/tabs/tab-list/tab/TabProps.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IconName, IconNameValues } from '../../../../components/icon'
import { Accessibility, Clickable, Dev } from '../../../../objects'
import { CommonProps } from '../../../../objects/facets/CommonProps'
import { IconName, IconNameValues } from '@/components/icon'
import { Accessibility, Clickable, Dev } from '@/objects'
import { CommonProps } from '@/objects/facets/CommonProps'

/**
* Tabs Item Interface
Expand Down
42 changes: 42 additions & 0 deletions packages/react/components/tabs/tab-panels/TabPanels.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ComponentName } from '@/components/enumsComponentsName'
import { TabsContext } from '@/components/tabs/context'
import TabPanel from '@/components/tabs/tab-panels/tab-panel'
import { TabPanelsProps } from '@/components/tabs/tab-panels/TabPanelsProps'
import { getColorStyle, TrilogyColor } from '@/objects'
import React from 'react'
import { StyleSheet, View } from 'react-native'

/**
* Tabs Nav Component
* @param children {ReactChild} React Child Element
* @param className
* @param id
* @param testId
* @param others
*/
const TabPanels = ({ children, ...others }: TabPanelsProps) => {
const { inverted } = React.useContext(TabsContext)

const styles = React.useMemo(
() =>
StyleSheet.create({
tabPanels: {
paddingVertical: 8,
backgroundColor: inverted ? getColorStyle(TrilogyColor.MAIN) : undefined,
},
}),
[inverted],
)

return (
<View style={styles.tabPanels} {...others}>
{React.Children.map(children, (child, index) => {
if (!React.isValidElement(child)) return false
return <TabPanel {...child.props} index={index} />
})}
</View>
)
}

TabPanels.displayName = ComponentName.TabPanels
export default TabPanels
4 changes: 2 additions & 2 deletions packages/react/components/tabs/tab-panels/TabPanelsProps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Dev } from '../../../objects'
import { CommonProps } from '../../../objects/facets/CommonProps'
import { Dev } from '@/objects'
import { CommonProps } from '@/objects/facets/CommonProps'

/**
* Tabs Item Interface
Expand Down
Loading

0 comments on commit 44f4c6f

Please sign in to comment.