diff --git a/.github/workflows/build-and-deploy-playroom.yml b/.github/workflows/build-and-deploy-playroom.yml index 7fa2e6d..7fe8c6d 100644 --- a/.github/workflows/build-and-deploy-playroom.yml +++ b/.github/workflows/build-and-deploy-playroom.yml @@ -34,4 +34,4 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./playroom - cname: trampoline.cx + cname: cubes.trampoline.cx diff --git a/README.md b/README.md index eafff49..e11b7f7 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,6 @@ Delightfully simple building blocks for quick prototyping ⚡ [![Storybook](https://raw.githubusercontent.com/storybookjs/brand/master/badge/badge-storybook.svg)](https://storybook.js.org/) - [👨‍🔬 Experiment Now!](#-experiment-now) - - [Playroom](#playroom) - - [CodeSandbox](#codesandbox) - - [Expo Snack](#expo-snack) - [🏃 Getting Started](#-getting-started) - [Usage](#usage) - [Expo](#expo) @@ -25,19 +22,7 @@ Delightfully simple building blocks for quick prototyping ⚡ ## 👨‍🔬 Experiment Now! -Start swiftly ⚡ using [Playroom](https://cubes.trampoline.cx), [CodeSandbox](https://codesandbox.io/s/github/alexbchr/cubes-basic-sandbox) or [Expo Snacks](https://snack.expo.io/@git/github.com/alexbchr/cubes-basic-sandbox). - -### Playroom - -Let you experiment quickly and easily using [Playroom](https://github.com/seek-oss/playroom). You can build prototypes easily and share them with the URL, without hassle or account creation. - -### CodeSandbox - -We recommend you to use CodeSandbox, as previewing is much faster and reliable than Expo Snacks. First time startup may take some time, but testing is fast afterwards. - -### Expo Snack - -Use Expo Snacks if you want to test on Web, Android and iOS. You can even test the native results directly in your browser using embedded [Appetize.io](https://appetize.io/). +Start swiftly ⚡ using [Playroom](https://cubes.trampoline.cx), where you can build prototypes easily and share them with the URL, without hassle or account creation. ## 🏃 Getting Started diff --git a/package.json b/package.json index d26f23d..7813df6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@trampoline/cubes", - "version": "0.1.0", + "version": "0.1.1", "main": "dist/index.js", "scripts": { "lint": "eslint \"src/**/*.{js,ts,jsx,tsx}\" \"storybook/**/*.{ts,tsx}\"", diff --git a/src/components/base/AspectView/AspectView.tsx b/src/components/base/AspectView/AspectView.tsx new file mode 100644 index 0000000..cf87f27 --- /dev/null +++ b/src/components/base/AspectView/AspectView.tsx @@ -0,0 +1,26 @@ +import * as React from 'react' +import { View, StyleSheet, ViewProps } from 'react-native' +import { useOnLayout } from '../../../utils/hooks/use-on-layout' + +export type AspectViewProps = ViewProps + +/** + * Basic View with `aspectRatio` prop working even on React Native Web. + */ +export const AspectView: React.FC = ({ style, ...props }) => { + const [layout, setLayout] = useOnLayout() + + const { aspectRatio = 1, ...inputStyle } = StyleSheet.flatten(style) || {} + const styleProp = [inputStyle, { aspectRatio }] + + if (layout) { + const { width = 0, height = 0 } = layout + if (width === 0) { + styleProp.push({ width: height * aspectRatio }) + } else { + styleProp.push({ height: width * aspectRatio }) + } + } + + return +} diff --git a/src/components/index.ts b/src/components/index.ts index 80e788c..a6e84ac 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -6,6 +6,7 @@ export * from './actions/Link/Link' export * from './actions/Pill/Pill' export * from './actions/actions' +export * from './base/AspectView/AspectView' export * from './base/Touchable/Touchable' export * from './behavior/Carousel/Carousel' diff --git a/src/components/structure/Card/Card.stories.tsx b/src/components/structure/Card/Card.stories.tsx index f782964..0d97913 100644 --- a/src/components/structure/Card/Card.stories.tsx +++ b/src/components/structure/Card/Card.stories.tsx @@ -26,6 +26,7 @@ DefaultSectioned.args = { title: 'Card title', sectioned: true, headerAction: { label: 'Header Action', action: action('Header Action Clicked') }, + imageSource: { uri: require('../../../../storybook/assets/images/puppy-1.jpg') }, mainActions: [ { label: 'Action 1', action: action('Action 1 Clicked') }, { label: 'Action 2', action: action('Action 2 Clicked') }, diff --git a/src/components/structure/Card/Card.tsx b/src/components/structure/Card/Card.tsx index dba1fe5..7d35d84 100644 --- a/src/components/structure/Card/Card.tsx +++ b/src/components/structure/Card/Card.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useState } from 'react' -import { Animated } from 'react-native' +import { Animated, Image, ImageSourcePropType, StyleSheet } from 'react-native' import { Heading } from '../../text/Heading/Heading' import { useStyles, useTheme } from '../../../theme' import { Link } from '../../actions/Link/Link' @@ -12,6 +12,7 @@ import { BodyText } from '../../text/BodyText/BodyText' import { shameStyles } from '../../../theme/shame-styles' import { TextAction } from '../../actions/actions' import { SwipeToDismiss, SwipeToDismissProps } from '../../base/SwipeToDismiss/SwipeToDismiss' +import { AspectView } from '../../base/AspectView/AspectView' import { Section } from './Section/Section' export interface CardProps { @@ -43,6 +44,16 @@ export interface CardProps { * Main actions displayed as buttons at the bottom of the Card. */ mainActions?: TextAction[] + /** + * Source of the image shown in the Card. + */ + imageSource?: ImageSourcePropType + /** + * Image ratio (width / height). Defaukt is 2 / 1. + * + * For example, the default ratio of 2 / 1 means that the width will be twice the height. + */ + imageRatio?: number /** * Card content. */ @@ -65,6 +76,8 @@ export const Card: React.FC & { Section: typeof Section } = ({ fullWidth = false, subdued = false, warning = false, + imageSource, + imageRatio = 2 / 1, mainActions = [], onDismiss, }) => { @@ -74,6 +87,7 @@ export const Card: React.FC & { Section: typeof Section } = ({ backgroundColor: theme.colors.fill.background.lighter, borderRadius: fullWidth ? 0 : theme.radius.medium, + overflow: 'hidden', }, cardSubdued: { backgroundColor: shameStyles.card.subdued.backgroundColor, @@ -106,6 +120,7 @@ export const Card: React.FC & { Section: typeof Section } = ({ wasSwiped ? { height: shrink } : undefined, ]} > + {imageSource ? : null} {title ? : null} {items} {mainActions.map((action, index) => ( @@ -167,4 +182,21 @@ const CardMainAction: React.FC<{ action: TextAction }> = ({ action }) => ( ) +const imageStyles = StyleSheet.create({ + imageContainer: { + width: '100%', + }, + image: { + width: '100%', + height: '100%', + }, +}) + +const CardImage: React.FC<{ source: ImageSourcePropType; ratio: number }> = ({ source, ratio }) => ( + // We inverse ratio to have a height / width ratio + + + +) + Card.Section = Section