mirror of
https://github.com/roleypoly/roleypoly-v1.git
synced 2025-06-17 02:29:10 +00:00
[design]: initialize design system
This commit is contained in:
parent
6fb39d6c4d
commit
b864df9393
14 changed files with 391 additions and 0 deletions
12
packages/roleypoly-design/src/button/Button.story.tsx
Normal file
12
packages/roleypoly-design/src/button/Button.story.tsx
Normal 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>)
|
10
packages/roleypoly-design/src/button/Button.tsx
Normal file
10
packages/roleypoly-design/src/button/Button.tsx
Normal 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
|
5
packages/roleypoly-design/src/button/index.ts
Normal file
5
packages/roleypoly-design/src/button/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export { default as default } from './Button'
|
||||
|
||||
export {
|
||||
StyledButton
|
||||
} from './styled-components'
|
58
packages/roleypoly-design/src/button/styled-components.tsx
Normal file
58
packages/roleypoly-design/src/button/styled-components.tsx
Normal 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);
|
||||
}
|
||||
}
|
||||
`
|
34
packages/roleypoly-design/src/helpers/deep-merge.ts
Normal file
34
packages/roleypoly-design/src/helpers/deep-merge.ts
Normal 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]'
|
||||
}
|
151
packages/roleypoly-design/src/helpers/overrides.ts
Normal file
151
packages/roleypoly-design/src/helpers/overrides.ts
Normal 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
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue