[design]: initialize design system

This commit is contained in:
41666 2019-05-20 00:57:55 -04:00
parent 6fb39d6c4d
commit b864df9393
No known key found for this signature in database
GPG key ID: BC51D07640DC10AF
14 changed files with 391 additions and 0 deletions

View file

@ -0,0 +1,12 @@
import * as React from 'react'
import { storiesOf } from '@storybook/react'
import Button from './Button'
const s = storiesOf('Button', module)
s.add('Default', () => <Button>Example</Button>)
s.add('Disabled', () => <Button>Example</Button>)
s.add('Primary', () => <Button>Example</Button>)
s.add('Secondary', () => <Button>Example</Button>)
s.add('Loading', () => <Button>Example</Button>)

View file

@ -0,0 +1,10 @@
import * as React from 'react'
import {
StyledButton
} from './styled-components'
const Button = ({ children, ...rest }: { children: React.ReactChild | React.ReactChild[] }) => <StyledButton {...rest}>
{children}
</StyledButton>
export default Button

View file

@ -0,0 +1,5 @@
export { default as default } from './Button'
export {
StyledButton
} from './styled-components'

View file

@ -0,0 +1,58 @@
// import * as React from 'react'
import styled from 'styled-components'
export const StyledButton = styled.button`
/* reset some styles */
box-shadow: none;
appearance: none;
outline: none;
cursor: pointer;
background-color: rgba(0,0,0,0.1);
/* real styles */
position: relative;
transition: all 0.05s ease-in-out;
padding: 1em 1.4em;
font-weight: bold;
border: 1px solid rgba(0,0,0,0.1);
&::after {
content: "";
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
/* transparent by default */
background-color: transparent;
transition: background-color 0.05s ease-in-out;
/* put the overlay behind the text */
z-index: -1;
}
/* on hover, raise, brighten and shadow */
&:hover {
transform: translateY(-1px);
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
&::after {
background-color: rgba(255, 255, 255, 0.3);
}
}
/* on click, lower and darken */
&:active {
outline: none;
box-shadow: none;
border-color: rgba(0,0,0,0.2);
transform: translateY(0);
&::after {
background-color: rgba(0, 0, 0, 0.1);
}
}
`

View file

@ -0,0 +1,34 @@
/*
Copyright (c) 2018-2019 Uber Technologies, Inc.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
export default function deepMerge (
target?: {},
...sources: Array<null | {}>
): {} {
target = target || {}
const len = sources.length
let obj
let value
for (let i = 0; i < len; i++) {
obj = sources[i] || {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
value = obj[key]
if (isCloneable(value)) {
target[key] = deepMerge(target[key] || {}, value)
} else {
target[key] = value
}
}
}
}
return target
}
/* eslint-disable-next-line flowtype/no-weak-types */
function isCloneable(obj: Object) {
return Array.isArray(obj) || {}.toString.call(obj) === '[object Object]'
}

View file

@ -0,0 +1,151 @@
/*
Copyright (c) 2018-2019 Uber Technologies, Inc.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
import * as React from 'react'
import deepMerge from './deep-merge'
export type StyleOverrideT = {} | (({ }) => {} | undefined)
export type OverrideObjectT<T> = {
component?: React.ComponentType<T>,
props?: {},
style?: StyleOverrideT
}
export type OverrideT<T> = OverrideObjectT<T> | React.ComponentType<T>
export type OverridesT<T> = {
[x: string]: OverrideT<T>
}
/**
* Given an override argument, returns the component implementation override if it exists
*/
// eslint-disable-next-line flowtype/no-weak-types
export function getOverride (override: any): any {
// Check if override is OverrideObjectT
if (override && typeof override === 'object') {
// Remove this 'any' once this flow issue is fixed:
// https://github.com/facebook/flow/issues/6666
// eslint-disable-next-line flowtype/no-weak-types
return override.component
}
// Otherwise it must be a component type (function or class) or null/undefined
return override
}
/**
* Given an override argument, returns the override props that should be passed
* to the component when rendering it.
*/
export function getOverrideProps<T> (override?: OverrideT<T>) {
if (override && typeof override === 'object') {
return {
// $FlowFixMe
...override.props,
// $FlowFixMe
$style: override.style
}
}
return {}
}
/**
* Coerces an override argument into an override object
* (sometimes it is just an override component)
*/
export function toObjectOverride<T> (
override: OverrideT<T>
): OverrideObjectT<T> {
if (typeof override === 'function') {
return {
component: (override)
}
}
// Flow can't figure out that typeof 'function' above will
// catch React.StatelessFunctionalComponent
// (probably related to https://github.com/facebook/flow/issues/6666)
// eslint-disable-next-line flowtype/no-weak-types
return (((override || {}) as any) as OverrideObjectT<T>)
}
/**
* Get a convenient override array that will always have [component, props]
*/
/* eslint-disable flowtype/no-weak-types */
export function getOverrides (
override: any,
defaultComponent: React.ComponentType<any>
): [React.ComponentType<any>, {}] {
const component = getOverride(override) || defaultComponent
const props = getOverrideProps(override)
return [component, props]
}
/* eslint-enable flowtype/no-weak-types */
/**
* Merges two overrides objects this is useful if you want to inject your own
* overrides into a child component, but also accept further overrides from
* from upstream. See `mergeOverride` below.
*/
export function mergeOverrides<T> (
target: OverridesT<T> | undefined = {},
source: OverridesT<T> | undefined = {}
): OverridesT<T> {
const allIdentifiers = Object.keys({ ...target, ...source })
return allIdentifiers.reduce((acc, name) => {
acc[name] = mergeOverride(
toObjectOverride(target[name]),
toObjectOverride(source[name])
)
return acc
}, {})
}
/**
* Merges two override objects using the following behavior:
* - Component implementation from the source (parent) replaces target
* - Props and styles are both deep merged
*/
export function mergeOverride<T> (
target: OverrideObjectT<T>,
source: OverrideObjectT<T>
): OverrideObjectT<T> {
// Shallow merge should handle `component`
const merged = { ...target, ...source }
// Props just use deep merge
if (target.props && source.props) {
merged.props = deepMerge({}, target.props, source.props)
}
// Style overrides need special merging since they may be functions
if (target.style && source.style) {
merged.style = mergeStyleOverrides(target.style, source.style)
}
return merged
}
/**
* Since style overrides can be an object *or* a function, we need to handle
* the case that one of them is a function. We do this by returning a new
* function that deep merges the result of each style override
*/
export function mergeStyleOverrides (
target: StyleOverrideT,
source: StyleOverrideT
): StyleOverrideT {
// Simple case of both objects
if (typeof target === 'object' && typeof source === 'object') {
return deepMerge({}, target, source)
}
// At least one is a function, return a new composite function
return (...args) => {
return deepMerge(
{},
typeof target === 'function' ? target(...args) : target,
typeof source === 'function' ? source(...args) : source
)
}
}