diff --git a/example/.ondevice/storybook.requires.js b/example/.ondevice/storybook.requires.js index f21f7dd1..9e747d17 100644 --- a/example/.ondevice/storybook.requires.js +++ b/example/.ondevice/storybook.requires.js @@ -54,6 +54,7 @@ const getStories = () => { "./src/stories/Card.stories.tsx": require("../src/stories/Card.stories.tsx"), "./src/stories/Checkbox.stories.tsx": require("../src/stories/Checkbox.stories.tsx"), "./src/stories/CodeInput.stories.tsx": require("../src/stories/CodeInput.stories.tsx"), + "./src/stories/Header.stories.tsx": require("../src/stories/Header.stories.tsx"), "./src/stories/Progress.stories.tsx": require("../src/stories/Progress.stories.tsx"), "./src/stories/RadioButton.stories.tsx": require("../src/stories/RadioButton.stories.tsx"), "./src/stories/Slider.stories.tsx": require("../src/stories/Slider.stories.tsx"), diff --git a/example/src/stories/Header.stories.tsx b/example/src/stories/Header.stories.tsx new file mode 100644 index 00000000..7cbbd86c --- /dev/null +++ b/example/src/stories/Header.stories.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import type {ComponentMeta, ComponentStory} from '@storybook/react' +import {Header} from 'rn-base-component' +import {Text} from 'react-native' + +export default { + title: 'components/Header', + component: Header, +} as ComponentMeta + +export const Basic: ComponentStory = rest =>
+ +Basic.args = { + title: 'Header Title', + rightComponent: Right Comp, +} diff --git a/src/assets/images/chevron-left.png b/src/assets/images/chevron-left.png new file mode 100644 index 00000000..02ed493d Binary files /dev/null and b/src/assets/images/chevron-left.png differ diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 43b4a8c3..b7bde087 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -108,8 +108,6 @@ const ButtonWrapper = styled.TouchableOpacity( outlineColor, borderRadius, disabled, - leftIcon, - rightIcon, }: Omit & {theme?: ITheme}) => ({ paddingVertical: theme?.spacing.small, flexDirection: 'row', @@ -117,9 +115,7 @@ const ButtonWrapper = styled.TouchableOpacity( borderRadius, backgroundColor: disabled ? theme?.colors.muted : backgroundColor || theme?.colors.green, justifyContent: 'center', - ...((leftIcon || rightIcon) && { - alignItems: 'center', - }), + alignItems: 'center', alignSelf: 'center', ...(outline && { borderWidth: outlineWidth || 1, diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx new file mode 100644 index 00000000..196aafe7 --- /dev/null +++ b/src/components/Header/Header.tsx @@ -0,0 +1,175 @@ +import React, {ReactNode} from 'react' +import {StyleSheet, TouchableOpacity} from 'react-native' +import type {ImageStyle, StyleProp, TextStyle, ViewStyle} from 'react-native' +import styled from 'styled-components/native' +import {TextBold} from '../Text/Text' +import {Images} from '../../theme' +import {hitSlop, metrics} from '../../helpers' +import {useTheme} from '../../hooks' + +export interface HeaderProps { + /** + * The top padding for the notch of device, usually we will use react-native-safe-area-context to get the inset + */ + paddingTop?: number + /** + * Height of the header + */ + height?: number + /** + * Style of Header container + */ + containerStyle?: StyleProp + /** + * Width of Header bottom border + */ + borderBottomWidth?: number + /** + * Color of Header bottom border + */ + borderBottomColor?: string + /** + * Title of the header, will be use if no custom CenterComponent is defined + */ + title?: string + /** + * The color of Header title + */ + titleColor?: string + /** + * The custom style of Header title + */ + titleStyle?: StyleProp + /** + * Background color of the Header + */ + backgroundColor?: string + /** + * Show the back to previous screen icon, default value is true + */ + hasBackButton?: boolean + /** + * The color of back to previous screen icon + */ + backIconColor?: string + /** + * The action for the back to previous screen icon + */ + backButtonPress?: () => void + /** + * The left component of the Header + */ + leftComponent?: ReactNode + /** + * The style of left component of the Header + */ + leftStyle?: StyleProp + /** + * The center component of the Header, will replace Title + */ + centerComponent?: ReactNode + /** + * The style of center component of the Header + */ + centerStyle?: StyleProp + /** + * The right component of the Header + */ + rightComponent?: ReactNode + /** + * The style of right component of the Header + */ + rightStyle?: StyleProp +} + +const Header: React.FC = ({ + paddingTop, + height, + containerStyle, + borderBottomWidth, + borderBottomColor, + title, + titleColor, + titleStyle, + backgroundColor, + hasBackButton = true, + backIconColor, + backButtonPress, + leftComponent, + leftStyle, + centerComponent, + centerStyle, + rightComponent, + rightStyle, +}) => { + const HeaderTheme = useTheme().components.Header + return ( + + + {leftComponent + ? leftComponent + : hasBackButton && ( + backButtonPress?.()}> + + + )} + + + + {centerComponent + ? centerComponent + : !!title && ( + + {title} + + )} + + + + {!!rightComponent && rightComponent} + + + ) +} + +const Container = styled.View((style: ViewStyle) => ({ + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: metrics.small, + ...style, +})) + +const SidesContainer = styled.View((style: ViewStyle) => ({ + flex: 1, + justifyContent: 'center', + ...style, +})) + +const TitleContainer = styled.View((style: ViewStyle) => ({ + flex: 4, + justifyContent: 'center', + alignItems: 'center', + ...style, +})) + +const IconBack = styled.Image(({tintColor}: ImageStyle) => ({ + height: metrics.large, + aspectRatio: 1, + tintColor, +})) + +export default Header diff --git a/src/components/index.ts b/src/components/index.ts index fc57e1fe..2f1c9287 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -7,6 +7,7 @@ import Checkbox from './Checkbox/Checkbox' import CodeInput from './CodeInput/CodeInput' import Slider from './Slider/Slider' import Card from './Card/Card' +import Header from './Header/Header' import TextInput from './TextInput/TextInput' export { @@ -17,6 +18,7 @@ export { ButtonTransparent, CodeInput, Checkbox, + Header, Progress, Slider, RadioButton, @@ -24,4 +26,5 @@ export { TextInput, Accordion, } + export * from './Text/Text' diff --git a/src/helpers/colors.ts b/src/helpers/colors.ts index 06bb1f32..b53d1a31 100644 --- a/src/helpers/colors.ts +++ b/src/helpers/colors.ts @@ -1,17 +1,6 @@ import type {ColorValue} from 'react-native' -export interface IColors { - readonly primary: string - readonly black: string - readonly white: string - readonly gray: string - readonly red: string - readonly textDisabled: string - readonly placeHolderText: string - readonly backgroundDisabled: string -} - -const baseColor: IColors = { +const baseColor = { primary: '#7239E5', black: '#1F1F1F', white: '#ffffff', @@ -20,7 +9,7 @@ const baseColor: IColors = { textDisabled: '#666666', placeHolderText: '#929298', backgroundDisabled: '#e3e6e8', -} +} as const const colors = { ...baseColor, diff --git a/src/theme/base/borderWidths.ts b/src/theme/base/borderWidths.ts index b01b25bb..94ffe103 100644 --- a/src/theme/base/borderWidths.ts +++ b/src/theme/base/borderWidths.ts @@ -19,7 +19,6 @@ const borderWidths = { enormous: 18, mammoth: 19, titanic: 20, -} +} as const -export type IBorderWidth = keyof typeof borderWidths export default borderWidths diff --git a/src/theme/base/colors.ts b/src/theme/base/colors.ts index 2d1030b1..203289a9 100644 --- a/src/theme/base/colors.ts +++ b/src/theme/base/colors.ts @@ -53,7 +53,6 @@ const colors = { darkTextColor: '#333333', placeHolderText: '#929298', errorText: '#ff0009', -} +} as const -export type IColors = keyof typeof colors export default colors diff --git a/src/theme/base/opacity.ts b/src/theme/base/opacity.ts index aec48dc6..d22c9ac1 100644 --- a/src/theme/base/opacity.ts +++ b/src/theme/base/opacity.ts @@ -14,7 +14,6 @@ const opacity = { dense: 0.9, darkened: 0.95, blackened: 1, -} +} as const -export type IOpacity = keyof typeof opacity export default opacity diff --git a/src/theme/base/shadows.ts b/src/theme/base/shadows.ts index fe720ce4..b1014e09 100644 --- a/src/theme/base/shadows.ts +++ b/src/theme/base/shadows.ts @@ -99,7 +99,6 @@ const shadows = { shadowRadius: 6.27, elevation: 10, }, -} +} as const -export type IShadows = keyof typeof shadows export default shadows diff --git a/src/theme/base/sizes.ts b/src/theme/base/sizes.ts index b1a42e9c..282ed84c 100644 --- a/src/theme/base/sizes.ts +++ b/src/theme/base/sizes.ts @@ -19,7 +19,6 @@ const sizes = { enormous: 224, mammoth: 256, titanic: 288, -} +} as const -export type ISize = keyof typeof sizes export default sizes diff --git a/src/theme/base/spacing.ts b/src/theme/base/spacing.ts index 456d3637..d863f6b8 100644 --- a/src/theme/base/spacing.ts +++ b/src/theme/base/spacing.ts @@ -20,7 +20,6 @@ const spacing = { mammoth: 38, titanic: 40, gigantic: 48, -} +} as const -export type ISpacing = keyof typeof spacing export default spacing diff --git a/src/theme/components/Checkbox.ts b/src/theme/components/Checkbox.ts index d1e4d135..cea49171 100644 --- a/src/theme/components/Checkbox.ts +++ b/src/theme/components/Checkbox.ts @@ -8,7 +8,7 @@ export type CheckboxThemeProps = Pick< > export const CheckboxTheme: CheckboxThemeProps = { - size: base.sizes.narrow, + size: metrics.xl, borderRadius: metrics.borderRadius, fillColor: base.colors.primary, unfillColor: base.colors.transparent, diff --git a/src/theme/components/Header.ts b/src/theme/components/Header.ts new file mode 100644 index 00000000..1ce30e74 --- /dev/null +++ b/src/theme/components/Header.ts @@ -0,0 +1,28 @@ +import {metrics, responsiveHeight} from '../../helpers' +import type {HeaderProps} from '../../components/Header/Header' +import base from '../base' + +export type HeaderThemeProps = Pick< + HeaderProps, + | 'paddingTop' + | 'height' + | 'borderBottomWidth' + | 'borderBottomColor' + | 'titleColor' + | 'titleStyle' + | 'backgroundColor' + | 'backIconColor' +> + +export const HeaderTheme: HeaderThemeProps = { + paddingTop: metrics.xxl, + height: metrics.huge, + borderBottomWidth: responsiveHeight(1), + borderBottomColor: base.colors.backgroundSecondary, + titleColor: base.colors.textColor, + titleStyle: { + fontSize: base.fontSizes.sm, + }, + backgroundColor: base.colors.white, + backIconColor: base.colors.black, +} diff --git a/src/theme/components/index.ts b/src/theme/components/index.ts index 94351efa..98a13f08 100644 --- a/src/theme/components/index.ts +++ b/src/theme/components/index.ts @@ -6,6 +6,7 @@ import { ButtonTransparentTheme, } from './Button' import {CheckboxTheme} from './Checkbox' +import {HeaderTheme} from './Header' import {TextTheme} from './Text' export default { @@ -15,5 +16,6 @@ export default { ButtonSecondary: ButtonSecondaryTheme, ButtonTransparent: ButtonTransparentTheme, Checkbox: CheckboxTheme, + Header: HeaderTheme, Text: TextTheme, } diff --git a/src/theme/images.ts b/src/theme/images.ts index e03b2cb8..6704fb8e 100644 --- a/src/theme/images.ts +++ b/src/theme/images.ts @@ -1,5 +1,6 @@ const Images = { check: require('../assets/images/check.png'), -} + chevronLeft: require('../assets/images/chevron-left.png'), +} as const export {Images}