From 858a8d9ebe6372c86ae5cb7ce6f9addca6990ba0 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Wed, 10 Jun 2015 12:26:05 -0700 Subject: [PATCH] Fix export; iterate on README --- README.md | 311 +++++++++++++++++++++++++++++++++++++++++++----------- index.js | 2 + 2 files changed, 249 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 118fcd7e..c248f94f 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,34 @@ # react-web-sdk -**Experimental / API proof of concept** +**Experimental / Proof of concept** -Components for building web applications and SDKs. Based on `react-native`'s -components. +A component-based React SDK for building and styling web applications and SDKs. +Inspired by `react-native`. -1. Styles are plain JavaScript objects. -2. Styles are passed to a component's `style` prop. -3. Non-dynamic styles rely on static CSS classes. -4. Dynamic styles are written as inline styles on the DOM node. +This library provides: -Each Component defines `StylePropTypes` and filters out any style properties -that are not part of its style API. For example, `View` does not support any -typographic properties. You must use `Text` to wrap and style any text strings. +* An initial implementation of the `react-native` components `Image`, `Text`, + and `View`. +* An initial implementation of defining explicit and default `style` propTypes. +* A proof of concept implementation for converting inline `style` definitions + to CSS classes. -### Implementation notes - -The current implementation uses a prebuilt CSS library – 200+ single-purpose, -obfuscated selectors. It provides a large number of common, knowable styles -needed to build apps. This makes the foundational CSS fixed (~5KB gzipped) and -highly cachable. Inline styles are used for anything the CSS library doesn't -provide, but their use is significantly reduced. - -A better implementation would generate the CSS library from the declarations -defined in the source code, replace static variables (use webpack's -`DefinePlugin`?), and only use inline-styles for dynamic, instance-specific +This proof of concept uses a ~3KB (gzipped) precomputed CSS bundle; a complete +implementation is likely to produce a slightly larger CSS file and fewer inline styles. -What about media queries? Perhaps rely on `mediaMatch` and re-render when the -`style` prop needs to change; works better with DOM-related changes that need -to happen at breakpoints. And that would avoid the need for any additional -CSS. Alternatively, rely on something similar to `react-style`'s approach and -duplicate the single-purpose classes within static CSS media queries. +Other React styling strategies: +[react-native](https://facebook.github.io/react-native/), +[react-style](https://github.com/js-next/react-style), +[jsxstyle](https://github.com/petehunt/jsxstyle), +[react-inline](https://github.com/martinandert/react-inline), and +[react-free-style](https://github.com/blakeembrey/react-free-style/) -### Example +### 1. Write styles using plain JavaScript objects + +Use JavaScript to write style definitions in React components: ```js -import React from 'react'; -import {Image, Text, View} from 'react-web-sdk'; - -class Example extends React.Component { - render() { - return ( - - accessibility text - 0.5 && style.text) }}> - Example component - - - ); - } -} - const style = { common: { backgroundColor: 'white', @@ -68,10 +45,131 @@ const style = { fontWeight: '300' } }; - -export default Example; ``` +### 2. Set styles using the `style` attribute + +The `style` attribute is a simple API for creating element-scoped styles. + +```js +... + + + ... +... +``` + +The current implementation uses a precomputed CSS library of known and common +declarations – 200+ single-declaration rules, with obfuscated selectors. This +handles a signficant portion of possible declarations. But it falls through to +inline styles significantly more often than an better implementation using +static analysis to generate the CSS library at build time. + +### 4. Use inline styles for dynamic values + +Inline styles are used for values that cannot be resolved at build time. + +```js + 0.5 ? 'red' : 'black')}}>... +``` + +### 5. Add `style` prop types + +Each Component defines `StylePropTypes` and filters out any style properties +that are not part of its style API. For example, `View` does not support any +typographic properties. + +See the section below on `StylePropTypes`. + +### Media Queries? + +We can use `mediaMatch` to orchestrate both style and DOM changes. The +component can be re-rendered when the styles change. This also avoids the need +for additional CSS. Adding media queries as keys on the `style` object (like +`react-style` does) might be a good, or it might not be. I'd prefer a solution +that accomodates both style and DOM changes, rather than one or the other. +Perhaps `this.context` or a higher-order component that passes viewport data to +components via `props`. + --- ## Components @@ -93,37 +191,26 @@ All other props are transferred directly to the `element`. #### Examples ```js -import {Component, getOtherProps, pickProps} from 'react-web-sdk'; +import {Component, pickProps} from 'react-web-sdk'; import React, {PropTypes} from 'react'; -const ExampleStylePropTypes = { - opacity: PropTypes.number -}; - -const ExampleStyleDefaultProps = { - opacity: 1 -}; +const ExampleStylePropTypes = { opacity: PropTypes.number }; +const ExampleStyleDefaultProps = { opacity: 1 }; class Example extends React.Component { static propTypes = { - anExampleProp: PropTypes.number, - someExampleProp: PropTypes.string, style: PropTypes.shape(ExampleStylePropTypes) } - static defaultProps = { - style: ExampleStyleDefaultProps - } - render() { - const other = getOtherProps(this); + // only apply supported styles const supportedStyle = pickProps(this.props.style, ExampleStylePropTypes); + // merge with default styles const style = { ...ExampleStyleDefaultProps, ...supportedStyle } return ( @@ -141,10 +228,10 @@ TODO All other props are transferred directly to the `element`. -+ `alt`: `string` ++ `accessibilityLabel`: `string` + `async`: `bool` (TODO) + `className`: `string` -+ `src`: `string` ++ `source`: `string` + `style`: `ImageStylePropTypes` #### ImageStylePropTypes @@ -154,6 +241,47 @@ All other props are transferred directly to the `element`. + `LayoutPropTypes` + `opacity`: `string` +#### Examples + +```js +import {Image, StylePropTypes} from 'react-web-sdk'; +import React, {PropTypes} from 'react'; + +const AvatarStylePropTypes = { + ...StylePropTypes.BorderThemePropTypes +}; + +const AvatarStyleDefaultProps = { + borderColor: 'white', + borderWidth: '5px' +}; + +class Avatar extends React.Component { + static propTypes = { + size: PropTypes.oneOf(['small', 'normal', 'large']), + style: PropTypes.shape(AvatarStylePropTypes), + user: PropTypes.object + } + + static defaultProps = { + size: 'normal' + } + + render() { + const supportedStyle = pickProps(this.props.style, AvatarStylePropTypes); + const style = { ...AvatarStyleDefaultProps, ...supportedStyle } + + return ( + + ); + } +} +``` + ### `Text` @@ -172,6 +300,61 @@ All other props are transferred directly to the `element`. + ViewStylePropTypes + TypographicPropTypes +#### Examples + +```js +import {StylePropTypes, Text} from 'react-web-sdk'; +import React, {PropTypes} from 'react'; + +class PrettyText extends React.Component { + static propTypes = { + color: PropTypes.oneOf(['white', 'gray', 'red']), + size: PropTypes.oneOf(['small', 'normal', 'large']), + weight: PropTypes.oneOf(['light', 'normal', 'bold']) + } + + static defaultProps = { + color: 'gray', + size: 'normal', + weight: 'normal' + } + + render() { + const { color, size, style, weight, ...other } = this.props; + + return ( + + ); + } +} + +const localStyle = { + color: { + white: { color: 'white' }, + gray: { color: 'gray' }, + red: { color: 'red' } + }, + size: { + small: { fontSize: '0.85rem', padding: '0.5rem' }, + normal: { fontSize: '1rem', padding: '0.75rem' }, + large: { fontSize: '1.5rem', padding: '1rem' } + }, + weight: { + light: { fontWeight: '300' }, + normal: { fontWeight: '400' }, + bold: { fontWeight: '700' } + } +} +``` + ### `View` diff --git a/index.js b/index.js index 500f1e52..454a592e 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ import {getOtherProps, omitProps, pickProps} from './lib/filterObjectProps'; +import StylePropTypes from './lib/StylePropTypes'; import Component from './lib/components/Component'; import Image from './lib/components/Image'; import Text from './lib/components/Text'; @@ -7,6 +8,7 @@ import View from './lib/components/View'; export default { getOtherProps, pickProps, + StylePropTypes, Component, Image, Text,