Initial implementation

This commit is contained in:
Nicolas Gallagher
2015-06-09 12:34:10 -07:00
parent e34820c11c
commit 90779f702a
47 changed files with 1341 additions and 1 deletions

6
.babelrc Normal file
View File

@@ -0,0 +1,6 @@
{
"optional": [
"runtime"
],
"stage": 1
}

327
README.md Normal file
View File

@@ -0,0 +1,327 @@
# react-web-sdk
**Experimental / API proof of concept**
Components for building web applications and SDKs. Based on `react-native`'s
components.
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.
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.
### 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
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.
## Example
```js
import React from 'react';
import {Image, Text, View} from 'react-web-sdk';
class Example extends React.Component {
render() {
return (
<View element="main" style={{ ...style.common, ...style.root }}>
<Image alt="accessibility text" src="/image.png" style={style.image} />
<Text style={{ ...style.common, ...(Math.random() > 0.5 && style.text) }}>
Example component
</Text>
</View>
);
}
}
const style = {
common: {
backgroundColor: 'white',
borderRadius: '1em'
},
root: {
flexDirection: 'row',
justifyContent: 'space-between'
},
image: {
opacity: 0.5
},
text: {
fontWeight: '300'
}
};
export default Example;
```
## Component: `SDKComponent`
A component that manages styles across the `className` and `style` properties.
The building block upon which all other components in `react-web-sdk` are
build.
### PropTypes
All other props are transferred directly to the `element`.
+ `className`: `string`
+ `element`: `func` or `string`
+ `style`: `object`
### Examples
```js
const ViewStylePropTypes = {
opacity: PropTypes.number
};
const ViewStyleDefaultProps = {
opacity: 1
};
class ViewExample extends React.Component {
render() {
// filter other props
const other = ReactWebSDK.getOtherProps(this);
// exclude other styles
const supportedStyle = ReactWebSDK.objectWithProps(this.props.style, ViewStylePropTypes);
// merge defaults with instance styles
const style = { ...ViewStyleDefaultProps, ...supportedStyle }
return (
<SDKComponent
{...other}
children={this.props.children}
className={`ViewExample ${this.props.className}`}
element={this.props.element}
style={style}
/>
);
}
}
```
## Component: `Image`
TODO
### PropTypes
All other props are transferred directly to the `element`.
+ `alt`: `string`
+ `async`: `bool` (TODO)
+ `className`: `string`
+ `src`: `string`
+ `style`: `ImageStylePropTypes`
### ImageStylePropTypes
+ `BackgroundPropTypes`
+ `BorderThemePropTypes`
+ `LayoutPropTypes`
+ `opacity`: `string`
## Component: `Text`
Text layout and styles.
### PropTypes
All other props are transferred directly to the `element`.
+ `className`: `string`
+ `element`: `func` or `string` (default `"div"`)
+ `style`: `TextStylePropTypes`
### TextStylePropTypes
+ ViewStylePropTypes
+ TypographicPropTypes
## Component: `View`
`View` is a flexbox container and the fundamental building block for UI. It is
designed to be nested inside other `View`'s and to have 0-to-many children of
any type.
### PropTypes
All other props are transferred directly to the `element`.
+ `className`: `string`
+ `element`: `func` or `string` (default `"div"`)
+ `pointerEvents`: `oneOf('all', 'box-only', 'box-none', 'none')`
+ `style`: `ViewStylePropTypes`
### ViewStylePropTypes
+ BackgroundPropTypes
+ BorderThemePropTypes
+ LayoutPropTypes
+ `boxShadow`: `string`
+ `color`: `string`
+ `opacity`: `number`
### ViewStyleDefaultProps
Implements the default styles from
[facebook/css-layout](https://github.com/facebook/css-layout).
```js
const ViewStyleDefaultProps = {
alignItems: 'stretch', // 1
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box', // 2
display: 'flex', // 3
flexBasis: 'auto', // 1
flexDirection: 'column', // 1
flexShrink: 0, // 1
listStyle: 'none',
margin: 0,
padding: 0,
position: 'relative' // 4
};
```
1. All the flex elements are oriented from top-to-bottom, left-to-right and do
not shrink. This is how things are laid out using the default CSS settings
and what you'd expect.
2. The most convenient way to express the relation between width and other
box-model properties.
3. Everything is `display:flex` by default. All the behaviors of `block` and
`inline-block` can be expressed in term of flex but not the opposite.
4. Everything is `position:relative`. This makes `position:absolute` target the
direct parent and not some parent which is either relative or absolute. If
you want to position an element relative to something else, you should move
it in the DOM instead of relying of CSS. It also makes `top`, `left`,
`right`, `bottom` do something when not specifying `position:absolute`.
### Examples
```js
// TODO
```
## StylePropTypes
### Background
+ `backgroundColor`: `string`
+ `backgroundImage`: `string`
+ `backgroundPosition`: `string`
+ `backgroundRepeat`: `string`
+ `backgroundSize`: `string`
### BorderTheme
+ `borderColor`: `string`
+ `borderTopColor`: `string`
+ `borderRightColor`: `string`
+ `borderBottomColor`: `string`
+ `borderLeftColor`: `string`
+ `borderStyle`: `string`
+ `borderRadius`: `number` or `string`
+ `borderTopLeftRadius`: `number` or `string`
+ `borderTopRightRadius`: `number` or `string`
+ `borderBottomLeftRadius`: `number` or `string`
+ `borderBottomRightRadius`: `number` or `string`
### BoxModel
+ `borderWidth`: `number` or `string`
+ `borderTopWidth`: `number` or `string`
+ `borderRightWidth`: `number` or `string`
+ `borderBottomWidth`: `number` or `string`
+ `borderLeftWidth`: `number` or `string`
+ `boxSizing`: `oneOf('border-box', 'content-box')`
+ `display`: `oneOf('block', 'flex', 'inline', 'inline-block', 'inline-flex')`
+ `height`: `number` or `string`
+ `margin`: `number` or `string`
+ `marginTop`: `number` or `string`
+ `marginRight`: `number` or `string`
+ `marginBottom`: `number` or `string`
+ `marginLeft`: `number` or `string`
+ `padding`: `number` or `string`
+ `paddingTop`: `number` or `string`
+ `paddingRight`: `number` or `string`
+ `paddingBottom`: `number` or `string`
+ `paddingLeft`: `number` or `string`
+ `width`: `number` or `string`
### Flexbox
* `alignContent`: `oneOf('center', 'flex-end', 'flex-start', 'stretch', 'space-around', 'space-between')`
* `alignItems`: `oneOf('baseline', 'center', 'flex-end', 'flex-start', 'stretch')`
* `alignSelf`: `oneOf('auto', 'baseline', 'center', 'flex-end', 'flex-start', 'stretch')`
* `flexBasis`: `string`
* `flexDirection`: `oneOf('column', 'column-reverse', 'row', 'row-reverse')`
* `flexGrow`: `number`
* `flexShrink`: `number`
* `flexWrap`: `oneOf('nowrap', 'wrap', 'wrap-reverse')`
* `justifyContent`: `oneOf('center', 'flex-end', 'flex-start', 'space-around', 'space-between')`
* `order`: `number`
See this [guide to flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/).
### Layout
* BoxModel
* Flexbox
* Position
### Position
* `position`: `oneOf('absolute', 'fixed', 'relative')`
* `bottom`: `number` or `string`
* `left`: `number` or `string`
* `right`: `number` or `string`
* `top`: `number` or `string`
* `zIndex`: `number`
### Typographic
* `direction`: `oneOf('auto', 'ltr', 'rtl')`
* `fontFamily`: `string`
* `fontSize`: `string`
* `fontWeight`: `oneOf('100', '200', '300', '400', '500', '600', '700', '800', '900', 'bold', 'normal')`
* `fontStyle`: `oneOf('normal', 'italic')`
* `letterSpacing`: `string`
* `lineHeight`: `number` or `string`
* `textAlign`: `oneOf('auto', 'left', 'right', 'center')`
* `textDecoration`: `oneOf('none', 'underline')`
* `textTransform`: `oneOf('capitalize', 'lowercase', 'none', 'uppercase')`
* `wordWrap`: `oneOf('break-word', 'normal')`
## Development
```
npm run build
npm run build:watch
open index.html
```

39
example.js Normal file
View File

@@ -0,0 +1,39 @@
import React from 'react';
import { Image, Text, View } from '.';
class Example extends React.Component {
render() {
return (
<View style={style.root}>
{[1,2,3,4,5,6].map((item) => {
return (
<View style={{ ...style.box, ...(item === 6 && style.boxFull) }}>
<Text style={{fontSize: '2rem'}}>{item}</Text>
</View>
);
})}
</View>
);
}
}
const style = {
root: {
flexDirection: 'row',
flexWrap: 'wrap',
height: '100px'
},
box: {
alignItems: 'center',
backgroundColor: 'lightblue',
flexGrow: 1,
justifyContent: 'center',
borderColor: 'blue',
borderWidth: '5px'
},
boxFull: {
width: '100%'
}
}
React.render(<Example />, document.getElementById('react-root'));

4
index.html Normal file
View File

@@ -0,0 +1,4 @@
<!DOCTYPE html>
<meta charset="utf-8">
<div id="react-root"></div>
<script src="dist/example.js"></script>

14
index.js Normal file
View File

@@ -0,0 +1,14 @@
import getOtherProps, {objectWithProps} from './lib/getOtherProps';
import Image from './lib/components/Image';
import SDKComponent from './lib/components/SDKComponent';
import Text from './lib/components/Text';
import View from './lib/components/View';
export default {
getOtherProps,
Image,
objectWithProps,
SDKComponent,
Text,
View
};

0
lib/.gitkeep Normal file
View File

View File

@@ -0,0 +1,9 @@
import {PropTypes} from 'react';
export default {
backgroundColor: PropTypes.string,
backgroundImage: PropTypes.string,
backgroundPosition: PropTypes.string,
backgroundRepeat: PropTypes.string,
backgroundSize: PropTypes.string
};

View File

@@ -0,0 +1,28 @@
import {PropTypes} from 'react';
export default {
// border-color
borderColor: PropTypes.string,
borderTopColor: PropTypes.string,
borderRightColor: PropTypes.string,
borderBottomColor: PropTypes.string,
borderLeftColor: PropTypes.string,
// border-style
borderStyle: PropTypes.string,
// border-radius
borderRadius: PropTypes.oneOfType([
PropTypes.number, PropTypes.string
]),
borderTopLeftRadius: PropTypes.oneOfType([
PropTypes.number, PropTypes.string
]),
borderTopRightRadius: PropTypes.oneOfType([
PropTypes.number, PropTypes.string
]),
borderBottomLeftRadius: PropTypes.oneOfType([
PropTypes.number, PropTypes.string
]),
borderBottomRightRadius: PropTypes.oneOfType([
PropTypes.number, PropTypes.string
])
};

View File

@@ -0,0 +1,71 @@
import {PropTypes} from 'react';
export default {
boxSizing: PropTypes.oneOf([
'border-box',
'content-box'
]),
// display
display: PropTypes.oneOf([
'block',
'flex',
'inline',
'inline-block',
'inline-flex'
]),
// dimensions
height: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
width: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
// border width
borderWidth: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
borderTopWidth: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
borderRightWidth: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
borderBottomWidth: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
borderLeftWidth: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
// margin
margin: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
marginTop: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
marginBottom: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
marginLeft: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
marginRight: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
// padding
padding: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
paddingTop: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
paddingBottom: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
paddingLeft: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
]),
paddingRight: PropTypes.oneOfType([
PropTypes.string, PropTypes.number
])
};

