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';