mirror of
https://github.com/roleypoly/roleypoly-v1.git
synced 2025-04-25 04:09:12 +00:00
[design]: initialize design system
This commit is contained in:
parent
6fb39d6c4d
commit
b864df9393
14 changed files with 391 additions and 0 deletions
3
packages/roleypoly-design/.gitignore
vendored
Normal file
3
packages/roleypoly-design/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
lib
|
||||
node_modules
|
||||
*.log
|
2
packages/roleypoly-design/.storybook/addons.js
Normal file
2
packages/roleypoly-design/.storybook/addons.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import '@storybook/addon-actions/register';
|
||||
import '@storybook/addon-links/register';
|
8
packages/roleypoly-design/.storybook/config.js
Normal file
8
packages/roleypoly-design/.storybook/config.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { configure } from '@storybook/react'
|
||||
const req = require.context('../src', true, /\.stor\bies|y\b\.[tj]sx?$/)
|
||||
|
||||
function loadStories() {
|
||||
req.keys().forEach(req)
|
||||
}
|
||||
|
||||
configure(loadStories, module)
|
16
packages/roleypoly-design/.storybook/webpack.config.js
Normal file
16
packages/roleypoly-design/.storybook/webpack.config.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
module.exports = ({ config }) => {
|
||||
config.module.rules.push({
|
||||
test: /\.(ts|tsx)$/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('awesome-typescript-loader'),
|
||||
},
|
||||
// Optional
|
||||
{
|
||||
loader: require.resolve('react-docgen-typescript-loader'),
|
||||
},
|
||||
],
|
||||
});
|
||||
config.resolve.extensions.push('.ts', '.tsx');
|
||||
return config;
|
||||
};
|
9
packages/roleypoly-design/.stylelintrc
Normal file
9
packages/roleypoly-design/.stylelintrc
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"processors": [
|
||||
"stylelint-processor-styled-components"
|
||||
],
|
||||
"extends": [
|
||||
"stylelint-config-recommended",
|
||||
"stylelint-config-styled-components"
|
||||
]
|
||||
}
|
42
packages/roleypoly-design/package.json
Normal file
42
packages/roleypoly-design/package.json
Normal file
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"private": true,
|
||||
"name": "@roleypoly/design",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"styled-components": "^4.2.0"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.4",
|
||||
"@storybook/addon-actions": "^5.0.11",
|
||||
"@storybook/addon-info": "^5.0.11",
|
||||
"@storybook/addon-knobs": "^5.0.11",
|
||||
"@storybook/addon-links": "^5.0.11",
|
||||
"@storybook/addons": "^5.0.11",
|
||||
"@storybook/react": "^5.0.11",
|
||||
"@types/jest": "^24.0.13",
|
||||
"@types/node": "^12.0.2",
|
||||
"@types/react": "^16.8.17",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/storybook__addon-actions": "^3.4.2",
|
||||
"@types/storybook__addon-knobs": "^5.0.0",
|
||||
"@types/storybook__addon-links": "^3.3.4",
|
||||
"@types/storybook__react": "^4.0.1",
|
||||
"@types/styled-components": "4.1.8",
|
||||
"awesome-typescript-loader": "^5.2.1",
|
||||
"babel-loader": "^8.0.6",
|
||||
"react-docgen-typescript-loader": "^3.1.0",
|
||||
"stylelint": "^10.0.1",
|
||||
"tslint": "^5.16.0",
|
||||
"tslint-config-standard": "^8.0.1",
|
||||
"typescript": "^3.4.5"
|
||||
},
|
||||
"scripts": {
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook"
|
||||
}
|
||||
}
|
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
|
||||
)
|
||||
}
|
||||
}
|
38
packages/roleypoly-design/tsconfig.json
Normal file
38
packages/roleypoly-design/tsconfig.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./lib",
|
||||
"module": "commonjs",
|
||||
"target": "es2018",
|
||||
"lib": [
|
||||
"es5",
|
||||
"es6",
|
||||
"es7",
|
||||
"es2017",
|
||||
"dom"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"allowJs": false,
|
||||
"jsx": "react",
|
||||
"moduleResolution": "node",
|
||||
"rootDirs": [
|
||||
"src"
|
||||
],
|
||||
"baseUrl": "src",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"noUnusedLocals": true,
|
||||
"declaration": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"build",
|
||||
"scripts"
|
||||
]
|
||||
}
|
3
packages/roleypoly-design/tslint.json
Normal file
3
packages/roleypoly-design/tslint.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "tslint-config-standard"
|
||||
}
|
Loading…
Add table
Reference in a new issue