View File

@@ -0,0 +1,49 @@
import {PropTypes} from 'react';
export default {
alignContent: PropTypes.oneOf([
'center',
'flex-end',
'flex-start',
'stretch',
'space-around',
'space-between'
]),
alignItems: PropTypes.oneOf([
'baseline',
'center',
'flex-end',
'flex-start',
'stretch'
]),
alignSelf: PropTypes.oneOf([
'auto',
'baseline',
'center',
'flex-end',
'flex-start',
'stretch'
]),
flexBasis: PropTypes.string,
flexDirection: PropTypes.oneOf([
'column',
'column-reverse',
'row',
'row-reverse'
]),
flexGrow: PropTypes.number,
flexShrink: PropTypes.number,
flexWrap: PropTypes.oneOf([
'nowrap',
'wrap',
'wrap-reverse'
]),
justifyContent: PropTypes.oneOf([
'center',
'flex-end',
'flex-start',
'space-around',
'space-between'
]),
order: PropTypes.number
};

View File

@@ -0,0 +1,9 @@
import BoxModel from './BoxModel';
import Flexbox from './Flexbox';
import Position from './Position';
export default {
...BoxModel,
...Flexbox,
...Position
};

View File

@@ -0,0 +1,14 @@
import {PropTypes} from 'react';
export default {
position: PropTypes.oneOf([
'absolute',
'fixed',
'relative'
]),
bottom: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
left: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
right: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
top: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
zIndex: PropTypes.number
};

