diff --git a/example/src/examples/TabsExample.tsx b/example/src/examples/TabsExample.tsx new file mode 100644 index 0000000..47d8f65 --- /dev/null +++ b/example/src/examples/TabsExample.tsx @@ -0,0 +1,36 @@ +import React, { useState } from 'react'; +import { View } from 'react-native'; +import { Tabs, Panel, Text } from 'react95-native'; + +const AppBarExample = () => { + const [value, setValue] = useState(0); + const [secondValue, setSecondValue] = useState(0); + + return ( + + + + Shoes + Accesories + Clothing + + + {value} + + + + + + Shoes + A + Clothing + + + {secondValue} + + + + ); +}; + +export default AppBarExample; diff --git a/example/src/examples/index.tsx b/example/src/examples/index.tsx index 1abf238..84b77fa 100644 --- a/example/src/examples/index.tsx +++ b/example/src/examples/index.tsx @@ -13,6 +13,7 @@ import MenuExample from './MenuExample'; import ProgressExample from './ProgressExample'; import SelectExample from './SelectExample'; import DesktopExample from './DesktopExample'; +import TabsExample from './TabsExample'; export default [ { name: 'ButtonExample', component: ButtonExample, title: 'Button' }, @@ -30,6 +31,7 @@ export default [ { name: 'ProgressExample', component: ProgressExample, title: 'Progress' }, { name: 'SelectExample', component: SelectExample, title: 'Select' }, { name: 'DesktopExample', component: DesktopExample, title: 'Desktop' }, + { name: 'TabsExample', component: TabsExample, title: 'Tabs' }, ].sort((a, b) => { /* Sort screens alphabetically */ if (a.title < b.title) return -1; diff --git a/src/Tabs/Tabs.tsx b/src/Tabs/Tabs.tsx new file mode 100644 index 0000000..0392c4d --- /dev/null +++ b/src/Tabs/Tabs.tsx @@ -0,0 +1,179 @@ +import React, { useState } from 'react'; +import { + StyleProp, + StyleSheet, + ViewStyle, + TouchableHighlight, + View, +} from 'react-native'; + +import { original as theme } from '../common/themes'; +import { border, padding, margin, blockSizes } from '../common/styles'; +import { Border } from '../common/styleElements'; +import { Text, Panel } from '..'; + +type TabsProps = { + children?: React.ReactNode; + style?: StyleProp; + value: any; + onChange?: () => void; + stretch?: boolean; +}; + +const Tabs = ({ + value, + onChange, + children, + stretch = false, + ...rest +}: TabsProps) => { + const childrenWithProps = React.Children.map(children, child => { + if (!React.isValidElement(child)) { + return null; + } + const tabProps = { + selected: child.props.value === value, + onPress: onChange, + stretch, + }; + return React.cloneElement(child, tabProps); + }); + + return ( + + {childrenWithProps} + + + ); +}; + +type TabBodyProps = { + children?: React.ReactNode; + style?: StyleProp; +}; + +const Body = ({ children, style, ...rest }: TabBodyProps) => { + return ( + + {children} + + ); +}; + +type TabProps = { + children?: React.ReactNode; + style?: StyleProp; + value: any; + onPress?: () => void; + selected?: boolean; + stretch?: boolean; +}; + +const Tab = ({ + value, + onPress, + selected, + stretch, + children, + ...rest +}: TabProps) => { + const [isPressed, setIsPressed] = useState(false); + + return ( + onPress(value)} + onHideUnderlay={() => setIsPressed(false)} + onShowUnderlay={() => setIsPressed(true)} + underlayColor='none' + style={[ + styles.tab, + { zIndex: selected ? 1 : 0 }, + stretch ? { flexGrow: 1 } : { width: 'auto' }, + selected ? margin(0, -8) : margin(0, 0), + ]} + {...rest} + > + + {/* TODO: add 'background' boolean prop to Border component since its usually used with background color */} + + {children} + + {isPressed && } + + + ); +}; + +const styles = StyleSheet.create({ + tabs: { + display: 'flex', + flexDirection: 'row', + alignItems: 'flex-end', + paddingHorizontal: 8, + zIndex: 1, + bottom: -2, + }, + body: { + display: 'flex', + padding: 16, + }, + tab: { + alignSelf: 'flex-end', + }, + tabContent: { + justifyContent: 'center', + width: 'auto', + }, + tabBodyBorder: { + height: 4, + position: 'absolute', + left: 4, + right: 4, + bottom: -2, + backgroundColor: theme.borderLight, + borderTopWidth: 2, + borderTopColor: theme.borderLightest, + }, + mask: { + height: 4, + position: 'absolute', + left: 4, + right: 4, + bottom: -2, + backgroundColor: theme.material, + }, + focusOutline: { + position: 'absolute', + left: 6, + top: 6, + bottom: 4, + right: 6, + ...border.focusOutline, + }, +}); + +Tabs.Tab = Tab; +Tabs.Body = Body; + +export default Tabs; diff --git a/src/Tabs/index.ts b/src/Tabs/index.ts new file mode 100644 index 0000000..bc6749b --- /dev/null +++ b/src/Tabs/index.ts @@ -0,0 +1 @@ +export { default } from './Tabs'; diff --git a/src/common/styleElements.tsx b/src/common/styleElements.tsx index 1e9eea2..7d74e53 100644 --- a/src/common/styleElements.tsx +++ b/src/common/styleElements.tsx @@ -9,6 +9,7 @@ type BorderProps = { invert?: boolean; variant?: 'default' | 'well' | 'outside' | 'cutout'; style?: object; + sharedStyle?: object; radius?: number; children?: React.ReactNode; }; @@ -17,7 +18,8 @@ export const Border = ({ invert = false, variant = 'default', style = {}, - radius, + sharedStyle = {}, + radius = 0, children, }: BorderProps) => { const wrapper: StyleProp = []; @@ -37,26 +39,34 @@ export const Border = ({ inner = [border.cutoutInner]; } - const sharedStyles = [ - borderStyles.position, - { - borderRadius: radius || 0, - }, - ]; + const getSharedStyles = (function () { + let r = radius + 4; + + return () => { + r -= 2; + return [ + borderStyles.position, + sharedStyle, + { + borderRadius: radius ? r : 0, + }, + ]; + }; + })(); return ( {outer ? ( - + {inner ? ( - {children} + {children} ) : ( children )} diff --git a/src/common/styles.ts b/src/common/styles.ts index ea6d9e9..143a396 100644 --- a/src/common/styles.ts +++ b/src/common/styles.ts @@ -117,3 +117,21 @@ export const blockSizes = { md: 35, lg: 43, }; + +export function padding(a: number, b?: number, c?: number, d?: number) { + return { + paddingTop: a, + paddingRight: b || a, + paddingBottom: c || a, + paddingLeft: d || b || a, + }; +} + +export function margin(a: number, b?: number, c?: number, d?: number) { + return { + marginTop: a, + marginRight: b || a, + marginBottom: c || a, + marginLeft: d || b || a, + }; +} diff --git a/src/index.ts b/src/index.ts index 6d1f079..2a9baa1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,5 +13,6 @@ export { default as Progress } from './Progress'; export { default as Select } from './Select'; export { default as Desktop } from './Desktop'; export { default as Menu } from './Menu'; +export { default as Tabs } from './Tabs'; export * from './common/themes';