Repository: nice-boys/components
Branch: master
Commit: d0dd86cb5018
Files: 46
Total size: 32.8 KB
Directory structure:
gitextract_2wa2j_48/
├── .github/
│ └── workflows/
│ └── main.yml
├── .gitignore
├── .npmignore
├── .prettierrc
├── .storybook/
│ ├── addons.js
│ ├── config.js
│ └── webpack.config.js
├── .yarnclean
├── README.md
├── now.json
├── package.json
├── src/
│ ├── BaseStyles/
│ │ ├── BaseStyles.stories.tsx
│ │ └── index.tsx
│ ├── Button/
│ │ ├── Button.stories.tsx
│ │ ├── Danger.tsx
│ │ ├── Ghost.tsx
│ │ ├── Join.tsx
│ │ ├── Outline.tsx
│ │ ├── Primary.tsx
│ │ ├── Tab.tsx
│ │ ├── Text.tsx
│ │ ├── base.tsx
│ │ └── index.tsx
│ ├── Card/
│ │ ├── Card.stories.tsx
│ │ ├── Card.tsx
│ │ └── index.tsx
│ ├── Nav/
│ │ ├── Nav.stories.tsx
│ │ ├── Nav.tsx
│ │ ├── components/
│ │ │ ├── Content.tsx
│ │ │ ├── Item.tsx
│ │ │ ├── Navbar.tsx
│ │ │ └── Wrapper.tsx
│ │ └── index.tsx
│ ├── Spinner/
│ │ ├── Donut.tsx
│ │ ├── Spinner.stories.tsx
│ │ ├── Spinner.tsx
│ │ └── index.tsx
│ ├── Tooltip/
│ │ ├── Tooltip.stories.tsx
│ │ ├── Tooltip.tsx
│ │ └── index.tsx
│ ├── index.ts
│ └── theme.ts
├── tsconfig.json
└── types/
├── @storybook/
│ └── addon-actions.d.ts
├── react-storybook-addons-props-combinations.d.ts
└── styled-normalize.d.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/main.yml
================================================
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@master
- name: Install Node.js 12.x
uses: actions/setup-node@master
with:
node-version: 12.x
- uses: actions/cache@preview
with:
path: ~/.cache/yarn
key: ${{ runner.os }}-yarn-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Lint
run: yarn run lint
- name: Typecheck
run: yarn tsc
- name: Build
run: yarn run build
================================================
FILE: .gitignore
================================================
node_modules
.DS_Store
.vscode
dist
.rts2_cache_*
.storybook-dist
================================================
FILE: .npmignore
================================================
.storybook
node_modules
.DS_Store
.vscode
.rts2_cache_*
.storybook-dist
now.json
================================================
FILE: .prettierrc
================================================
{}
================================================
FILE: .storybook/addons.js
================================================
import "@storybook/addon-actions/register";
import "@storybook/addon-links/register";
import "@storybook/addon-storysource/register";
import "@storybook/addon-viewport/register";
================================================
FILE: .storybook/config.js
================================================
import React from "react";
import prettyFormat from "pretty-format";
import { configure, addDecorator } from "@storybook/react";
import { setDefaults } from "react-storybook-addon-props-combinations";
import "@storybook/addon-console";
import { BaseStyles } from "../src";
addDecorator(storyFn => (
<>
{storyFn()}
>
));
// automatically import all files ending in *.stories.js
const req = require.context("../src", true, /\.stories\.(js|tsx?)$/);
function loadStories() {
req.keys().forEach(filename => req(filename));
}
setDefaults({
CombinationRenderer: ({ Component, props }) => {
const el = ;
return (
{el}
{prettyFormat(el, {
plugins: [prettyFormat.plugins.ReactElement]
})}
);
}
});
configure(loadStories, module);
================================================
FILE: .storybook/webpack.config.js
================================================
module.exports = ({ config, mode }) => {
config.module.rules.push({
test: /\.tsx?$/,
use: [
{
loader: require.resolve("babel-loader"),
options: {
presets: [["react-app", { flow: false, typescript: true }]]
}
}
]
});
config.resolve.extensions.push(".ts", ".tsx");
config.module.rules.push({
test: /\.stories\.tsx?$/,
loaders: [
{
loader: require.resolve("@storybook/addon-storysource/loader"),
options: { parser: "typescript" }
}
],
enforce: "pre"
});
return config;
};
================================================
FILE: .yarnclean
================================================
@types/react-native
================================================
FILE: README.md
================================================
# `@nice-boys/components`
Our React component library to quickly plug together products. Used in conjunction with our [product-boilerplate](https://github.com/nice-boys/product-boilerplate).
## Documentation
**[Visit the Storybook for examples of all components](https://components.nice-boys.now.sh)**
This component library is based on:
- [styled-components](https://styled-components.com)
- [styled-system](https://github.com/jxnblk/styled-system)
- [rebass](https://rebassjs.org)
Recommended usage is with [rebass](https://rebassjs.org), this library does not re-export those core primitives (Flex, Box, etc).
---
## Contributing
### Local development
We use Storybook to develop the component library locally. To start Storybook locally, run:
```
yarn run dev
```
Any `.stories.js` file will be picked up by Storybook. See their doc on [how to write stories](https://storybook.js.org/docs/basics/writing-stories/) to learn more.
### Using your local version in an app
There are three steps to use your local version of `@nice-boys/components` in an app for development:
1. **Link `@nice-boys/components` globally**: Run `yarn link` in the `@nice-boys/components` directory to link the package globally (you only have to do that once on your machine)
2. **Tell your app to use your local version of `@nice-boys/components`**: Run `yarn link @nice-boys/components` in your app directory that depends on the package (you only have to do this once per app)
3. **Rebuild the components on any change**: Run `yarn run build:watch` in the components dir to make sure the library rebuilds every time you change/add/remove a component
That's it!
| @nice-boys/components | yourapp |
| --------------------- | ------------------------------- |
| yarn link | yarn link @nice-boys/components |
| yarn run build:watch | yarn run dev |
If you want to use the remote version in your app again, simply run `yarn unlink @nice-boys/components` in the app directory.
## License
Licensed under the MIT license. Copyright © 2019 Max Stoiber
================================================
FILE: now.json
================================================
{
"version": 2,
"name": "@nice-boys/components",
"alias": "components.nice-boys.now.sh",
"scope": "nice-boys",
"builds": [
{
"src": "package.json",
"use": "@now/static-build",
"config": { "distDir": ".storybook-dist" }
}
]
}
================================================
FILE: package.json
================================================
{
"name": "@nice-boys/components",
"version": "0.2.0",
"description": "Nice Boys component library",
"source": "src/index.ts",
"main": "dist/nice-boys-components.js",
"umd:main": "dist/nice-boys-components.umd.js",
"types": "dist/index.d.ts",
"repository": "https://github.com/nice-boys/components",
"author": "Max Stoiber",
"license": "MIT",
"private": false,
"scripts": {
"dev": "start-storybook -p 6006",
"build:watch": "microbundle watch --jsx React.createElement",
"build": "microbundle --jsx React.createElement",
"lint": "eslint src --ext .js,.ts,.tsx",
"test": "yarn run lint",
"now-build": "build-storybook -c .storybook -o .storybook-dist",
"prepublishOnly": "yarn run build"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"eslintConfig": {
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
"prettier/@typescript-eslint",
"react-app"
],
"plugins": [
"react-hooks"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-member-accessibility": 0,
"@typescript-eslint/no-empty-interface": 0,
"@typescript-eslint/no-var-requires": 0,
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
},
"lint-staged": {
"*.{js,ts,tsx}": [
"eslint --fix",
"prettier --write",
"git add"
],
"*.{css,json,md,mdx}": [
"prettier --write",
"git add"
]
},
"peerDependencies": {
"react": ">= 15.0.0",
"styled-components": ">= 4.0.0"
},
"devDependencies": {
"@babel/core": "^7.4.5",
"@storybook/addon-actions": "5.0.11",
"@storybook/addon-console": "^1.1.0",
"@storybook/addon-info": "5.0.11",
"@storybook/addon-links": "5.0.11",
"@storybook/addon-storysource": "5.0.11",
"@storybook/addon-viewport": "5.0.11",
"@storybook/addons": "5.0.11",
"@storybook/react": "5.0.11",
"@types/react": "^16.8.19",
"@types/rebass": "^3.0.4",
"@types/storybook__react": "^4.0.1",
"@types/styled-components": "^4.4.0",
"@types/styled-system": "^4.2.1",
"@typescript-eslint/eslint-plugin": "^1.6.0",
"@typescript-eslint/parser": "^1.6.0",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.6",
"eslint": "^5.16.0",
"eslint-config-prettier": "^6.11.0",
"eslint-config-react-app": "^3.0.8",
"eslint-loader": "3.0.2",
"eslint-plugin-flowtype": "2.50.1",
"eslint-plugin-import": "2.14.0",
"eslint-plugin-jsx-a11y": "6.1.2",
"eslint-plugin-react": "7.16.0",
"eslint-plugin-react-hooks": "^1.6.0",
"husky": "^3.1.0",
"lint-staged": "^9.4.3",
"microbundle": "^0.11.0",
"prettier": "^1.17.1",
"react": "^16.8.6",
"react-docgen-typescript-loader": "^3.1.0",
"react-storybook-addon-props-combinations": "^1.1.0",
"styled-components": "^4.2.0"
},
"dependencies": {
"@tippy.js/react": "^2.2.0",
"react-feather": "^2.0.3",
"rebass": "^3.x.x",
"styled-normalize": "^8.x.x",
"styled-system": "^4.x.x"
}
}
================================================
FILE: src/BaseStyles/BaseStyles.stories.tsx
================================================
import React from "react";
import { storiesOf } from "@storybook/react";
import { BaseStyles } from "./";
storiesOf("BaseStyles", module).add("Global", () => (
<>
This component sets the base styles, including normalize.css (see the grey
background and no margin of the whole page)
>
));
================================================
FILE: src/BaseStyles/index.tsx
================================================
import { createGlobalStyle } from "styled-components";
import normalize from "styled-normalize";
import { theme } from "../theme";
export const BaseStyles = createGlobalStyle`
${normalize}
html {
font-size: 16px;
line-height: 1.5;
background-color: ${theme.ui.wash};
color: ${theme.text.primary};
padding: 0;
margin: 0;
-webkit-font-smoothing: antialiased;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial,
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
}
html,
body,
#__next {
height: 100%;
}
`;
================================================
FILE: src/Button/Button.stories.tsx
================================================
import { storiesOf } from "@storybook/react";
import withPropsCombinations from "react-storybook-addon-props-combinations";
import { action } from "@storybook/addon-actions";
import {
PrimaryButton,
DangerButton,
GhostButton,
JoinButton,
OutlineButton,
TabButton,
TextButton
} from "./";
storiesOf("Buttons", module)
.add(
"Primary",
withPropsCombinations(PrimaryButton, {
children: ["Click me"],
size: ["small", undefined, "large"],
disabled: [true, false],
onClick: [action("click")]
})
)
.add(
"Ghost",
withPropsCombinations(GhostButton, {
children: ["Potential click"],
size: ["small", undefined, "large"],
disabled: [true, false],
onClick: [action("click")]
})
)
.add(
"Outline",
withPropsCombinations(OutlineButton, {
children: ["Potential click"],
size: ["small", undefined, "large"],
disabled: [true, false],
onClick: [action("click")]
})
)
.add(
"Text",
withPropsCombinations(TextButton, {
children: ["Potential click"],
size: ["small", undefined, "large"],
disabled: [true, false],
onClick: [action("click")]
})
)
.add(
"Tab",
withPropsCombinations(TabButton, {
children: ["Potential click"],
size: ["small", undefined, "large"],
disabled: [true, false],
onClick: [action("click")]
})
)
.add(
"Join",
withPropsCombinations(JoinButton, {
children: ["Join"],
size: ["small", undefined, "large"],
disabled: [true, false],
onClick: [action("click")]
})
)
.add(
"Danger",
withPropsCombinations(DangerButton, {
children: ["Careful"],
size: ["small", undefined, "large"],
disabled: [true, false],
onClick: [action("click")]
})
);
================================================
FILE: src/Button/Danger.tsx
================================================
import React from "react";
import styled from "styled-components";
import { theme } from "../theme";
import {
styles,
getPaddingFromSize,
getFontSizeFromSize,
ButtonProps
} from "./base";
const Button = styled.button`
${styles.base};
background: linear-gradient(
to top,
${theme.accent.error.seven},
${theme.accent.error.six}
);
border: 1px solid ${theme.accent.error.seven};
color: ${theme.lights.six};
&:hover {
${styles.hover};
background: linear-gradient(
to top,
${theme.accent.error.six},
${theme.accent.error.five}
);
}
&:active {
${styles.active};
background: linear-gradient(
to top,
${theme.accent.error.five},
${theme.accent.error.six}
);
}
&:focus {
${styles.focus};
box-shadow: 0 0 0 1px ${theme.ui.wash},
0 0 0 3px ${theme.accent.error.border};
}
`;
export const DangerButton = (props: ButtonProps) => {
const { size } = props;
const { py, px } = getPaddingFromSize(size);
const fontSize = getFontSizeFromSize(size);
return ;
};
================================================
FILE: src/Button/Ghost.tsx
================================================
import React from "react";
import styled from "styled-components";
import { theme } from "../theme";
import {
styles,
getPaddingFromSize,
getFontSizeFromSize,
ButtonProps
} from "./base";
const Button = styled.button`
${styles.base};
background: linear-gradient(
to top,
${theme.lights.five},
${theme.lights.six}
);
color: ${theme.text.tertiary};
border: 1px solid ${theme.lights.three};
&:hover {
${styles.hover};
background: linear-gradient(
to top,
${theme.lights.five},
${theme.lights.six}
);
color: ${theme.text.primary};
border: 1px solid ${theme.lights.three};
}
&:active {
${styles.active};
background: linear-gradient(
to top,
${theme.lights.six},
${theme.lights.five}
);
border: 1px solid ${theme.lights.three};
}
&:focus {
${styles.focus};
box-shadow: 0 0 0 1px ${theme.lights.six}, 0 0 0 3px ${theme.lights.three};
border: 1px solid ${theme.lights.four};
}
`;
export const GhostButton = (props: ButtonProps) => {
const { size } = props;
const { py, px } = getPaddingFromSize(size);
const fontSize = getFontSizeFromSize(size);
return ;
};
================================================
FILE: src/Button/Join.tsx
================================================
import React from "react";
import styled from "styled-components";
import { theme } from "../theme";
import {
styles,
getPaddingFromSize,
getFontSizeFromSize,
ButtonProps
} from "./base";
const Button = styled.button`
${styles.base};
background: linear-gradient(
to top,
${theme.accent.success.seven},
${theme.accent.success.six}
);
border: 1px solid ${theme.accent.success.seven};
color: ${theme.lights.six};
&:hover {
${styles.hover};
background: linear-gradient(
to top,
${theme.accent.success.six},
${theme.accent.success.five}
);
}
&:active {
${styles.active};
background: linear-gradient(
to top,
${theme.accent.success.five},
${theme.accent.success.six}
);
}
&:focus {
${styles.focus};
box-shadow: 0 0 0 1px ${theme.ui.wash},
0 0 0 3px ${theme.accent.success.border};
}
`;
export const JoinButton = (props: ButtonProps) => {
const { size } = props;
const { py, px } = getPaddingFromSize(size);
const fontSize = getFontSizeFromSize(size);
return ;
};
================================================
FILE: src/Button/Outline.tsx
================================================
import React from "react";
import styled from "styled-components";
import { theme } from "../theme";
import {
styles,
getPaddingFromSize,
getFontSizeFromSize,
ButtonProps
} from "./base";
const Button = styled.button`
${styles.base};
background: none;
border: 1px solid ${theme.lights.three};
color: ${theme.text.tertiary};
box-shadow: none;
&:hover {
${styles.hover};
color: ${theme.text.primary};
border: 1px solid ${theme.lights.two};
background: none;
box-shadow: none;
}
&:active {
${styles.active};
border: 1px solid ${theme.lights.two};
background: none;
}
&:focus {
${styles.focus};
border: 1px solid ${theme.lights.two};
box-shadow: 0 0 0 1px ${theme.ui.wash}, 0 0 0 3px ${theme.lights.three};
}
`;
export const OutlineButton = (props: ButtonProps) => {
const { size } = props;
const { py, px } = getPaddingFromSize(size);
const fontSize = getFontSizeFromSize(size);
return ;
};
================================================
FILE: src/Button/Primary.tsx
================================================
import React from "react";
import styled from "styled-components";
import { theme } from "../theme";
import {
styles,
getPaddingFromSize,
getFontSizeFromSize,
ButtonProps
} from "./base";
const Button = styled.button`
${styles.base};
background: linear-gradient(to top, ${theme.brand.seven}, ${theme.brand.six});
border: 1px solid ${theme.brand.seven};
color: ${theme.lights.six};
&:hover {
${styles.hover};
background: linear-gradient(
to top,
${theme.brand.six},
${theme.brand.five}
);
}
&:active {
${styles.active};
background: linear-gradient(
to top,
${theme.brand.five},
${theme.brand.six}
);
}
&:focus {
${styles.focus};
box-shadow: 0 0 0 1px ${theme.ui.wash}, 0 0 0 3px ${theme.brand.border};
}
`;
export const PrimaryButton = (props: ButtonProps) => {
const { size } = props;
const { py, px } = getPaddingFromSize(size);
const fontSize = getFontSizeFromSize(size);
return ;
};
================================================
FILE: src/Button/Tab.tsx
================================================
import React from "react";
import styled from "styled-components";
import { theme } from "../theme";
import {
styles,
getPaddingFromSize,
getFontSizeFromSize,
ButtonProps
} from "./base";
const Button = styled.button`
${styles.base};
background: linear-gradient(to top, ${theme.darks.zero}, ${theme.darks.one});
border: 1px solid ${theme.darks.zero};
color: ${theme.lights.five};
&:hover {
${styles.hover};
color: ${theme.lights.six};
background: linear-gradient(
to top,
${theme.darks.zero},
${theme.darks.one}
);
}
&:active {
${styles.active};
background: linear-gradient(
to top,
${theme.darks.zero},
${theme.darks.zero}
);
}
&:focus {
${styles.focus};
box-shadow: 0 0 0 1px ${theme.ui.wash}, 0 0 0 3px ${theme.lights.one};
}
`;
export const TabButton = (props: ButtonProps) => {
const { size } = props;
const { py, px } = getPaddingFromSize(size);
const fontSize = getFontSizeFromSize(size);
return ;
};
================================================
FILE: src/Button/Text.tsx
================================================
import React from "react";
import styled from "styled-components";
import { theme } from "../theme";
import {
styles,
getPaddingFromSize,
getFontSizeFromSize,
ButtonProps
} from "./base";
const Button = styled.button`
${styles.base};
background: none;
color: ${theme.brand.six};
box-shadow: none;
padding-left: 0;
padding-right: 0;
&:hover {
${styles.hover};
color: ${theme.brand.nine};
background: none;
box-shadow: none;
}
&:active {
${styles.active};
background: none;
}
&:focus {
${styles.focus};
box-shadow: 0 0 0 1px ${theme.ui.wash}, 0 0 0 3px ${theme.lights.three};
}
`;
export const TextButton = (props: ButtonProps) => {
const { size } = props;
const { py, px } = getPaddingFromSize(size);
const fontSize = getFontSizeFromSize(size);
return ;
};
================================================
FILE: src/Button/base.tsx
================================================
import React, { ButtonHTMLAttributes } from "react";
import { css } from "styled-components";
import {
space,
width,
style,
getPx,
SpaceProps,
WidthProps,
FontSizeProps
} from "styled-system";
import { theme } from "../theme";
export interface ButtonProps
extends ButtonHTMLAttributes,
SpaceProps,
WidthProps,
FontSizeProps {
// href?: string;
children: React.ReactNode;
size?: "large" | "small";
}
export interface PaddingType {
py: number;
px: number;
}
export const getPaddingFromSize = (size: ButtonProps["size"]): PaddingType => {
const defaults = { py: 2, px: 3 };
switch (size) {
case "small":
return { py: 1, px: 2 };
case "large":
return { py: 3, px: 5 };
default:
return defaults;
}
};
export const getFontSizeFromSize = (size: ButtonProps["size"]): number => {
switch (size) {
case "small":
return 0;
case "large":
return 2;
default:
return 1;
}
};
const fontSize = style({
// React prop name
prop: "fontSize",
// The corresponding CSS property (defaults to prop argument)
cssProperty: "fontSize",
// key for theme values
key: "fontSizes",
// accessor function for transforming the value
transformValue: getPx,
// add a fallback scale object or array, if theme is not present
scale: [13, 15, 17, 20, 24]
});
const focus = css`
box-shadow: 0 0 0 1px ${theme.lights.six}, 0 0 0 3px ${theme.ui.border};
outline: none;
`;
const active = css`
border: 1px solid transparent;
`;
const hover = css`
box-shadow: ${props => (props.disabled ? "none" : theme.shadow.medium)};
transition: all ${theme.animation.in};
`;
const base = css`
${space};
${width};
${fontSize};
-webkit-appearance: none;
white-space: nowrap;
word-break: keep-all;
cursor: pointer;
line-height: 1;
position: relative;
text-align: center;
background: linear-gradient(
to top,
${theme.lights.five},
${theme.lights.six}
);
border: 1px solid transparent;
box-shadow: ${props => (props.disabled ? "none" : theme.shadow.small)};
border-radius: 4px;
transition: all ${theme.animation.out};
opacity: ${props => (props.disabled ? "0.64" : "1")};
font-weight: 500;
text-decoration: none;
display: inline-block;
&:hover {
${hover};
}
&:disabled {
cursor: not-allowed;
}
&:active {
${active}
}
&:focus {
${focus};
}
`;
export const styles = {
base,
focus,
active,
hover
};
================================================
FILE: src/Button/index.tsx
================================================
export * from "./Primary";
export * from "./Danger";
export * from "./Ghost";
export * from "./Join";
export * from "./Outline";
export * from "./Tab";
export * from "./Text";
================================================
FILE: src/Card/Card.stories.tsx
================================================
import React from "react";
import { storiesOf } from "@storybook/react";
import { Card, CardTitle } from "./";
import { Flex } from "rebass";
storiesOf("Card", module).add("Default", () => (
This is a card!
));
================================================
FILE: src/Card/Card.tsx
================================================
import React from "react";
import styled from "styled-components";
import { Flex, Heading, HeadingProps } from "rebass";
import { theme } from "../theme";
const Card = styled(Flex).attrs(props => ({
p: props.p || 3,
flexDirection: props.flexDirection || "column"
}))`
border-radius: 4px;
background-color: ${theme.ui.cardWash};
box-shadow: ${theme.shadow.large};
`;
const CardTitle = (props: HeadingProps) => (
);
export { Card, CardTitle };
================================================
FILE: src/Card/index.tsx
================================================
export * from "./Card";
================================================
FILE: src/Nav/Nav.stories.tsx
================================================
import React from "react";
import { Flex, Box } from "rebass";
import { storiesOf } from "@storybook/react";
import { Nav } from "./Nav";
import { PrimaryButton } from "../Button";
const ChangefeedNav = () => (
Navbar
);
storiesOf("Nav", module)
.add("Desktop", ChangefeedNav)
.add("Mobile", ChangefeedNav, { viewport: { defaultViewport: "iphonex" } });
================================================
FILE: src/Nav/Nav.tsx
================================================
import React from "react";
import { Wrapper } from "./components/Wrapper";
import { Navbar } from "./components/Navbar";
import { Item } from "./components/Item";
import { Content } from "./components/Content";
interface Props {
children: React.ReactNode;
}
const Nav = (props: Props) => {
return (
{props.children}
);
};
Nav.Item = Item;
Nav.Content = Content;
export { Nav };
================================================
FILE: src/Nav/components/Content.tsx
================================================
import React from "react";
import styled from "styled-components";
import { Flex, FlexProps } from "rebass";
import { defaultBreakpoints } from "styled-system";
import { ChevronDown } from "react-feather";
import { theme } from "../../theme";
const Mobile = styled(Flex)`
display: none;
@media screen and (max-width: ${defaultBreakpoints[0]}) {
display: flex;
}
`;
const Desktop = styled(Flex)`
@media screen and (max-width: ${defaultBreakpoints[0]}) {
display: none;
}
`;
const MobileExpandedWrapper = styled(Flex)`
position: absolute;
top: 100%;
width: 100%;
height: 100vh;
left: 0;
background: rgba(0, 0, 0, 0.1);
`;
const MobileExpandedCard = styled(Flex)`
position: absolute;
background: white;
top: 0;
`;
const UnstyledButton = styled.button`
padding: 0;
margin: 0;
background: none;
border: none;
outline: none;
`;
export const Content = (props: FlexProps) => {
const [isOpen, setIsOpen] = React.useState(false);
return (
<>
setIsOpen(!isOpen)}>
{isOpen && (
setIsOpen(false)}>
{props.children}
)}
>
);
};
================================================
FILE: src/Nav/components/Item.tsx
================================================
import React from "react";
import styled from "styled-components";
import { Box, LinkProps, Flex } from "rebass";
import { theme } from "../../theme";
import { defaultBreakpoints } from "styled-system";
import { ChevronRight } from "react-feather";
const Mobile = styled(Box)`
display: none;
@media screen and (max-width: ${defaultBreakpoints[0]}) {
display: flex;
}
`;
const Link = styled(Flex).attrs(props => ({
as: props.as || "a"
}))`
color: ${theme.darks.zero};
text-decoration: none;
background-color: transparent;
transition: background 100ms ease-out;
padding: 8px 16px;
border-radius: 4px;
line-height: normal;
cursor: pointer;
:hover {
background-color: ${theme.lights.four};
}
:active,
:focus {
color: ${theme.darks.zero};
}
@media screen and (max-width: ${defaultBreakpoints[0]}) {
border-bottom: 1px solid ${theme.ui.border};
padding: 16px 0;
margin: 0px 16px;
svg {
transition: transform 100ms ease-in-out;
}
&:hover {
background-color: transparent;
}
&:hover svg {
transform: translateX(2px);
}
&:last-of-type {
border-bottom: none;
}
}
`;
export const Item = (props: LinkProps) => {
return (
// @ts-ignore
{props.children}
);
};
================================================
FILE: src/Nav/components/Navbar.tsx
================================================
import styled from "styled-components";
import { Flex } from "rebass";
import { theme } from "../../theme";
export const Navbar = styled(Flex).attrs({
flexDirection: "row",
alignItems: "center",
p: 3
})({
backgroundColor: "white",
boxShadow: theme.shadow.large,
position: "fixed",
top: 0,
left: 0,
right: 0,
zIndex: 99
});
================================================
FILE: src/Nav/components/Wrapper.tsx
================================================
import styled from "styled-components";
import { Flex } from "rebass";
export const Wrapper = styled(Flex)({ position: "relative", height: "65px" });
================================================
FILE: src/Nav/index.tsx
================================================
export * from "./Nav";
================================================
FILE: src/Spinner/Donut.tsx
================================================
import styled, { keyframes } from "styled-components";
import { Box } from "rebass";
import { theme } from "../theme";
const donutSpin = keyframes`
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
`;
export default styled(Box)`
animation: ${donutSpin} 800ms linear infinite;
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: ${theme.text.primary};
border-radius: 50%;
height: 30px;
width: 30px;
`;
================================================
FILE: src/Spinner/Spinner.stories.tsx
================================================
import React from "react";
import { storiesOf } from "@storybook/react";
import { Spinner } from "./";
storiesOf("Spinner", module).add("Default", () => );
================================================
FILE: src/Spinner/Spinner.tsx
================================================
import React from "react";
import { Flex, FlexProps } from "rebass";
import Donut from "./Donut";
export const Spinner = React.forwardRef((props: FlexProps, ref) => (
));
================================================
FILE: src/Spinner/index.tsx
================================================
export * from "./Spinner";
================================================
FILE: src/Tooltip/Tooltip.stories.tsx
================================================
import React from "react";
import { storiesOf } from "@storybook/react";
import withPropsCombinations from "react-storybook-addon-props-combinations";
import { Tooltip } from "./Tooltip";
import { PrimaryButton } from "../Button";
storiesOf("Tooltip", module).add(
"Default",
withPropsCombinations(Tooltip, {
children: [Button],
placement: ["top", "bottom", "left", "right"],
content: ["Tooltip text"]
})
);
================================================
FILE: src/Tooltip/Tooltip.tsx
================================================
import React from "react";
import Tippy from "@tippy.js/react";
interface TooltipProps {
content: string | React.ReactNode;
placement?: "top" | "bottom" | "left" | "right";
style?: object;
children: React.ReactElement;
}
export const Tooltip = (props: TooltipProps) => {
const { style = {}, placement = "top", content, children, ...rest } = props;
return (
{content}
}
// https://github.com/FezVrasta/popper.js/issues/535
popperOptions={{
modifiers: {
preventOverflow: {
boundariesElement: "window"
}
}
}}
{...rest}
>
{children}
);
};
================================================
FILE: src/Tooltip/index.tsx
================================================
export * from "./Tooltip";
================================================
FILE: src/index.ts
================================================
export * from "./Button";
export * from "./theme";
export * from "./BaseStyles";
export * from "./Tooltip";
export * from "./Spinner";
export * from "./Nav";
export * from "./Card";
================================================
FILE: src/theme.ts
================================================
const darks = {
zero: "#000",
one: "#161718",
two: "#2C2E30",
three: "#44474A",
four: "#5B5F63",
five: "#73787D",
six: "#8A9096",
seven: "#A1A9B0"
};
const lights = {
one: "#BBC2C9",
two: "#D3DBE3",
three: "#E9ECF0",
four: "#F2F5F7",
five: "#FAFBFC",
six: "#FFF"
};
const brand = {
one: "#D4E8FC",
two: "#ABD3FC",
three: "#83BFFC",
four: "#5BABFC",
five: "#1A8CFF",
six: "#007EFC",
seven: "#0071E3",
eight: "#0065C9",
nine: "#0058B0",
ten: "#004B96",
eleven: "#003E7D"
};
const error = {
one: "#FFE3E3",
two: "#F7C3C3",
three: "#F09C9C",
four: "#E87171",
five: "#F05151",
six: "#E84444",
seven: "#DF4040",
eight: "#B53535",
nine: "#9C2E2E",
ten: "#822626",
eleven: "#691F1F"
};
const success = {
one: "#E4F5EC",
two: "#C5EBD6",
three: "#A2DBBD",
four: "#75D9A3",
five: "#5BCF90",
six: "#44C881",
seven: "#40BD79",
eight: "#32945F",
nine: "#2D8556",
ten: "#28754C",
eleven: "#21613F"
};
const warn = {
one: "#FFF0D4",
two: "#FFE2AB",
three: "#FFD687",
four: "#FFCE6E",
five: "#FFC554",
six: "#FFBC3B",
seven: "#E5A935",
eight: "#CC972F",
nine: "#B28429",
ten: "#996C14",
eleven: "#66480D"
};
export const theme = {
darks,
lights,
brand: {
...brand,
default: brand.six,
text: brand.eleven,
wash: brand.one,
border: brand.two
},
text: {
primary: darks.one,
secondary: darks.two,
tertiary: darks.three
},
ui: {
wash: lights.four,
cardWash: lights.five,
border: lights.three
},
accent: {
error: {
...error,
default: error.six,
text: error.eleven,
wash: error.one,
border: error.two
},
success: {
...success,
default: success.six,
text: success.eleven,
wash: success.one,
border: success.two
},
warn: {
...warn,
default: warn.six,
text: warn.eleven,
wash: warn.one,
border: warn.two
}
},
shadow: {
small: "0px 1px 2px rgba(0, 0, 0, 0.02)",
medium: "0px 1px 4px rgba(0, 0, 0, 0.04)",
large: "0px 1px 8px rgba(0, 0, 0, 0.08)"
},
animation: {
in: "0.2s ease-in",
out: "0.2s ease-out"
}
};
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"lib": ["dom", "es2017"],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noImplicitThis": true,
"preserveConstEnums": true,
"removeComments": false,
"sourceMap": true,
"strict": true,
"target": "esnext"
}
}
================================================
FILE: types/@storybook/addon-actions.d.ts
================================================
declare module "@storybook/addon-actions";
================================================
FILE: types/react-storybook-addons-props-combinations.d.ts
================================================
declare module "react-storybook-addon-props-combinations";
================================================
FILE: types/styled-normalize.d.ts
================================================
declare module "styled-normalize" {
const normalize: string;
export default normalize;
}