View File

@@ -0,0 +1,30 @@
import {PropTypes} from 'react';
export default {
direction: PropTypes.oneOf([
'auto', 'ltr', 'rtl'
]),
fontFamily: PropTypes.string,
fontSize: PropTypes.string,
fontWeight: PropTypes.oneOf([
'100', '200', '300', '400', '500', '600', '700', '800', '900',
'bold', 'normal'
]),
fontStyle: PropTypes.oneOf([
'normal', 'italic'
]),
letterSpacing: PropTypes.string,
lineHeight: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
textAlign: PropTypes.oneOf([
'auto', 'left', 'right', 'center'
]),
textDecoration: PropTypes.oneOf([
'none', 'underline'
]),
textTransform: PropTypes.oneOf([
'capitalize', 'lowercase', 'none', 'uppercase'
]),
wordWrap: PropTypes.oneOf([
'break-word', 'normal'
])
};

View File

@@ -0,0 +1,13 @@
import BackgroundPropTypes from './Background';
import BorderThemePropTypes from './BorderTheme';
import FlexboxPropTypes from './Flexbox';
import LayoutPropTypes from './Layout';
import TypographicPropTypes from './Typographic';
export default {
BackgroundPropTypes,
BorderThemePropTypes,
FlexboxPropTypes,
LayoutPropTypes,
TypographicPropTypes
};

47
lib/autoprefix.js Normal file
View File

@@ -0,0 +1,47 @@
export default function prefixStyles(style) {
if (style.hasOwnProperty('flexBasis')) {
style = {
WebkitFlexBasis: style.flexBasis,
msFlexBasis: style.flexBasis,
...style
};
}
if (style.hasOwnProperty('flexGrow')) {
style = {
WebkitBoxFlex: style.flexGrow,
WebkitFlexGrow: style.flexGrow,
msFlexPositive: style.flexGrow,
...style
};
}
if (style.hasOwnProperty('flexShrink')) {
style = {
WebkitFlexShrink: style.flexShrink,
msFlexNegative: style.flexShrink,
...style
};
}
// NOTE: adding `;` to the string value prevents React from automatically
// adding a `px` suffix to the unitless value
if (style.hasOwnProperty('order')) {
style = {
WebkitBoxOrdinalGroup: `${parseInt(style.order, 10) + 1};`,
WebkitOrder: `${style.order}`,
msFlexOrder: `${style.order};`,
...style
};
}
if (style.hasOwnProperty('transform')) {
style = {
WebkitTransform: style.transform,
msTransform: style.transform,
...style
};
}
return style;
}

