├── .npmignore ├── .eslintignore ├── .travis.yml ├── src ├── index.ts ├── __tests__ │ ├── index.spec.ts │ ├── __snapshots__ │ │ ├── enhance.spec.tsx.snap │ │ └── Panel.spec.tsx.snap │ ├── enhance.spec.tsx │ └── Panel.spec.tsx ├── consts.ts ├── config.ts ├── enhance.tsx ├── styles.ts ├── Panel.tsx └── Panel.md ├── styleguide ├── setup.ts ├── jss.js ├── Intro.md └── styles.css ├── test └── jestsetup.ts ├── .eslintrc ├── .gitignore ├── .editorconfig ├── tsconfig.json ├── styleguide.config.js ├── Readme.md └── package.json /.npmignore: -------------------------------------------------------------------------------- 1 | __tests__/ -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | styleguide/setup.ts 3 | styleguide-build/ 4 | lib/ 5 | coverage/* 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - node_modules 5 | node_js: 6 | - 6 7 | - 8 8 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Panel } from './Panel'; 2 | export default Panel; 3 | export { default as spaceman } from './enhance'; 4 | -------------------------------------------------------------------------------- /styleguide/setup.ts: -------------------------------------------------------------------------------- 1 | const spaceman = require('../src/enhance.tsx').default; 2 | 3 | (global as any).spaceman = spaceman; 4 | -------------------------------------------------------------------------------- /styleguide/jss.js: -------------------------------------------------------------------------------- 1 | const jss = require('jss').default; 2 | const preset = require('jss-preset-default').default; 3 | 4 | jss.setup(preset()); 5 | 6 | module.exports = jss; 7 | -------------------------------------------------------------------------------- /test/jestsetup.ts: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import * as Adapter from 'enzyme-adapter-react-16'; 3 | 4 | // React 16 Enzyme adapter 5 | configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tamia/react", 3 | "parser": "typescript-eslint-parser", 4 | "rules": { 5 | "no-undef": 0, 6 | "no-unused-vars": 0, 7 | "no-useless-constructor": 0 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | styleguide-build/ 3 | .DS_Store 4 | Thumbs.db 5 | .idea/ 6 | .vscode/ 7 | *.sublime-project 8 | *.sublime-workspace 9 | *.log 10 | .eslintcache 11 | Changelog.md 12 | lib/ 13 | coverage/ 14 | -------------------------------------------------------------------------------- /src/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import Panel, { spaceman } from '../index'; 2 | 3 | describe('API', () => { 4 | it('should export an API', () => { 5 | expect(Panel).toEqual(expect.any(Function)); 6 | expect(spaceman).toEqual(expect.any(Function)); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/consts.ts: -------------------------------------------------------------------------------- 1 | export type Size = 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl' | 'xxxl'; 2 | 3 | export const Sizes: { [index: string]: number | undefined } = { 4 | none: undefined, 5 | xs: 2, 6 | s: 4, 7 | m: 8, 8 | l: 16, 9 | xl: 32, 10 | xxl: 64, 11 | xxxl: 128, 12 | }; 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.{json,yml,md,babelrc,eslintrc,remarkrc}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /styleguide/Intro.md: -------------------------------------------------------------------------------- 1 | # React Spaceman 2 | 3 | React component to manage whitespace. 4 | 5 | *Inspired by Nathan Curtis’ article [Space in Design Systems](https://medium.com/eightshapes-llc/space-in-design-systems-188bcbae0d62).* 6 | 7 | ### Installation 8 | 9 | ```bash 10 | npm install --save-dev react-spaceman 11 | ``` 12 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | export interface ISizeItem { 2 | size: string, 3 | value: number, 4 | } 5 | 6 | export const Sizes: ISizeItem[] = [ 7 | { size: 'xxs', value: 2 }, 8 | { size: 'xs', value: 4 }, 9 | { size: 's', value: 8 }, 10 | { size: 'm', value: 16 }, 11 | { size: 'l', value: 32 }, 12 | { size: 'xl', value: 64 }, 13 | { size: 'xxl', value: 128 }, 14 | ]; 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "jsx": "react", 7 | "strict": true, 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "noUnusedLocals": true, 11 | "pretty": true, 12 | "lib": [ 13 | "dom", 14 | "esnext" 15 | ], 16 | "declaration": true 17 | }, 18 | "include": [ 19 | "src/**/*" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /styleguide.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { createConfig, css, typescript } = require('webpack-blocks'); 3 | module.exports = { 4 | sections: [ 5 | { 6 | content: path.join(__dirname, 'styleguide/Intro.md'), 7 | }, 8 | { 9 | components: 'src/[A-Z]*.tsx', 10 | }, 11 | ], 12 | propsParser: require('react-docgen-typescript').parse, 13 | require: [ 14 | path.join(__dirname, 'styleguide/styles.css'), 15 | path.join(__dirname, 'styleguide/setup.ts'), 16 | ], 17 | context: { 18 | jss: path.join(__dirname, 'styleguide/jss.js'), 19 | }, 20 | webpackConfig: createConfig([css(), typescript()]), 21 | getComponentPathLine: componentName => `import Panel from 'react-spaceman';`, 22 | showUsage: true, 23 | showSidebar: false, 24 | styleguideDir: path.join(__dirname, 'styleguide-build'), 25 | }; 26 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/enhance.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`enhance returned function should accept Spaceman props 1`] = ` 4 | 9 | hello 10 | 11 | `; 12 | 13 | exports[`enhance returned function should pass extra props to a div 1`] = ` 14 | 22 | hello 23 | 24 | `; 25 | 26 | exports[`enhance returned function should render a div 1`] = ` 27 | 31 | hello 32 | 33 | `; 34 | 35 | exports[`enhance returned function should set default Spaceman props 1`] = ` 36 | 41 | hello 42 | 43 | `; 44 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/Panel.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Panel should add a class for margin between children 1`] = ` 4 |
7 |
8 | ein 9 |
10 |
11 | zwei 12 |
13 |
14 | polizei 15 |
16 |
17 | `; 18 | 19 | exports[`Panel should add a class for margin between inline children 1`] = ` 20 | 23 | 24 | ein 25 | 26 | 27 | zwei 28 | 29 | 30 | polizei 31 | 32 | 33 | `; 34 | 35 | exports[`Panel should pass props to a custom tag 1`] = ` 36 |
40 | hello 41 |
42 | `; 43 | 44 | exports[`Panel should render a custom tag 1`] = ` 45 |
48 | hello 49 |
50 | `; 51 | 52 | exports[`Panel should render a div 1`] = ` 53 |
56 | hello 57 |
58 | `; 59 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # React Spaceman 2 | 3 | [![Build Status](https://travis-ci.org/sapegin/react-spaceman.svg)](https://travis-ci.org/sapegin/react-spaceman) 4 | 5 | React component to manage whitespace inside components and between components. 6 | 7 | *Inspired by Nathan Curtis’ article [Space in Design Systems](https://medium.com/eightshapes-llc/space-in-design-systems-188bcbae0d62).* 8 | 9 | ## Installation 10 | 11 | ```bash 12 | npm install --save-dev react-spaceman 13 | ``` 14 | 15 | ## Documentation & examples 16 | 17 | [See documentation & examples](https://sapegin.github.io/react-spaceman/). 18 | 19 | ## Changelog 20 | 21 | The changelog can be found on the [Releases page](https://github.com/sapegin/react-spaceman/releases). 22 | 23 | ## Contributing 24 | 25 | Everyone is welcome to contribute. Please take a moment to review the [contributing guidelines](Contributing.md). 26 | 27 | ## Authors and license 28 | 29 | [Artem Sapegin](http://sapegin.me) and [contributors](https://github.com/sapegin/react-spaceman/graphs/contributors). 30 | 31 | MIT License, see the included [License.md](License.md) file. 32 | -------------------------------------------------------------------------------- /src/enhance.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Size } from './consts'; 3 | import { Panel } from './Panel'; 4 | 5 | export interface IProps { 6 | below?: Size, 7 | inset?: Size, 8 | x?: Size, 9 | y?: Size, 10 | between?: Size, 11 | inline?: boolean, 12 | squish?: boolean, 13 | className?: string, 14 | children?: React.ReactNode, 15 | [key: string]: any, 16 | } 17 | 18 | export interface IDefaults { 19 | below?: Size, 20 | inset?: Size, 21 | x?: Size, 22 | y?: Size, 23 | between?: Size, 24 | inline?: boolean, 25 | squish?: boolean, 26 | className?: string, 27 | } 28 | 29 | export default function(tag: React.ReactType, defaults: IDefaults = {}) { 30 | return ({ below, inset, x, y, between, squish, className, children, ...props }: IProps) => ( 31 | 42 | {children} 43 | 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/__tests__/enhance.spec.tsx: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme'; 2 | import * as React from 'react'; 3 | import enhance from '../enhance'; 4 | 5 | describe('enhance', () => { 6 | it('should return a function', () => { 7 | const result = enhance('div'); 8 | expect(result).toEqual(expect.any(Function)); 9 | }); 10 | 11 | it('returned function should render a div', () => { 12 | const Div = enhance('div'); 13 | const wrapper = shallow(
hello
); 14 | expect(wrapper).toMatchSnapshot(); 15 | }); 16 | 17 | it('returned function should accept Spaceman props', () => { 18 | const Div = enhance('div'); 19 | const wrapper = shallow(
hello
); 20 | expect(wrapper).toMatchSnapshot(); 21 | }); 22 | 23 | it('returned function should set default Spaceman props', () => { 24 | const Div = enhance('div', { below: 'm' }); 25 | const wrapper = shallow(
hello
); 26 | expect(wrapper).toMatchSnapshot(); 27 | }); 28 | 29 | it('returned function should pass extra props to a div', () => { 30 | const Div = enhance('div'); 31 | const wrapper = shallow(
hello
); 32 | expect(wrapper).toMatchSnapshot(); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/styles.ts: -------------------------------------------------------------------------------- 1 | import { Sizes } from './config'; 2 | 3 | const prefix = 'rspcmn'; 4 | 5 | let notAttachedCss = ''; 6 | 7 | export type ClassMap = { [index: string]: string }; 8 | 9 | export function addClass(elem: string, fn: (name: string) => string): string { 10 | const name = `${prefix}-${elem}`; 11 | const css = fn(`.${name}`); 12 | 13 | notAttachedCss += `${css}\n`; 14 | 15 | return name; 16 | } 17 | 18 | export function addClassesForAllSizes( 19 | elem: string, 20 | fn: (name: string, value: number, size: string) => string 21 | ): ClassMap { 22 | const classes: ClassMap = {}; 23 | const css = Sizes.map(({ size, value }) => { 24 | const name = `${prefix}-${elem}-${size}`; 25 | classes[size] = name; 26 | return fn(`.${name}`, value, size); 27 | }).join('\n'); 28 | 29 | notAttachedCss += `${css}\n`; 30 | 31 | return classes; 32 | } 33 | 34 | export function attachAllStyles() { 35 | if (typeof document === 'undefined') { 36 | return; 37 | } 38 | 39 | // Create