From e5021187e96b78b53203bd95d08d6818aea47d17 Mon Sep 17 00:00:00 2001 From: "Thomas F. K. Jorna" Date: Wed, 14 Jul 2021 15:10:31 +0200 Subject: New Ignite 7.0.6 app --- app/components/auto-image/auto-image.story.tsx | 31 +++++ app/components/auto-image/auto-image.tsx | 46 +++++++ app/components/bullet-item/bullet-item.tsx | 41 +++++++ app/components/button/button.presets.ts | 54 +++++++++ app/components/button/button.props.ts | 35 ++++++ app/components/button/button.story.tsx | 33 +++++ app/components/button/button.tsx | 36 ++++++ app/components/checkbox/checkbox.props.ts | 44 +++++++ app/components/checkbox/checkbox.story.tsx | 121 +++++++++++++++++++ app/components/checkbox/checkbox.tsx | 53 +++++++++ app/components/form-row/form-row.presets.ts | 71 +++++++++++ app/components/form-row/form-row.props.tsx | 23 ++++ app/components/form-row/form-row.story.tsx | 107 +++++++++++++++++ app/components/form-row/form-row.tsx | 13 ++ app/components/header/header.props.ts | 45 +++++++ app/components/header/header.story.tsx | 43 +++++++ app/components/header/header.tsx | 61 ++++++++++ app/components/icon/icon.props.ts | 21 ++++ app/components/icon/icon.story.tsx | 19 +++ app/components/icon/icon.tsx | 19 +++ app/components/icon/icons/arrow-left.png | Bin 0 -> 329 bytes app/components/icon/icons/arrow-left@2x.png | Bin 0 -> 329 bytes app/components/icon/icons/bullet.png | Bin 0 -> 204 bytes app/components/icon/icons/bullet@2x.png | Bin 0 -> 204 bytes app/components/icon/icons/index.ts | 6 + app/components/index.ts | 12 ++ app/components/screen/screen.presets.ts | 66 ++++++++++ app/components/screen/screen.props.ts | 46 +++++++ app/components/screen/screen.tsx | 66 ++++++++++ app/components/switch/switch.props.ts | 39 ++++++ app/components/switch/switch.story.tsx | 110 +++++++++++++++++ app/components/switch/switch.tsx | 114 ++++++++++++++++++ app/components/text-field/text-field.story.tsx | 159 +++++++++++++++++++++++++ app/components/text-field/text-field.tsx | 98 +++++++++++++++ app/components/text/text.presets.ts | 48 ++++++++ app/components/text/text.props.ts | 37 ++++++ app/components/text/text.story.tsx | 92 ++++++++++++++ app/components/text/text.tsx | 28 +++++ app/components/wallpaper/bg.png | Bin 0 -> 56176 bytes app/components/wallpaper/bg@2x.png | Bin 0 -> 203224 bytes app/components/wallpaper/wallpaper.presets.ts | 34 ++++++ app/components/wallpaper/wallpaper.props.ts | 19 +++ app/components/wallpaper/wallpaper.story.tsx | 16 +++ app/components/wallpaper/wallpaper.tsx | 25 ++++ 44 files changed, 1931 insertions(+) create mode 100644 app/components/auto-image/auto-image.story.tsx create mode 100644 app/components/auto-image/auto-image.tsx create mode 100644 app/components/bullet-item/bullet-item.tsx create mode 100644 app/components/button/button.presets.ts create mode 100644 app/components/button/button.props.ts create mode 100644 app/components/button/button.story.tsx create mode 100644 app/components/button/button.tsx create mode 100644 app/components/checkbox/checkbox.props.ts create mode 100644 app/components/checkbox/checkbox.story.tsx create mode 100644 app/components/checkbox/checkbox.tsx create mode 100644 app/components/form-row/form-row.presets.ts create mode 100644 app/components/form-row/form-row.props.tsx create mode 100644 app/components/form-row/form-row.story.tsx create mode 100644 app/components/form-row/form-row.tsx create mode 100644 app/components/header/header.props.ts create mode 100644 app/components/header/header.story.tsx create mode 100644 app/components/header/header.tsx create mode 100644 app/components/icon/icon.props.ts create mode 100644 app/components/icon/icon.story.tsx create mode 100644 app/components/icon/icon.tsx create mode 100644 app/components/icon/icons/arrow-left.png create mode 100644 app/components/icon/icons/arrow-left@2x.png create mode 100644 app/components/icon/icons/bullet.png create mode 100644 app/components/icon/icons/bullet@2x.png create mode 100644 app/components/icon/icons/index.ts create mode 100644 app/components/index.ts create mode 100644 app/components/screen/screen.presets.ts create mode 100644 app/components/screen/screen.props.ts create mode 100644 app/components/screen/screen.tsx create mode 100644 app/components/switch/switch.props.ts create mode 100644 app/components/switch/switch.story.tsx create mode 100644 app/components/switch/switch.tsx create mode 100644 app/components/text-field/text-field.story.tsx create mode 100644 app/components/text-field/text-field.tsx create mode 100644 app/components/text/text.presets.ts create mode 100644 app/components/text/text.props.ts create mode 100644 app/components/text/text.story.tsx create mode 100644 app/components/text/text.tsx create mode 100644 app/components/wallpaper/bg.png create mode 100644 app/components/wallpaper/bg@2x.png create mode 100644 app/components/wallpaper/wallpaper.presets.ts create mode 100644 app/components/wallpaper/wallpaper.props.ts create mode 100644 app/components/wallpaper/wallpaper.story.tsx create mode 100644 app/components/wallpaper/wallpaper.tsx (limited to 'app/components') diff --git a/app/components/auto-image/auto-image.story.tsx b/app/components/auto-image/auto-image.story.tsx new file mode 100644 index 0000000..f7ecc86 --- /dev/null +++ b/app/components/auto-image/auto-image.story.tsx @@ -0,0 +1,31 @@ +/* eslint-disable */ +import * as React from "react" +import { storiesOf } from "@storybook/react-native" +import { StoryScreen, Story, UseCase } from "../../../storybook/views" +import { AutoImage } from "./auto-image" + +declare let module + +const bowser = require("../../screens/welcome/bowser.png") +const morty = { uri: "https://rickandmortyapi.com/api/character/avatar/2.jpeg" } + +storiesOf("AutoImage", module) + .addDecorator((fn) => {fn()}) + .add("Style Presets", () => ( + + + + + + + + + + + + + + + + + )) diff --git a/app/components/auto-image/auto-image.tsx b/app/components/auto-image/auto-image.tsx new file mode 100644 index 0000000..39d71ca --- /dev/null +++ b/app/components/auto-image/auto-image.tsx @@ -0,0 +1,46 @@ +import React, { useLayoutEffect, useState } from "react" +import { + Image as RNImage, + ImageProps as DefaultImageProps, + ImageURISource, + Platform, +} from "react-native" + +type ImageProps = DefaultImageProps & { + source: ImageURISource +} + +/** + * An Image wrapper component that autosizes itself to the size of the actual image. + * You can always override by passing a width and height in the style. + * If passing only one of width/height this image component will use the actual + * size of the other dimension. + * + * This component isn't required, but is provided as a convenience so that + * we don't have to remember to explicitly set image sizes on every image instance. + * + * To use as a stand-in replacement import { AutoImage as Image } and remove the + * Image import from react-native. Now all images in that file are handled by this + * component and are web-ready if not explicitly sized in the style property. + */ +export function AutoImage(props: ImageProps) { + const [imageSize, setImageSize] = useState({ width: 0, height: 0 }) + + useLayoutEffect(() => { + if (props.source?.uri) { + RNImage.getSize(props.source.uri as any, (width, height) => { + setImageSize({ width, height }) + }) + } else if (Platform.OS === "web") { + // web requires a different method to get it's size + RNImage.getSize(props.source as any, (width, height) => { + setImageSize({ width, height }) + }) + } else { + const { width, height } = RNImage.resolveAssetSource(props.source) + setImageSize({ width, height }) + } + }, []) + + return +} diff --git a/app/components/bullet-item/bullet-item.tsx b/app/components/bullet-item/bullet-item.tsx new file mode 100644 index 0000000..d999e10 --- /dev/null +++ b/app/components/bullet-item/bullet-item.tsx @@ -0,0 +1,41 @@ +import * as React from "react" +import { View, ViewStyle, ImageStyle, TextStyle } from "react-native" +import { Text } from "../text/text" +import { Icon } from "../icon/icon" +import { spacing, typography } from "../../theme" + +const BULLET_ITEM: ViewStyle = { + flexDirection: "row", + marginTop: spacing[4], + paddingBottom: spacing[4], + borderBottomWidth: 1, + borderBottomColor: "#3A3048", +} +const BULLET_CONTAINER: ViewStyle = { + marginRight: spacing[4] - 1, + marginTop: spacing[2], +} +const BULLET: ImageStyle = { + width: 8, + height: 8, +} +const BULLET_TEXT: TextStyle = { + flex: 1, + fontFamily: typography.primary, + color: "#BAB6C8", + fontSize: 15, + lineHeight: 22, +} + +export interface BulletItemProps { + text: string +} + +export function BulletItem(props: BulletItemProps) { + return ( + + + + + ) +} diff --git a/app/components/button/button.presets.ts b/app/components/button/button.presets.ts new file mode 100644 index 0000000..b140fd2 --- /dev/null +++ b/app/components/button/button.presets.ts @@ -0,0 +1,54 @@ +import { ViewStyle, TextStyle } from "react-native" +import { color, spacing } from "../../theme" + +/** + * All text will start off looking like this. + */ +const BASE_VIEW: ViewStyle = { + paddingVertical: spacing[2], + paddingHorizontal: spacing[2], + borderRadius: 4, + justifyContent: "center", + alignItems: "center", +} + +const BASE_TEXT: TextStyle = { + paddingHorizontal: spacing[3], +} + +/** + * All the variations of text styling within the app. + * + * You want to customize these to whatever you need in your app. + */ +export const viewPresets: Record = { + /** + * A smaller piece of secondard information. + */ + primary: { ...BASE_VIEW, backgroundColor: color.palette.orange } as ViewStyle, + + /** + * A button without extras. + */ + link: { + ...BASE_VIEW, + paddingHorizontal: 0, + paddingVertical: 0, + alignItems: "flex-start", + } as ViewStyle, +} + +export const textPresets: Record = { + primary: { ...BASE_TEXT, fontSize: 9, color: color.palette.white } as TextStyle, + link: { + ...BASE_TEXT, + color: color.text, + paddingHorizontal: 0, + paddingVertical: 0, + } as TextStyle, +} + +/** + * A list of preset names. + */ +export type ButtonPresetNames = keyof typeof viewPresets diff --git a/app/components/button/button.props.ts b/app/components/button/button.props.ts new file mode 100644 index 0000000..1377a7e --- /dev/null +++ b/app/components/button/button.props.ts @@ -0,0 +1,35 @@ +import { StyleProp, TextStyle, TouchableOpacityProps, ViewStyle } from "react-native" +import { ButtonPresetNames } from "./button.presets" +import { TxKeyPath } from "../../i18n" + +export interface ButtonProps extends TouchableOpacityProps { + /** + * Text which is looked up via i18n. + */ + tx?: TxKeyPath + + /** + * The text to display if not using `tx` or nested components. + */ + text?: string + + /** + * An optional style override useful for padding & margin. + */ + style?: StyleProp + + /** + * An optional style override useful for the button text. + */ + textStyle?: StyleProp + + /** + * One of the different types of text presets. + */ + preset?: ButtonPresetNames + + /** + * One of the different types of text presets. + */ + children?: React.ReactNode +} diff --git a/app/components/button/button.story.tsx b/app/components/button/button.story.tsx new file mode 100644 index 0000000..4861772 --- /dev/null +++ b/app/components/button/button.story.tsx @@ -0,0 +1,33 @@ +import * as React from "react" +import { ViewStyle, TextStyle, Alert } from "react-native" +import { storiesOf } from "@storybook/react-native" +import { StoryScreen, Story, UseCase } from "../../../storybook/views" +import { Button } from "./button" + +declare let module + +const buttonStyleArray: ViewStyle[] = [{ paddingVertical: 100 }, { borderRadius: 0 }] + +const buttonTextStyleArray: TextStyle[] = [{ fontSize: 20 }, { color: "#a511dc" }] + +storiesOf("Button", module) + .addDecorator((fn) => {fn()}) + .add("Style Presets", () => ( + + + + ) : ( + + )} + + + + {rightIcon ? ( + + ) : ( + + )} + + ) +} diff --git a/app/components/icon/icon.props.ts b/app/components/icon/icon.props.ts new file mode 100644 index 0000000..71ce0b7 --- /dev/null +++ b/app/components/icon/icon.props.ts @@ -0,0 +1,21 @@ +import { ImageStyle, StyleProp, ViewStyle } from "react-native" +import { IconTypes } from "./icons" + +export interface IconProps { + /** + * Style overrides for the icon image + */ + style?: StyleProp + + /** + * Style overrides for the icon container + */ + + containerStyle?: StyleProp + + /** + * The name of the icon + */ + + icon?: IconTypes +} diff --git a/app/components/icon/icon.story.tsx b/app/components/icon/icon.story.tsx new file mode 100644 index 0000000..d119ed4 --- /dev/null +++ b/app/components/icon/icon.story.tsx @@ -0,0 +1,19 @@ +import * as React from "react" +import { storiesOf } from "@storybook/react-native" +import { StoryScreen, Story, UseCase } from "../../../storybook/views" +import { Icon } from "./icon" + +declare let module + +storiesOf("Icon", module) + .addDecorator((fn) => {fn()}) + .add("Names", () => ( + + + + + + + + + )) diff --git a/app/components/icon/icon.tsx b/app/components/icon/icon.tsx new file mode 100644 index 0000000..4735e13 --- /dev/null +++ b/app/components/icon/icon.tsx @@ -0,0 +1,19 @@ +import * as React from "react" +import { View, ImageStyle } from "react-native" +import { AutoImage as Image } from "../auto-image/auto-image" +import { IconProps } from "./icon.props" +import { icons } from "./icons" + +const ROOT: ImageStyle = { + resizeMode: "contain", +} + +export function Icon(props: IconProps) { + const { style: styleOverride, icon, containerStyle } = props + + return ( + + + + ) +} diff --git a/app/components/icon/icons/arrow-left.png b/app/components/icon/icons/arrow-left.png new file mode 100644 index 0000000..9d607d7 Binary files /dev/null and b/app/components/icon/icons/arrow-left.png differ diff --git a/app/components/icon/icons/arrow-left@2x.png b/app/components/icon/icons/arrow-left@2x.png new file mode 100644 index 0000000..9d607d7 Binary files /dev/null and b/app/components/icon/icons/arrow-left@2x.png differ diff --git a/app/components/icon/icons/bullet.png b/app/components/icon/icons/bullet.png new file mode 100644 index 0000000..8fc256f Binary files /dev/null and b/app/components/icon/icons/bullet.png differ diff --git a/app/components/icon/icons/bullet@2x.png b/app/components/icon/icons/bullet@2x.png new file mode 100644 index 0000000..8fc256f Binary files /dev/null and b/app/components/icon/icons/bullet@2x.png differ diff --git a/app/components/icon/icons/index.ts b/app/components/icon/icons/index.ts new file mode 100644 index 0000000..00e8a59 --- /dev/null +++ b/app/components/icon/icons/index.ts @@ -0,0 +1,6 @@ +export const icons = { + back: require("./arrow-left.png"), + bullet: require("./bullet.png"), +} + +export type IconTypes = keyof typeof icons diff --git a/app/components/index.ts b/app/components/index.ts new file mode 100644 index 0000000..5ed4a28 --- /dev/null +++ b/app/components/index.ts @@ -0,0 +1,12 @@ +export * from "./bullet-item/bullet-item" +export * from "./button/button" +export * from "./checkbox/checkbox" +export * from "./form-row/form-row" +export * from "./header/header" +export * from "./icon/icon" +export * from "./screen/screen" +export * from "./switch/switch" +export * from "./text/text" +export * from "./text-field/text-field" +export * from "./wallpaper/wallpaper" +export * from "./auto-image/auto-image" diff --git a/app/components/screen/screen.presets.ts b/app/components/screen/screen.presets.ts new file mode 100644 index 0000000..a016b77 --- /dev/null +++ b/app/components/screen/screen.presets.ts @@ -0,0 +1,66 @@ +import { ViewStyle } from "react-native" +import { color } from "../../theme" + +/** + * All screen keyboard offsets. + */ +export const offsets = { + none: 0, +} + +/** + * The variations of keyboard offsets. + */ +export type KeyboardOffsets = keyof typeof offsets + +/** + * All the variations of screens. + */ +export const presets = { + /** + * No scrolling. Suitable for full-screen carousels and components + * which have built-in scrolling like FlatList. + */ + fixed: { + outer: { + backgroundColor: color.background, + flex: 1, + height: "100%", + } as ViewStyle, + inner: { + justifyContent: "flex-start", + alignItems: "stretch", + height: "100%", + width: "100%", + } as ViewStyle, + }, + + /** + * Scrolls. Suitable for forms or other things requiring a keyboard. + * + * Pick this one if you don't know which one you want yet. + */ + scroll: { + outer: { + backgroundColor: color.background, + flex: 1, + height: "100%", + } as ViewStyle, + inner: { justifyContent: "flex-start", alignItems: "stretch" } as ViewStyle, + }, +} + +/** + * The variations of screens. + */ +export type ScreenPresets = keyof typeof presets + +/** + * Is this preset a non-scrolling one? + * + * @param preset The preset to check + */ +export function isNonScrolling(preset?: ScreenPresets) { + // any of these things will make you scroll + return !preset || !presets[preset] || preset === "fixed" +} diff --git a/app/components/screen/screen.props.ts b/app/components/screen/screen.props.ts new file mode 100644 index 0000000..0326fd7 --- /dev/null +++ b/app/components/screen/screen.props.ts @@ -0,0 +1,46 @@ +import React from "react" +import { StyleProp, ViewStyle } from "react-native" +import { KeyboardOffsets, ScreenPresets } from "./screen.presets" + +export interface ScreenProps { + /** + * Children components. + */ + children?: React.ReactNode + + /** + * An optional style override useful for padding & margin. + */ + style?: StyleProp + + /** + * One of the different types of presets. + */ + preset?: ScreenPresets + + /** + * An optional background color + */ + backgroundColor?: string + + /** + * An optional status bar setting. Defaults to light-content. + */ + statusBar?: "light-content" | "dark-content" + + /** + * Should we not wrap in SafeAreaView? Defaults to false. + */ + unsafe?: boolean + + /** + * By how much should we offset the keyboard? Defaults to none. + */ + keyboardOffset?: KeyboardOffsets + + /** + * Should keyboard persist on screen tap. Defaults to handled. + * Only applies to scroll preset. + */ + keyboardShouldPersistTaps?: "handled" | "always" | "never" +} diff --git a/app/components/screen/screen.tsx b/app/components/screen/screen.tsx new file mode 100644 index 0000000..ba84547 --- /dev/null +++ b/app/components/screen/screen.tsx @@ -0,0 +1,66 @@ +import * as React from "react" +import { KeyboardAvoidingView, Platform, ScrollView, StatusBar, View } from "react-native" +import { useSafeAreaInsets } from "react-native-safe-area-context" +import { ScreenProps } from "./screen.props" +import { isNonScrolling, offsets, presets } from "./screen.presets" + +const isIos = Platform.OS === "ios" + +function ScreenWithoutScrolling(props: ScreenProps) { + const insets = useSafeAreaInsets() + const preset = presets.fixed + const style = props.style || {} + const backgroundStyle = props.backgroundColor ? { backgroundColor: props.backgroundColor } : {} + const insetStyle = { paddingTop: props.unsafe ? 0 : insets.top } + + return ( + + + {props.children} + + ) +} + +function ScreenWithScrolling(props: ScreenProps) { + const insets = useSafeAreaInsets() + const preset = presets.scroll + const style = props.style || {} + const backgroundStyle = props.backgroundColor ? { backgroundColor: props.backgroundColor } : {} + const insetStyle = { paddingTop: props.unsafe ? 0 : insets.top } + + return ( + + + + + {props.children} + + + + ) +} + +/** + * The starting component on every screen in the app. + * + * @param props The screen props + */ +export function Screen(props: ScreenProps) { + if (isNonScrolling(props.preset)) { + return + } else { + return + } +} diff --git a/app/components/switch/switch.props.ts b/app/components/switch/switch.props.ts new file mode 100644 index 0000000..8235457 --- /dev/null +++ b/app/components/switch/switch.props.ts @@ -0,0 +1,39 @@ +import { StyleProp, ViewStyle } from "react-native" + +export interface SwitchProps { + /** + * On or off. + */ + value?: boolean + /** + * Fires when the on/off switch triggers. + * + * @param newValue The new value we're switching to. + */ + onToggle?: (newValue: boolean) => void + + /** + * A style override to apply to the container. Useful for margins and paddings. + */ + style?: StyleProp + + /** + * Additional track styling when on. + */ + trackOnStyle?: StyleProp + + /** + * Additional track styling when off. + */ + trackOffStyle?: StyleProp + + /** + * Additional thumb styling when on. + */ + thumbOnStyle?: StyleProp + + /** + * Additional thumb styling when off. + */ + thumbOffStyle?: StyleProp +} diff --git a/app/components/switch/switch.story.tsx b/app/components/switch/switch.story.tsx new file mode 100644 index 0000000..998d1df --- /dev/null +++ b/app/components/switch/switch.story.tsx @@ -0,0 +1,110 @@ +/* eslint-disable react-native/no-inline-styles */ +/* eslint-disable react-native/no-color-literals */ + +import * as React from "react" +import { View, ViewStyle } from "react-native" +import { storiesOf } from "@storybook/react-native" +import { StoryScreen, Story, UseCase } from "../../../storybook/views" +import { Toggle } from "react-powerplug" +import { Switch } from "./switch" + +declare let module + +const styleArray: ViewStyle[] = [{ borderColor: "#686868" }] + +const trackOffStyle: ViewStyle[] = [ + { backgroundColor: "#686868" }, + { + height: 80, + borderRadius: 0, + }, +] +const trackOnStyle: ViewStyle[] = [ + { + backgroundColor: "#b1008e", + borderColor: "#686868", + }, + { + height: 80, + borderRadius: 0, + }, +] +const thumbOffStyle: ViewStyle[] = [ + { + backgroundColor: "#b1008e", + borderColor: "#686868", + }, + { + height: 80, + borderRadius: 0, + }, +] +const thumbOnStyle: ViewStyle[] = [ + { backgroundColor: "#f0c" }, + { + height: 80, + borderRadius: 0, + borderColor: "#686868", + }, +] + +storiesOf("Switch", module) + .addDecorator((fn) => {fn()}) + .add("Behaviour", () => ( + + + + {({ on, toggle }) => } + + + + + + + + + + )) + .add("Styling", () => ( + + + + {({ on, toggle }) => ( + + + + )} + + + + + + {({ on, toggle }) => ( + + + + )} + + + + )) diff --git a/app/components/switch/switch.tsx b/app/components/switch/switch.tsx new file mode 100644 index 0000000..0813747 --- /dev/null +++ b/app/components/switch/switch.tsx @@ -0,0 +1,114 @@ +import React from "react" +import { ViewStyle, Animated, Easing, TouchableWithoutFeedback } from "react-native" +import { color } from "../../theme" +import { SwitchProps } from "./switch.props" + +// dimensions +const THUMB_SIZE = 30 +const WIDTH = 56 +const MARGIN = 2 +const OFF_POSITION = -0.5 +const ON_POSITION = WIDTH - THUMB_SIZE - MARGIN +const BORDER_RADIUS = (THUMB_SIZE * 3) / 4 + +// colors +const ON_COLOR = color.primary +const OFF_COLOR = color.palette.offWhite +const BORDER_ON_COLOR = ON_COLOR +const BORDER_OFF_COLOR = "rgba(0, 0, 0, 0.1)" + +// animation +const DURATION = 250 + +// the track always has these props +const TRACK = { + height: THUMB_SIZE + MARGIN, + width: WIDTH, + borderRadius: BORDER_RADIUS, + borderWidth: MARGIN / 2, + backgroundColor: color.background, +} + +// the thumb always has these props +const THUMB: ViewStyle = { + position: "absolute", + width: THUMB_SIZE, + height: THUMB_SIZE, + borderColor: BORDER_OFF_COLOR, + borderRadius: THUMB_SIZE / 2, + borderWidth: MARGIN / 2, + backgroundColor: color.background, + shadowColor: BORDER_OFF_COLOR, + shadowOffset: { width: 1, height: 2 }, + shadowOpacity: 1, + shadowRadius: 2, + elevation: 2, +} + +const makeAnimatedValue = (switchOn) => new Animated.Value(switchOn ? 1 : 0) + +export function Switch(props: SwitchProps) { + const [timer] = React.useState(makeAnimatedValue(props.value)) + const startAnimation = React.useMemo( + () => (newValue: boolean) => { + const toValue = newValue ? 1 : 0 + const easing = Easing.out(Easing.circle) + Animated.timing(timer, { + toValue, + duration: DURATION, + easing, + useNativeDriver: true, + }).start() + }, + [timer], + ) + + const [previousValue, setPreviousValue] = React.useState(props.value) + React.useEffect(() => { + if (props.value !== previousValue) { + startAnimation(props.value) + setPreviousValue(props.value) + } + }, [props.value]) + + const handlePress = React.useMemo(() => () => props.onToggle && props.onToggle(!props.value), [ + props.onToggle, + props.value, + ]) + + if (!timer) { + return null + } + + const translateX = timer.interpolate({ + inputRange: [0, 1], + outputRange: [OFF_POSITION, ON_POSITION], + }) + + const style = props.style + + const trackStyle = [ + TRACK, + { + backgroundColor: props.value ? ON_COLOR : OFF_COLOR, + borderColor: props.value ? BORDER_ON_COLOR : BORDER_OFF_COLOR, + }, + props.value ? props.trackOnStyle : props.trackOffStyle, + ] + + const thumbStyle = [ + THUMB, + { + transform: [{ translateX }], + }, + props.value ? props.thumbOnStyle : props.thumbOffStyle, + ] + + return ( + + + + + + ) +} diff --git a/app/components/text-field/text-field.story.tsx b/app/components/text-field/text-field.story.tsx new file mode 100644 index 0000000..74a4da0 --- /dev/null +++ b/app/components/text-field/text-field.story.tsx @@ -0,0 +1,159 @@ +/* eslint-disable react-native/no-inline-styles */ +/* eslint-disable react-native/no-color-literals */ + +import * as React from "react" +import { storiesOf } from "@storybook/react-native" +import { StoryScreen, Story, UseCase } from "../../../storybook/views" +import { Text, TextField } from "../" +import { State } from "react-powerplug" +import { ViewStyle, TextStyle, Alert } from "react-native" + +declare let module + +const styleArray: ViewStyle[] = [{ paddingHorizontal: 30 }, { borderWidth: 30 }] + +const inputStyleArray: TextStyle[] = [ + { + backgroundColor: "rebeccapurple", + color: "white", + padding: 40, + }, + { + borderWidth: 10, + borderRadius: 4, + borderColor: "#7fff00", + }, +] +let alertWhenFocused = true + +storiesOf("TextField", module) + .addDecorator((fn) => {fn()}) + .add("Labelling", () => ( + + + + {({ state, setState }) => ( + setState({ value })} + value={state.value} + label="Name" + placeholder="omg your name" + /> + )} + + + + + + {({ state, setState }) => ( + setState({ value })} + value={state.value} + placeholderTx="storybook.placeholder" + labelTx="storybook.field" + /> + )} + + + + )) + .add("Style Overrides", () => ( + + + + {({ state, setState }) => ( + setState({ value })} + value={state.value} + label="First Name" + style={{ paddingTop: 0, paddingHorizontal: 40 }} + /> + )} + + + {({ state, setState }) => ( + setState({ value })} + value={state.value} + label="Last Name" + style={{ paddingBottom: 0 }} + /> + )} + + + + + {({ state, setState }) => ( + setState({ value })} + value={state.value} + label="Name" + inputStyle={{ + backgroundColor: "rebeccapurple", + color: "white", + padding: 40, + borderWidth: 10, + borderRadius: 4, + borderColor: "hotpink", + }} + /> + )} + + + + + + + {({ state, setState }) => ( + setState({ value })} + value={state.value} + label="Name" + style={styleArray} + inputStyle={inputStyleArray} + /> + )} + + + + + )) + .add("Ref Forwarding", () => ( + + + + {({ state, setState }) => ( + setState({ value })} + value={state.value} + label="Name" + inputStyle={{ + backgroundColor: "rebeccapurple", + color: "white", + padding: 40, + borderWidth: 10, + borderRadius: 4, + borderColor: "hotpink", + }} + forwardedRef={(ref) => ref} + onFocus={() => { + if (alertWhenFocused) { + // Prevent text field focus from being repeatedly triggering alert + alertWhenFocused = false + Alert.alert("Text field focuesed with forwarded ref!") + } + }} + /> + )} + + + + + )) diff --git a/app/components/text-field/text-field.tsx b/app/components/text-field/text-field.tsx new file mode 100644 index 0000000..eea1a70 --- /dev/null +++ b/app/components/text-field/text-field.tsx @@ -0,0 +1,98 @@ +import React from "react" +import { StyleProp, TextInput, TextInputProps, TextStyle, View, ViewStyle } from "react-native" +import { color, spacing, typography } from "../../theme" +import { translate, TxKeyPath } from "../../i18n" +import { Text } from "../text/text" + +// the base styling for the container +const CONTAINER: ViewStyle = { + paddingVertical: spacing[3], +} + +// the base styling for the TextInput +const INPUT: TextStyle = { + fontFamily: typography.primary, + color: color.text, + minHeight: 44, + fontSize: 18, + backgroundColor: color.palette.white, +} + +// currently we have no presets, but that changes quickly when you build your app. +const PRESETS: { [name: string]: ViewStyle } = { + default: {}, +} + +export interface TextFieldProps extends TextInputProps { + /** + * The placeholder i18n key. + */ + placeholderTx?: TxKeyPath + + /** + * The Placeholder text if no placeholderTx is provided. + */ + placeholder?: string + + /** + * The label i18n key. + */ + labelTx?: TxKeyPath + + /** + * The label text if no labelTx is provided. + */ + label?: string + + /** + * Optional container style overrides useful for margins & padding. + */ + style?: StyleProp + + /** + * Optional style overrides for the input. + */ + inputStyle?: StyleProp + + /** + * Various look & feels. + */ + preset?: keyof typeof PRESETS + + forwardedRef?: any +} + +/** + * A component which has a label and an input together. + */ +export function TextField(props: TextFieldProps) { + const { + placeholderTx, + placeholder, + labelTx, + label, + preset = "default", + style: styleOverride, + inputStyle: inputStyleOverride, + forwardedRef, + ...rest + } = props + + const containerStyles = [CONTAINER, PRESETS[preset], styleOverride] + const inputStyles = [INPUT, inputStyleOverride] + const actualPlaceholder = placeholderTx ? translate(placeholderTx) : placeholder + + return ( + + + + + ) +} diff --git a/app/components/text/text.presets.ts b/app/components/text/text.presets.ts new file mode 100644 index 0000000..9622268 --- /dev/null +++ b/app/components/text/text.presets.ts @@ -0,0 +1,48 @@ +import { TextStyle } from "react-native" +import { color, typography } from "../../theme" + +/** + * All text will start off looking like this. + */ +const BASE: TextStyle = { + fontFamily: typography.primary, + color: color.text, + fontSize: 15, +} + +/** + * All the variations of text styling within the app. + * + * You want to customize these to whatever you need in your app. + */ +export const presets = { + /** + * The default text styles. + */ + default: BASE, + + /** + * A bold version of the default text. + */ + bold: { ...BASE, fontWeight: "bold" } as TextStyle, + + /** + * Large headers. + */ + header: { ...BASE, fontSize: 24, fontWeight: "bold" } as TextStyle, + + /** + * Field labels that appear on forms above the inputs. + */ + fieldLabel: { ...BASE, fontSize: 13, color: color.dim } as TextStyle, + + /** + * A smaller piece of secondard information. + */ + secondary: { ...BASE, fontSize: 9, color: color.dim } as TextStyle, +} + +/** + * A list of preset names. + */ +export type TextPresets = keyof typeof presets diff --git a/app/components/text/text.props.ts b/app/components/text/text.props.ts new file mode 100644 index 0000000..d2c55dc --- /dev/null +++ b/app/components/text/text.props.ts @@ -0,0 +1,37 @@ +import { StyleProp, TextProps as TextProperties, TextStyle } from "react-native" +import i18n from "i18n-js" +import { TextPresets } from "./text.presets" +import { TxKeyPath } from "../../i18n" + +export interface TextProps extends TextProperties { + /** + * Children components. + */ + children?: React.ReactNode + + /** + * Text which is looked up via i18n. + */ + tx?: TxKeyPath + + /** + * Optional options to pass to i18n. Useful for interpolation + * as well as explicitly setting locale or translation fallbacks. + */ + txOptions?: i18n.TranslateOptions + + /** + * The text to display if not using `tx` or nested components. + */ + text?: string + + /** + * An optional style override useful for padding & margin. + */ + style?: StyleProp + + /** + * One of the different types of text presets. + */ + preset?: TextPresets +} diff --git a/app/components/text/text.story.tsx b/app/components/text/text.story.tsx new file mode 100644 index 0000000..5582c1b --- /dev/null +++ b/app/components/text/text.story.tsx @@ -0,0 +1,92 @@ +/* eslint-disable react-native/no-inline-styles */ +/* eslint-disable react-native/no-color-literals */ + +import * as React from "react" +import { View, ViewStyle } from "react-native" +import { storiesOf } from "@storybook/react-native" +import { StoryScreen, Story, UseCase } from "../../../storybook/views" +import { color } from "../../theme" +import { Text } from "./text" + +declare let module + +const VIEWSTYLE = { + flex: 1, + backgroundColor: color.storybookDarkBg, +} +const viewStyleArray: ViewStyle[] = [VIEWSTYLE, { backgroundColor: "#7fff00" }] + +storiesOf("Text", module) + .addDecorator((fn) => {fn()}) + .add("Style Presets", () => ( + + + + Hello! + + Check out{"\n"} + my{"\n"} + line height + + The quick brown fox jumped over the slow lazy dog. + $123,456,789.00 + + + + + Osnap! I'm puffy. + + + + + Behold! + + + + )) + .add("Passing Content", () => ( + + + + + + + + + + + + + + + Passing strings as children. + + + + + + {" "} + Hello bolded World. + + + + + )) + .add("Styling", () => ( + + + + + {" "} + Hello bolded World. + + + + + )) diff --git a/app/components/text/text.tsx b/app/components/text/text.tsx new file mode 100644 index 0000000..3ea613b --- /dev/null +++ b/app/components/text/text.tsx @@ -0,0 +1,28 @@ +import * as React from "react" +import { Text as ReactNativeText } from "react-native" +import { presets } from "./text.presets" +import { TextProps } from "./text.props" +import { translate } from "../../i18n" + +/** + * For your text displaying needs. + * + * This component is a HOC over the built-in React Native one. + */ +export function Text(props: TextProps) { + // grab the props + const { preset = "default", tx, txOptions, text, children, style: styleOverride, ...rest } = props + + // figure out which content to use + const i18nText = tx && translate(tx, txOptions) + const content = i18nText || text || children + + const style = presets[preset] || presets.default + const styles = [style, styleOverride] + + return ( + + {content} + + ) +} diff --git a/app/components/wallpaper/bg.png b/app/components/wallpaper/bg.png new file mode 100644 index 0000000..641838e Binary files /dev/null and b/app/components/wallpaper/bg.png differ diff --git a/app/components/wallpaper/bg@2x.png b/app/components/wallpaper/bg@2x.png new file mode 100644 index 0000000..3ae8396 Binary files /dev/null and b/app/components/wallpaper/bg@2x.png differ diff --git a/app/components/wallpaper/wallpaper.presets.ts b/app/components/wallpaper/wallpaper.presets.ts new file mode 100644 index 0000000..3885b8f --- /dev/null +++ b/app/components/wallpaper/wallpaper.presets.ts @@ -0,0 +1,34 @@ +import { ImageStyle } from "react-native" + +/** + * All wallpaper will start off looking like this. + */ +const BASE: ImageStyle = { + position: "absolute", + top: 0, + left: 0, + bottom: 0, + right: 0, +} + +/** + * All the variations of wallpaper styling within the app. + * + * You want to customize these to whatever you need in your app. + */ +export const presets = { + /** + * The default wallpaper styles. + */ + stretch: { + ...BASE, + resizeMode: "stretch", + width: null, // Have to set these to null because android ¯\_(ツ)_/¯ + height: null, + } as ImageStyle, +} + +/** + * A list of preset names. + */ +export type WallpaperPresets = keyof typeof presets diff --git a/app/components/wallpaper/wallpaper.props.ts b/app/components/wallpaper/wallpaper.props.ts new file mode 100644 index 0000000..592bac9 --- /dev/null +++ b/app/components/wallpaper/wallpaper.props.ts @@ -0,0 +1,19 @@ +import { ImageStyle, StyleProp } from "react-native" +import { WallpaperPresets } from "./wallpaper.presets" + +export interface WallpaperProps { + /** + * An optional style override useful for padding & margin. + */ + style?: StyleProp + + /** + * An optional background image to override the default image. + */ + backgroundImage?: string + + /** + * One of the different types of wallpaper presets. + */ + preset?: WallpaperPresets +} diff --git a/app/components/wallpaper/wallpaper.story.tsx b/app/components/wallpaper/wallpaper.story.tsx new file mode 100644 index 0000000..8f5488a --- /dev/null +++ b/app/components/wallpaper/wallpaper.story.tsx @@ -0,0 +1,16 @@ +import * as React from "react" +import { storiesOf } from "@storybook/react-native" +import { StoryScreen, Story, UseCase } from "../../../storybook/views" +import { Wallpaper } from "./wallpaper" + +declare let module + +storiesOf("Wallpaper", module) + .addDecorator((fn) => {fn()}) + .add("Style Presets", () => ( + + + + + + )) diff --git a/app/components/wallpaper/wallpaper.tsx b/app/components/wallpaper/wallpaper.tsx new file mode 100644 index 0000000..ebba75a --- /dev/null +++ b/app/components/wallpaper/wallpaper.tsx @@ -0,0 +1,25 @@ +import React from "react" +import { AutoImage as Image } from "../auto-image/auto-image" +import { presets } from "./wallpaper.presets" +import { WallpaperProps } from "./wallpaper.props" + +const defaultImage = require("./bg.png") + +/** + * For your text displaying needs. + * + * This component is a HOC over the built-in React Native one. + */ +export function Wallpaper(props: WallpaperProps) { + // grab the props + const { preset = "stretch", style: styleOverride, backgroundImage } = props + + // assemble the style + const presetToUse = presets[preset] || presets.stretch + const styles = [presetToUse, styleOverride] + + // figure out which image to use + const source = backgroundImage || defaultImage + + return +} -- cgit v1.2.3