61
lib/components/Image.js Normal file
View File

@@ -0,0 +1,61 @@
import {objectWithProps} from '../getOtherProps';
import React, {PropTypes} from 'react';
import StylePropTypes from '../StylePropTypes';
import SDKComponent from './SDKComponent';
const ImageStyleDefaultProps = {
backgroundColor: 'lightGray',
borderWidth: 0,
maxWidth: '100%'
};
const ImageStylePropTypes = {
...StylePropTypes.BorderThemePropTypes,
...StylePropTypes.LayoutPropTypes,
backgroundColor: PropTypes.string,
opacity: PropTypes.string
};
class Image extends React.Component {
static _getPropTypes() {
return {
alt: PropTypes.string,
async: PropTypes.bool,
className: PropTypes.string,
src: PropTypes.string,
style: PropTypes.shape(ImageStylePropTypes)
};
}
static _getDefaultProps() {
return {
async: true,
className: '',
src: 'data:image/gif;base64,' +
'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
style: {}
};
}
render() {
const { alt, className, src, style, ...other } = this.props;
const filteredStyle = objectWithProps(style, Object.keys(ImageStylePropTypes));
const mergedStyle = { ...ImageStyleDefaultProps, ...filteredStyle };
return (
<SDKComponent
{...other}
alt={alt}
className={`Image ${className}`}
element="img"
src={src}
style={mergedStyle}
/>
);
}
}
Image.propTypes = Image._getPropTypes();
Image.defaultProps = Image._getDefaultProps();
export default Image;

View File

@@ -0,0 +1,74 @@
import autoprefix from '../autoprefix';
import getOtherProps from '../getOtherProps';
import React, {PropTypes} from 'react';
// styles
import styleMap from '../styleMap';
class SDKComponent extends React.Component {
static _getPropTypes() {
return {
className: PropTypes.string,
element: PropTypes.oneOfType([
PropTypes.string, PropTypes.func
]),
style: PropTypes.object
};
}
static _getDefaultProps() {
return {
className: '',
element: 'div',
style: {}
};
}
render() {
const other = getOtherProps(this);
const { classNames, inlineStyles } = this._separateClassNamesAndStyles();
return (
<this.props.element
{...other}
className={classNames.join(' ')}
style={autoprefix(inlineStyles)}
/>
);
}
_getSinglePurposeClassName(prop, style) {
const uniqueClassName = `${prop}-${style[prop]}`;
if (
style.hasOwnProperty(prop) &&
styleMap[uniqueClassName]
) {
return styleMap[uniqueClassName];
}
}
_separateClassNamesAndStyles() {
const classNameProp = this.props.className;
const styleProp = this.props.style;
let classNames = [ classNameProp ];
let inlineStyles = {};
for (let prop in styleProp) {
let singlePurposeClassName =
this._getSinglePurposeClassName(prop, styleProp);
if (singlePurposeClassName) {
classNames.push(singlePurposeClassName);
} else {
inlineStyles[prop] = styleProp[prop];
}
}
return { classNames, inlineStyles };
}
}
SDKComponent.propTypes = SDKComponent._getPropTypes();
SDKComponent.defaultProps = SDKComponent._getDefaultProps();
export default SDKComponent;

65
lib/components/Text.js Normal file
View File

@@ -0,0 +1,65 @@
import {objectWithProps} from '../getOtherProps';
import React, {PropTypes} from 'react';
import StylePropTypes from '../StylePropTypes';
import SDKComponent from './SDKComponent';
import {ViewStylePropTypes} from './View';
const TextStyleDefaultProps = {
alignItems: 'stretch', /* 1 */
borderWidth: '0',
borderStyle: 'solid',
boxSizing: 'border-box', /* 2 */
display: 'flex', /* 3 */
flexBasis: 'auto', /* 1 */
flexDirection: 'column', /* 1 */
flexShrink: 0, /* 1 */
listStyle: 'none',
margin: '0',
padding: '0',
position: 'relative' /* 4 */
};
const TextStylePropTypes = {
...ViewStylePropTypes,
...StylePropTypes.TypographicPropTypes
};
class Text extends React.Component {
static _getPropTypes() {
return {
className: PropTypes.string,
element: PropTypes.oneOfType([
PropTypes.string, PropTypes.func
]),
style: PropTypes.shape(TextStylePropTypes)
};
}
static _getDefaultProps() {
return {
className: '',
element: 'div',
style: {}
};
}
render() {
const { className, element, style, ...other } = this.props;
const filteredStyle = objectWithProps(style, Object.keys(TextStylePropTypes));
const mergedStyle = { ...TextStyleDefaultProps, ...filteredStyle };
return (
<SDKComponent
{...other}
className={`Text ${className}`}
element={element}
style={mergedStyle}
/>
);
}
}
Text.propTypes = Text._getPropTypes();
Text.defaultProps = Text._getDefaultProps();
export default Text;

