mirror of
https://github.com/roleypoly/roleypoly-v1.git
synced 2025-04-26 04:29:11 +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