81
lib/components/View.js Normal file
View File

@@ -0,0 +1,81 @@
import {objectWithProps} from '../getOtherProps';
import React, {PropTypes} from 'react';
import StylePropTypes from '../StylePropTypes';
import SDKComponent from './SDKComponent';
// https://github.com/facebook/css-layout#default-values
const ViewStyleDefaultProps = {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
listStyle: 'none',
margin: 0,
padding: 0,
position: 'relative'
};
const ViewStylePropTypes = {
...StylePropTypes.BackgroundPropTypes,
...StylePropTypes.BorderThemePropTypes,
...StylePropTypes.LayoutPropTypes,
boxShadow: PropTypes.string,
opacity: PropTypes.number,
transform: PropTypes.string
};
class View extends React.Component {
static _getPropTypes() {
return {
className: PropTypes.string,
element: PropTypes.oneOfType([
PropTypes.string, PropTypes.func
]),
pointerEvents: PropTypes.oneOf([
'auto',
'box-none',
'box-only',
'none'
]),
style: PropTypes.shape(ViewStylePropTypes)
};
}
static _getDefaultProps() {
return {
className: '',
element: 'div',
style: {}
};
}
render() {
const { className, element, pointerEvents, style, ...other } = this.props;
const filteredStyle = objectWithProps(style, Object.keys(ViewStylePropTypes));
const pointerEventsStyle = pointerEvents && { pointerEvents };
const mergedStyle = {
...ViewStyleDefaultProps,
...filteredStyle,
...pointerEventsStyle
};
return (
<SDKComponent
{...other}
className={`View ${className}`}
element={element}
style={mergedStyle}
/>
);
}
}
View.propTypes = View._getPropTypes();
View.defaultProps = View._getDefaultProps();
export default View;
export {ViewStylePropTypes};

0
lib/css/.gitkeep Normal file
View File

13
lib/css/background.css Normal file
View File

@@ -0,0 +1,13 @@
:local .backgroundColor-#fff { background-color: #fff; }
:local .backgroundColor-transparent { background-color: currentcolor; }
:local .backgroundImage { background-image: none; }
:local .backgroundPosition-center { background-position: center; }
:local .backgroundSize-contain { background-size: contain; }
:local .backgroundSize-cover { background-size: cover; }
:local .backgroundRepeat-no-repeat { background-repeat: no-repeat; }
:local .backgroundRepeat-repeat { background-repeat: repeat; }
:local .backgroundRepeat-repeat-x { background-repeat: repeat-x; }
:local .backgroundRepeat-repeat-y { background-repeat: repeat-y; }

42
lib/css/border.css Normal file
View File

@@ -0,0 +1,42 @@
:local .borderColor-currentcolor { border-color: currentcolor; }
:local .borderColor-transparent { border-color: transparent; }
:local .borderStyle-dotted { border-style: dotted; }
:local .borderStyle-dashed { border-style: dashed; }
:local .borderStyle-solid { border-style: solid; }
:local .borderWidth-0 { border-width: 0; }
:local .borderTopWidth-0 { border-top-width: 0; }
:local .borderRightWidth-0 { border-right-width: 0; }
:local .borderBottomWidth-0 { border-bottom-width: 0; }
:local .borderLeftWidth-0 { border-left-width: 0; }
:local .borderWidth-1px { border-width: 1px; }
:local .borderTopWidth-1px { border-top-width: 1px; }
:local .borderRightWidth-1px { border-right-width: 1px; }
:local .borderBottomWidth-1px { border-bottom-width: 1px; }
:local .borderLeftWidth-1px { border-left-width: 1px; }
:local .borderWidth-2px { border-width: 2px; }
:local .borderTopWidth-2px { border-top-width: 2px; }
:local .borderRightWidth-2px { border-right-width: 2px; }
:local .borderBottomWidth-2px { border-bottom-width: 2px; }
:local .borderLeftWidth-2px { border-left-width: 2px; }
:local .borderWidth-3px { border-width: 3px; }
:local .borderTopWidth-3px { border-top-width: 3px; }
:local .borderRightWidth-3px { border-right-width: 3px; }
:local .borderBottomWidth-3px { border-bottom-width: 3px; }
:local .borderLeftWidth-3px { border-left-width: 3px; }
:local .borderWidth-4px { border-width: 4px; }
:local .borderTopWidth-4px { border-top-width: 4px; }
:local .borderRightWidth-4px { border-right-width: 4px; }
:local .borderBottomWidth-4px { border-bottom-width: 4px; }
:local .borderLeftWidth-4px { border-left-width: 4px; }
:local .borderWidth-5px { border-width: 5px; }
:local .borderTopWidth-5px { border-top-width: 5px; }
:local .borderRightWidth-5px { border-right-width: 5px; }
:local .borderBottomWidth-5px { border-bottom-width: 5px; }
:local .borderLeftWidth-5px { border-left-width: 5px; }

2
lib/css/boxSizing.css Normal file
View File

@@ -0,0 +1,2 @@
:local .boxSizing-border-box { box-sizing: border-box; }
:local .boxSizing-content-box { box-sizing: content-box; }

4
lib/css/clear.css Normal file
View File

@@ -0,0 +1,4 @@
:local .clear-both { clear: both; }
:local .clear-left { clear: left; }
:local .clear-none { clear: none; }
:local .clear-right { clear: right; }

4
lib/css/color.css Normal file
View File

@@ -0,0 +1,4 @@
:local .color-\#fff { color: #fff; }
:local .color-\#000 { color: #000; }
:local .color-transparent { color: transparent; }
:local .color-inherit { color: inherit; }

3
lib/css/direction.css Normal file
View File

@@ -0,0 +1,3 @@
:local .direction-auto { direction: auto; }
:local .direction-ltr { direction: ltr; }
:local .direction-rtl { direction: rtl; }

12
lib/css/display.css Normal file
View File

@@ -0,0 +1,12 @@
:local .display-block { display: block; }
:local .display-flex { display: flex; }
:local .display-inline { display: inline; }
:local .display-inline-block { display: inline-block; }
:local .display-inline-flex { display: inline-flex; }
:local .display-inline-table { display: inline-table; }
:local .display-none { display: none; }
:local .display-table { display: table; }
:local .display-table-row { display: table-row; }
:local .display-table-cell { display: table-cell; }
:local .display-table-column { display: table-column; }
:local .display-table-column-group { display: table-column-group; }

70
lib/css/flexbox.css Normal file
View File

@@ -0,0 +1,70 @@
:local .alignContent-center { align-content: center; }
:local .alignContent-flex-end { align-content: flex-end; }
:local .alignContent-flex-start { align-content: flex-start; }
:local .alignContent-stretch { align-content: stretch; }
:local .alignContent-space-around { align-content: space-around; }
:local .alignContent-space-between { align-content: space-between; }
:local .alignItems-center { align-items: center; }
:local .alignItems-flex-end { align-items: flex-end; }
:local .alignItems-flex-start { align-items: flex-start; }
:local .alignItems-stretch { align-items: stretch; }
:local .alignItems-space-around { align-items: space-around; }
:local .alignItems-space-between { align-items: space-between; }
:local .alignSelf-auto { align-self: auto; }
:local .alignSelf-baseline { align-self: baseline; }
:local .alignSelf-center { align-self: center; }
:local .alignSelf-flex-end { align-self: flex-end; }
:local .alignSelf-flex-start { align-self: flex-start; }
:local .alignSelf-stretch { align-self: stretch; }
:local .flexBasis-0 { flex-basis: 0%; }
:local .flexBasis-auto { flex-basis: auto; }
:local .flexDirection-column { flex-direction: column; }
:local .flexDirection-column-reverse { flex-direction: column-reverse; }
:local .flexDirection-row { flex-direction: row; }
:local .flexDirection-row-reverse { flex-direction: row-reverse; }
:local .flexGrow-0 { flex-grow: 0; }
:local .flexGrow-1 { flex-grow: 1; }
:local .flexGrow-2 { flex-grow: 2; }
:local .flexGrow-3 { flex-grow: 3; }
:local .flexGrow-4 { flex-grow: 4; }
:local .flexGrow-5 { flex-grow: 5; }
:local .flexGrow-6 { flex-grow: 6; }
:local .flexGrow-7 { flex-grow: 7; }
:local .flexGrow-8 { flex-grow: 8; }
:local .flexGrow-9 { flex-grow: 9; }
:local .flexShrink-0 { flex-shrink: 0; }
:local .flexShrink-1 { flex-shrink: 1; }
:local .flexShrink-2 { flex-shrink: 2; }
:local .flexShrink-3 { flex-shrink: 3; }
:local .flexShrink-4 { flex-shrink: 4; }
:local .flexShrink-5 { flex-shrink: 5; }
:local .flexShrink-6 { flex-shrink: 6; }
:local .flexShrink-7 { flex-shrink: 7; }
:local .flexShrink-8 { flex-shrink: 8; }
:local .flexShrink-9 { flex-shrink: 9; }
:local .flexWrap-nowrap { flex-wrap: nowrap; }
:local .flexWrap-wrap { flex-wrap: wrap; }
:local .flexWrap-wrap-reverse { flex-wrap: wrap-reverse; }
:local .justifyContent-center { justify-content: center; }
:local .justifyContent-flex-end { justify-content: flex-end; }
:local .justifyContent-flex-start { justify-content: flex-start; }
:local .justifyContent-space-around { justify-content: space-around; }
:local .justifyContent-space-between { justify-content: space-between; }
:local .order-1 { order: 1; }
:local .order-2 { order: 2; }
:local .order-3 { order: 3; }
:local .order-4 { order: 4; }
:local .order-5 { order: 5; }
:local .order-6 { order: 6; }
:local .order-7 { order: 7; }
:local .order-8 { order: 8; }
:local .order-9 { order: 9; }

3
lib/css/float.css Normal file
View File

@@ -0,0 +1,3 @@
:local .float-left { float: left; }
:local .float-none { float: none; }
:local .float-right { float: right; }

16
lib/css/font.css Normal file
View File

@@ -0,0 +1,16 @@
:local .font-inherit { font: inherit; }
:local .fontStyle-italic { font-style: italic; }
:local .fontStyle-normal { font-style: normal; }
:local .fontWeight-100 { font-weight: 100; }
:local .fontWeight-200 { font-weight: 200; }
:local .fontWeight-300 { font-weight: 300; }
:local .fontWeight-400 { font-weight: 400; }
:local .fontWeight-500 { font-weight: 500; }
:local .fontWeight-600 { font-weight: 600; }
:local .fontWeight-700 { font-weight: 700; }
:local .fontWeight-800 { font-weight: 800; }
:local .fontWeight-900 { font-weight: 900; }
:local .fontWeight-bold { font-weight: bold; }
:local .fontWeight-normal { font-weight: normal; }

3
lib/css/height.css Normal file
View File

@@ -0,0 +1,3 @@
:local .maxHeight-100\% { max-height: 100%; }
:local .minHeight-100\% { min-height: 100%; }
:local .height-100\% { height: 100%; }

13
lib/css/margin.css Normal file
View File

@@ -0,0 +1,13 @@
:local .margin-0 { margin: 0; }
:local .margin-auto { margin: auto; }
:local .marginTop-0 { margin-top: 0; }
:local .marginTop-auto { margin-top: auto; }
:local .marginRight-0 { margin-right: 0; }
:local .marginRight-auto { margin-right: auto; }
:local .marginBottom-0 { margin-bottom: 0; }
:local .marginBottom-auto { margin-bottom: auto; }
:local .marginLeft-0 { margin-left: 0; }
:local .marginLeft-auto { margin-left: auto; }

14
lib/css/overflow.css Normal file
View File

@@ -0,0 +1,14 @@
:local .overflow-auto { overflow: auto; }
:local .overflow-hidden { overflow: hidden; }
:local .overflow-scroll { overflow: scroll; }
:local .overflow-visible { overflow: visible; }
:local .overflowX-auto { overflow-x: auto; }
:local .overflowX-hidden { overflow-x: hidden; }
:local .overflowX-scroll { overflow-x: scroll; }
:local .overflowX-visible { overflow-x: visible; }
:local .overflowY-auto { overflow-y: auto; }
:local .overflowY-hidden { overflow-y: hidden; }
:local .overflowY-scroll { overflow-y: scroll; }
:local .overflowY-visible { overflow-y: visible; }

17
lib/css/padding.css Normal file
View File

@@ -0,0 +1,17 @@
:local .padding-0 { padding: 0; }
:local .paddingTop-0 { padding-top: 0; }
:local .paddingRight-0 { padding-right: 0; }
:local .paddingBottom-0 { padding-bottom: 0; }
:local .paddingLeft-0 { padding-left: 0; }
:local .padding-1em { padding: 1em; }
:local .paddingTop-1em { padding-top: 1em; }
:local .paddingRight-1em { padding-right: 1em; }
:local .paddingBottom-1em { padding-bottom: 1em; }
:local .paddingLeft-1em { padding-left: 1em; }
:local .padding-1rem { padding: 1rem; }
:local .paddingTop-1rem { padding-top: 1rem; }
:local .paddingRight-1rem { padding-right: 1rem; }
:local .paddingBottom-1rem { padding-bottom: 1rem; }
:local .paddingLeft-1rem { padding-left: 1rem; }

View File

@@ -0,0 +1,6 @@
:local .pointerEvents-auto { pointer-events: auto; }
:local .pointerEvents-none { pointer-events: none; }
:local .pointerEvents-box-none { pointer-events: none; }
:local .pointerEvents-box-none * { pointer-events: auto;}
:local .pointerEvents-box-only { pointer-events: auto; }
:local .pointerEvents-box-only * { pointer-events: none; }

13
lib/css/position.css Normal file
View File

@@ -0,0 +1,13 @@
:local .position-absolute { position: absolute; }
:local .position-fixed { position: fixed; }
:local .position-relative { position: relative; }
:local .top-0 { top: 0; }
:local .right-0 { right: 0; }
:local .left-0 { left: 0; }
:local .bottom-0 { bottom: 0; }
:local .top-100\% { top: 100%; }
:local .right-100\% { right: 100%; }
:local .left-100\% { left: 100%; }
:local .bottom-100\% { bottom: 100%; }

14
lib/css/text.css Normal file
View File

@@ -0,0 +1,14 @@
:local .textAlign-center { text-align: center; }
:local .textAlign-left { text-align: left; }
:local .textAlign-right { text-align: right; }
:local .textAlign-justify { text-align: justify; }
:local .textDecoration-none { text-decoration: none; }
:local .textDecoration-underline { text-decoration: underline; }
:local .textOverflow-ellipsis { text-overflow: ellipsis }
:local .textTransform-capitalize { text-transform: capitalize; }
:local .textTransform-lowercase { text-transform: lowercase; }
:local .textTransform-none { text-transform: none; }
:local .textTransform-uppercase { text-transform: uppercase; }

3
lib/css/userSelect.css Normal file
View File

@@ -0,0 +1,3 @@
:local .userSelect-all { user-select: all; }
:local .userSelect-inherit { user-select: inherit; }
:local .userSelect-none { user-select: none; }

2
lib/css/visibility.css Normal file
View File

@@ -0,0 +1,2 @@
:local .visibility-hidden { visibility: hidden; }
:local .visibility-visible { visibility: visible; }

3
lib/css/whiteSpace.css Normal file
View File

@@ -0,0 +1,3 @@
:local .whiteSpace-normal { white-space: normal; }
:local .whiteSpace-nowrap { white-space: nowrap; }
:local .whiteSpace-pre { white-space: pre; }

3
lib/css/width.css Normal file
View File

@@ -0,0 +1,3 @@
:local .maxWidth-100\% { max-width: 100%; }
:local .minWidth-100\% { min-width: 100%; }
:local .width-100\% { width: 100%; }

2
lib/css/word.css Normal file
View File

@@ -0,0 +1,2 @@
:local .wordWrap-break-word { word-wrap: break-word; }
:local .wordWrap-normal { word-wrap: normal; }

12
lib/css/zIndex.css Normal file
View File

@@ -0,0 +1,12 @@
:local .zIndex--1 { z-index: -1; }
:local .zIndex-0 { z-index: 0; }
:local .zIndex-1 { z-index: 1; }
:local .zIndex-2 { z-index: 2; }
:local .zIndex-3 { z-index: 3; }
:local .zIndex-4 { z-index: 4; }
:local .zIndex-5 { z-index: 5; }
:local .zIndex-6 { z-index: 6; }
:local .zIndex-7 { z-index: 7; }
:local .zIndex-8 { z-index: 8; }
:local .zIndex-9 { z-index: 9; }
:local .zIndex-10 { z-index: 10; }

37
lib/getOtherProps.js Normal file
View File

@@ -0,0 +1,37 @@
/**
* Extract all props that are not part of the React Component's 'propTypes'
*/
export default (componentInstance) => {
const excludedProps = Object.keys(componentInstance.constructor.propTypes);
return objectWithoutProps(componentInstance.props, excludedProps);
};
function objectFilterProps(obj, props, excluded=false) {
if (!Array.isArray(props)) {
throw new TypeError('props is not an Array');
}
let filtered = {};
for (let prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
let isMatch = props.indexOf(prop) > -1;
if (excluded && isMatch) {
continue;
} else if (!excluded && !isMatch) {
continue;
}
filtered[prop] = obj[prop];
}
}
return filtered;
}
export function objectWithProps(obj, props) {
return objectFilterProps(obj, props);
}
export function objectWithoutProps(obj, props) {
return objectFilterProps(obj, props, true);
}

47
lib/styleMap.js Normal file
View File

@@ -0,0 +1,47 @@
import border from './css/border.css';
import boxSizing from './css/boxSizing.css';
import clear from './css/clear.css';
import color from './css/color.css';
import cssFloat from './css/float.css';
import direction from './css/direction.css';
import display from './css/display.css';
import flexbox from './css/flexbox.css';
import font from './css/font.css';
import height from './css/height.css';
import margin from './css/margin.css';
import overflow from './css/overflow.css';
import padding from './css/padding.css';
import pointerEvents from './css/pointerEvents.css';
import position from './css/position.css';
import text from './css/text.css';
import userSelect from './css/userSelect.css';
import visibility from './css/visibility.css';
import whiteSpace from './css/whiteSpace.css';
import width from './css/width.css';
import word from './css/word.css';
const map = Object.assign({},
border,
boxSizing,
clear,
color,
cssFloat,
direction,
display,
flexbox,
font,
height,
margin,
overflow,
padding,
pointerEvents,
position,
text,
userSelect,
visibility,
whiteSpace,
width,
word
);
export default map;

View File

@@ -3,6 +3,28 @@
"version": "0.0.1",
"description": "UI SDK based on react-native",
"main": "index.js",
"scripts": {
"build": "npm run build:package & npm run build:example",
"build:example": "webpack index.js dist/main.js --config webpack.config.js",
"build:example:watch": "npm run build:example -- --watch",
"build:package": "webpack example.js dist/example.js --config webpack.config.js",
"build:package:watch": "npm run build:package -- --watch",
"build:watch": "npm run build:package:watch & npm run build:example:watch"
},
"author": "Nicolas Gallagher",
"license": "MIT"
"license": "MIT",
"devDependencies": {
"autoprefixer-core": "^5.2.0",
"babel-core": "^5.5.6",
"babel-loader": "^5.1.4",
"babel-runtime": "^5.5.6",
"css-loader": "^0.14.4",
"node-libs-browser": "^0.5.2",
"postcss-loader": "^0.4.4",
"style-loader": "^0.12.3",
"webpack": "^1.9.10"
},
"dependencies": {
"react": "^0.13.3"
}
}

19
webpack.config.js Normal file
View File

@@ -0,0 +1,19 @@
var autoprefixer = require('autoprefixer-core');
module.exports = {
module: {
loaders: [
{
test: /\.css$/,
loader: 'style-loader!css-loader?localIdentName=[hash:base64:5]!postcss-loader'
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: { cacheDirectory: true }
}
]
},
postcss: [ autoprefixer ]
};