Browse Source

Merge pull request #491 from Gamaranto/style-reducers

Style reducers and Buttons.
Bedeho Mender 4 years ago
parent
commit
98e6789fe5

+ 0 - 0
packages/components/.eslintrc.js → .eslintrc.js


+ 2 - 2
.prettierrc

@@ -1,4 +1,4 @@
 {
-	"semi": false,
-	"trailingComma": "none"
+	"semi": true,
+	"trailingComma": "es5"
 }

+ 2 - 0
packages/components/assets/index.ts

@@ -0,0 +1,2 @@
+export * from "./bars-play.svg";
+export * from "./bars-plus.svg";

+ 68 - 67
packages/components/package.json

@@ -1,69 +1,70 @@
 {
-  "name": "components",
-  "version": "1.0.0",
-  "description": "React Components for the Atlas Project",
-  "homepage": "https://github.com/Joystream/atlas#readme",
-  "license": "ISC",
-  "main": "dist/index.cjs.js",
-  "module": "dist/index.es.js",
-  "types": "dist/types",
-  "directories": {
-    "src": "src",
-    "test": "__tests__"
-  },
-  "files": [
-    "src"
-  ],
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/Joystream/atlas.git"
-  },
-  "scripts": {
-    "start": "rollup -wc",
-    "index": "node scripts/build-index.js",
-    "build": "rollup -c",
-    "storybook": "start-storybook -p 6006",
-    "build-storybook": "build-storybook",
-    "now-build": "build-storybook",
-    "test": "echo \"Error: run tests from root\" && exit 1"
-  },
-  "bugs": {
-    "url": "https://github.com/Joystream/atlas/issues"
-  },
-  "devDependencies": {
-    "@babel/core": "^7.8.7",
-    "@babel/preset-env": "^7.8.7",
-    "@babel/preset-react": "^7.8.3",
-    "@babel/preset-typescript": "^7.9.0",
-    "@emotion/babel-preset-css-prop": "^10.0.27",
-    "@emotion/core": "^10.0.28",
-    "@fortawesome/fontawesome-svg-core": "^1.2.28",
-    "@fortawesome/free-solid-svg-icons": "^5.13.0",
-    "@fortawesome/react-fontawesome": "^0.1.9",
-    "@rollup/plugin-commonjs": "^11.0.2",
-    "@rollup/plugin-node-resolve": "^7.1.1",
-    "@storybook/addon-actions": "^5.3.17",
-    "@storybook/addon-docs": "^5.3.17",
-    "@storybook/addon-knobs": "^5.3.17",
-    "@storybook/addon-links": "^5.3.17",
-    "@storybook/addon-storysource": "^5.3.17",
-    "@storybook/addons": "^5.3.17",
-    "@storybook/react": "^5.3.17",
-    "@svgr/rollup": "^5.4.0",
-    "@svgr/webpack": "^5.4.0",
-    "babel-jest": "^25.4.0",
-    "babel-loader": "^8.0.6",
-    "babel-plugin-transform-export-extensions": "^6.22.0",
-    "react": "^16.13.0",
-    "react-docgen-typescript-loader": "^3.7.1",
-    "react-dom": "^16.13.0",
-    "react-player": "^1.15.3",
-    "rollup": "^2.1.0",
-    "rollup-plugin-babel": "^4.4.0",
-    "rollup-plugin-typescript2": "^0.26.0",
-    "storybook-addon-jsx": "^7.1.15",
-    "ts-loader": "^6.2.1",
-    "typescript": "^3.8.3",
-    "video-react": "^0.14.1"
-  }
+	"name": "components",
+	"version": "1.0.0",
+	"description": "React Components for the Atlas Project",
+	"homepage": "https://github.com/Joystream/atlas#readme",
+	"license": "ISC",
+	"main": "dist/index.cjs.js",
+	"module": "dist/index.es.js",
+	"types": "dist/types",
+	"directories": {
+		"src": "src",
+		"test": "__tests__"
+	},
+	"files": [
+		"src"
+	],
+	"repository": {
+		"type": "git",
+		"url": "https://github.com/Joystream/atlas.git"
+	},
+	"scripts": {
+		"start": "rollup -wc",
+		"index": "node scripts/build-index.js",
+		"build": "rollup -c",
+		"storybook": "start-storybook -p 6006",
+		"build-storybook": "build-storybook",
+		"now-build": "build-storybook",
+		"test": "echo \"Error: run tests from root\" && exit 1"
+	},
+	"bugs": {
+		"url": "https://github.com/Joystream/atlas/issues"
+	},
+	"devDependencies": {
+		"@babel/core": "^7.8.7",
+		"@babel/preset-env": "^7.8.7",
+		"@babel/preset-react": "^7.8.3",
+		"@babel/preset-typescript": "^7.9.0",
+		"@emotion/babel-preset-css-prop": "^10.0.27",
+		"@emotion/core": "^10.0.28",
+		"@fortawesome/fontawesome-svg-core": "^1.2.28",
+		"@fortawesome/free-solid-svg-icons": "^5.13.0",
+		"@fortawesome/react-fontawesome": "^0.1.9",
+		"@rollup/plugin-commonjs": "^11.0.2",
+		"@rollup/plugin-node-resolve": "^7.1.1",
+		"@storybook/addon-actions": "^5.3.17",
+		"@storybook/addon-docs": "^5.3.17",
+		"@storybook/addon-knobs": "^5.3.17",
+		"@storybook/addon-links": "^5.3.17",
+		"@storybook/addon-storysource": "^5.3.17",
+		"@storybook/addons": "^5.3.17",
+		"@storybook/react": "^5.3.17",
+		"@svgr/rollup": "^5.4.0",
+		"@svgr/webpack": "^5.4.0",
+		"babel-jest": "^25.4.0",
+		"babel-loader": "^8.0.6",
+		"babel-plugin-transform-export-extensions": "^6.22.0",
+		"csstype": "^2.6.10",
+		"react": "^16.13.0",
+		"react-docgen-typescript-loader": "^3.7.1",
+		"react-dom": "^16.13.0",
+		"react-player": "^1.15.3",
+		"rollup": "^2.1.0",
+		"rollup-plugin-babel": "^4.4.0",
+		"rollup-plugin-typescript2": "^0.26.0",
+		"storybook-addon-jsx": "^7.1.15",
+		"ts-loader": "^6.2.1",
+		"typescript": "^3.8.3",
+		"video-react": "^0.14.1"
+	}
 }

+ 251 - 155
packages/components/src/components/Button/Button.style.ts

@@ -1,157 +1,253 @@
-import { css } from "@emotion/core"
-import { typography, colors } from "../../theme"
+import { typography, colors } from "../../theme";
+import { makeStyles, StyleFn, StyleObj } from "../../utils";
+import { disabled, dimensionsFromProps, log } from "../../theme/fragments";
 
 export type ButtonStyleProps = {
-  text?: string
-  type?: "primary" | "secondary"
-  width?: "normal" | "full"
-  size?: "regular" | "small" | "smaller"
-  disabled?: boolean
-}
-
-export let makeStyles = ({
-  text,
-  type = "primary",
-  width = "normal",
-  size = "regular",
-  disabled = false
-}: ButtonStyleProps) => {
-
-  const buttonHeight = size === "regular" ? "20px" : size === "small" ? "15px" : "10px";
-
-  const primaryButton = {
-    container: css`
-      border: 1px solid ${colors.blue[500]};
-      color: ${colors.white};
-      background-color: ${colors.blue[500]};
-      justify-content:center;
-      padding: ${size === "regular" ? (!!text ? "14px 17px" : "14px") :
-        size === "small" ? (!!text ? "12px 14px" : "12px") : "10px"
-      };
-      display: ${width === "normal" ? "inline-flex" : "flex"};
-      align-items: center;
-      cursor: default;
-      font-family: ${typography.fonts.base};
-      font-weight: ${typography.weights.medium};
-      font-size: ${size === "regular" ? typography.sizes.button.large :
-        size === "small" ? typography.sizes.button.medium : 
-        typography.sizes.button.small
-      };
-      margin: 0 ${width === "normal" ? "15px" : "0"} 0 0;
-      height: ${buttonHeight};
-      max-height: ${buttonHeight};
-
-      &:hover {
-        background-color: ${colors.blue[700]};
-        border-color: ${colors.blue[700]};
-        color: ${colors.white};
-      }
-
-      &:active {
-        background-color: ${colors.blue[900]};
-        border-color: ${colors.blue[900]};
-        color: ${colors.white};
-      }
-
-      &::selection {
-        background: transparent;
-      }
-    `
-  }
-
-  const secondaryButton = {
-    container: css`
-      border: 1px solid ${colors.blue[500]};
-      color: ${colors.white};
-      background-color: ${colors.black};
-      justify-content:center;
-      padding: ${size === "regular" ? (!!text ? "14px 17px" : "14px") :
-        size === "small" ? (!!text ? "12px 14px" : "12px") : "10px"
-      };
-      display: ${width === "normal" ? "inline-flex" : "flex"};
-      align-items: center;
-      cursor: default;
-      font-family: ${typography.fonts.base};
-      font-weight: ${typography.weights.medium};
-      font-size: ${size === "regular" ? typography.sizes.button.large :
-        size === "small" ? typography.sizes.button.medium : 
-        typography.sizes.button.small
-      };
-      margin: 0 ${width === "normal" ? "15px" : "0"} 0 0;
-      height: ${buttonHeight};
-      max-height: ${buttonHeight};
-
-      &:hover {
-        background-color: ${colors.black};
-        border-color: ${colors.blue[700]};
-        color: ${colors.blue[300]};
-      }
-
-      &:active {
-        background-color: ${colors.black};
-        border-color: ${colors.blue[700]};
-        color: ${colors.blue[700]};
-      }
-
-      &::selection {
-        background: transparent;
-      }
-    `
-  }
-
-  const disabledButton = {
-    container: css`
-      border: 1px solid ${colors.white};
-      color: ${colors.white};
-      background-color: ${colors.gray[100]};
-      justify-content:center;
-      padding: ${size === "regular" ? (!!text ? "14px 17px" : "14px") :
-        size === "small" ? (!!text ? "12px 14px" : "12px") : "10px"
-      };
-      display: ${width === "normal" ? "inline-flex" : "flex"};
-      align-items: center;
-      cursor: ${disabled ? "not-allowed" : "default"};
-      font-family: ${typography.fonts.base};
-      font-weight: ${typography.weights.medium};
-      font-size: ${size === "regular" ? typography.sizes.button.large :
-        size === "small" ? typography.sizes.button.medium : 
-        typography.sizes.button.small
-      };
-      margin: 0 ${width === "normal" ? "15px" : "0"} 0 0;
-      height: ${buttonHeight};
-      max-height: ${buttonHeight};
-
-      &:hover {
-        background-color: ${colors.gray[100]};
-        border-color: ${colors.white};
-        color: ${colors.white};
-      }
-
-      &:active {
-        background-color: ${colors.gray[100]};
-        border-color: ${colors.white};
-        color: ${colors.white};
-      }
-
-      &::selection {
-        background: transparent;
-      }
-    `
-  }
-
-  const icon = css`
-    margin-right: ${!!text ? "10px" : "0"};
-    font-size: ${size === "regular" ? typography.sizes.icon.large :
-        size === "small" ? typography.sizes.icon.medium : 
-        typography.sizes.icon.small
-      };
-
-    & > path:nth-of-type(1) {	
-      color: inherit;	
-      flex-shrink: 0;
-    }
-  `
-
-  const result = disabled ? disabledButton : type === "primary" ? primaryButton : secondaryButton
-  return { icon, ...result }
-}
+	text?: string;
+	type?: "primary" | "secondary";
+	full?: boolean;
+	size?: "regular" | "small" | "smaller";
+	children?: React.ReactNode;
+	disabled?: boolean;
+};
+
+const baseStyles: StyleFn = () => ({
+	borderWidth: "1px",
+	borderStyle: "solid",
+	fontFamily: typography.fonts.base,
+	fontWeight: typography.weights.medium,
+	display: "inline-flex",
+	justifyContent: "center",
+	alignItems: "center",
+	color: colors.white,
+	"&::selected": {
+		background: "transparent",
+	},
+});
+const colorFromType: StyleFn = (styles, { type }: ButtonStyleProps) => {
+	switch (type) {
+		case "primary":
+			return {
+				...styles,
+				backgroundColor: colors.blue[500],
+				borderColor: colors.blue[500],
+
+				"&:hover": {
+					backgroundColor: colors.blue[700],
+					borderColor: colors.blue[700],
+					color: colors.white,
+				},
+				"&:active": {
+					backgroundColor: colors.blue[900],
+					borderColor: colors.blue[900],
+					color: colors.white,
+				},
+			};
+
+		case "secondary":
+			return {
+				...styles,
+				color: colors.blue[500],
+				backgroundColor: colors.black,
+				borderColor: colors.blue[500],
+
+				"&:hover": {
+					borderColor: colors.blue[700],
+					color: colors.blue[300],
+				},
+
+				"&:active": {
+					borderColor: colors.blue[700],
+					color: colors.blue[700],
+				},
+			};
+	}
+};
+const paddingFromType: StyleFn = (
+	styles,
+	{ size, children, full }: { size: "regular" | "small" | "smaller"; children?: React.ReactNode; full: boolean }
+) => {
+	const buttonHeight = size === "regular" ? "20px" : size === "small" ? "15px" : "10px";
+	return {
+		...styles,
+		margin: `0 ${full ? "0" : "15px"} 0 0`,
+		padding:
+			size === "regular"
+				? !!children
+					? "14px 17px"
+					: "14px"
+				: size === "small"
+				? !!children
+					? "12px 14px"
+					: "12px"
+				: "10px",
+		fontSize:
+			size === "regular"
+				? typography.sizes.button.large
+				: size === "small"
+				? typography.sizes.button.medium
+				: typography.sizes.button.small,
+
+		height: buttonHeight,
+		maxHeight: buttonHeight,
+	};
+};
+
+const iconStyles: StyleFn = (styles, { children, size }) => {
+	return {
+		...styles,
+		marginRight: children != null ? "10px" : "0",
+		fontSize:
+			size === "regular"
+				? typography.sizes.icon.large
+				: size === "small"
+				? typography.sizes.icon.medium
+				: typography.sizes.icon.small,
+
+		flexShrink: 0,
+		"& > *": {
+			stroke: "currentColor",
+		},
+	};
+};
+
+export const useCSS = (props: ButtonStyleProps) => ({
+	container: makeStyles([baseStyles, colorFromType, dimensionsFromProps, paddingFromType, disabled])(props),
+	icon: makeStyles([iconStyles])(props),
+});
+
+// 	text,
+// 	type = "primary",
+// 	width = "normal",
+// 	size = "regular",
+// 	disabled = false,
+// }: ButtonStyleProps) => {
+//
+
+// 	const primaryButton = {
+// 		container: css`
+// 			border: 1px solid ${colors.blue[500]};
+// 			color: ${colors.white};
+// 			background-color: ${colors.blue[500]};
+
+// 			padding: ${size === "regular"
+// 				? !!text
+// 					? "14px 17px"
+// 					: "14px"
+// 				: size === "small"
+// 				? !!text
+// 					? "12px 14px"
+// 					: "12px"
+// 				: "10px"};
+// 			font-size: ${size === "regular"
+// 				? typography.sizes.button.large
+// 				: size === "small"
+// 				? typography.sizes.button.medium
+// 				: typography.sizes.button.small};
+//
+//
+// 		`,
+// 	};
+
+// 	const secondaryButton = {
+// 		container: css`
+// 			border: 1px solid ${colors.blue[500]};
+
+// 			background-color: ${colors.black};
+// 			justify-content: center;
+// 			padding: ${size === "regular"
+// 				? !!text
+// 					? "14px 17px"
+// 					: "14px"
+// 				: size === "small"
+// 				? !!text
+// 					? "12px 14px"
+// 					: "12px"
+// 				: "10px"};
+// 			font-size: ${size === "regular"
+// 				? typography.sizes.button.large
+// 				: size === "small"
+// 				? typography.sizes.button.medium
+// 				: typography.sizes.button.small};
+// 			margin: 0 ${width === "normal" ? "15px" : "0"} 0 0;
+// 			height: ${buttonHeight};
+// 			max-height: ${buttonHeight};
+
+// 			&:hover {
+// 				background-color: ${colors.black};
+// 				border-color: ${colors.blue[700]};
+// 				color: ${colors.blue[300]};
+// 			}
+
+// 			&:active {
+// 				background-color: ${colors.black};
+// 				border-color: ${colors.blue[700]};
+// 				color: ${colors.blue[700]};
+// 			}
+// 		`,
+// 	};
+
+// 	const disabledButton = {
+// 		container: css`
+// 			border: 1px solid ${colors.white};
+// 			color: ${colors.white};
+// 			background-color: ${colors.gray[100]};
+// 			justify-content: center;
+// 			padding: ${size === "regular"
+// 				? !!text
+// 					? "14px 17px"
+// 					: "14px"
+// 				: size === "small"
+// 				? !!text
+// 					? "12px 14px"
+// 					: "12px"
+// 				: "10px"};
+// 			display: ${width === "normal" ? "inline-flex" : "flex"};
+// 			align-items: center;
+// 			cursor: ${disabled ? "not-allowed" : "default"};
+// 			font-family: ${typography.fonts.base};
+// 			font-weight: ${typography.weights.medium};
+// 			font-size: ${size === "regular"
+// 				? typography.sizes.button.large
+// 				: size === "small"
+// 				? typography.sizes.button.medium
+// 				: typography.sizes.button.small};
+// 			margin: 0 ${width === "normal" ? "15px" : "0"} 0 0;
+// 			height: ${buttonHeight};
+// 			max-height: ${buttonHeight};
+
+// 			&:hover {
+// 				background-color: ${colors.gray[100]};
+// 				border-color: ${colors.white};
+// 				color: ${colors.white};
+// 			}
+
+// 			&:active {
+// 				background-color: ${colors.gray[100]};
+// 				border-color: ${colors.white};
+// 				color: ${colors.white};
+// 			}
+
+// 			&::selection {
+// 				background: transparent;
+// 			}
+// 		`,
+// 	};
+
+// 	const icon = css`
+// 		margin-right: ${!!text ? "10px" : "0"};
+// 		font-size: ${size === "regular"
+// 			? typography.sizes.icon.large
+// 			: size === "small"
+// 			? typography.sizes.icon.medium
+// 			: typography.sizes.icon.small};
+
+// 		& > path:nth-of-type(1) {
+// 			color: inherit;
+// 			flex-shrink: 0;
+// 		}
+// 	`;
+
+// 	const result = disabled ? disabledButton : type === "primary" ? primaryButton : secondaryButton;
+// 	return { icon, ...result };
+// };

+ 21 - 23
packages/components/src/components/Button/Button.tsx

@@ -1,29 +1,27 @@
-import React from "react"
-import { makeStyles, ButtonStyleProps } from "./Button.style"
-import { IconProp } from "@fortawesome/fontawesome-svg-core"
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
+import React from "react";
+import { ButtonStyleProps, useCSS } from "./Button.style";
+import BlockIcon from "../../../assets/block.svg";
 
 type ButtonProps = {
-  text?: string
-  icon?: IconProp
-  disabled?: boolean
-  onClick?: (e: React.MouseEvent<HTMLDivElement>) => void
-} & ButtonStyleProps
+	children?: React.ReactNode;
+	icon?: boolean;
+	disabled?: boolean;
+	onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
+} & ButtonStyleProps;
 
 export default function Button({
-  text = "",
-  icon,
-  disabled = false,
-  onClick,
-  ...styleProps
+	children,
+	icon,
+	type = "primary",
+	disabled = false,
+	onClick,
+	...styleProps
 }: ButtonProps) {
-  let styles = makeStyles({text, disabled, ...styleProps})
-  return (
-    <div css={styles.container} onClick={disabled ? null : onClick}>
-      {!!icon &&
-        <FontAwesomeIcon icon={icon} css={styles.icon} />
-      }
-      {text}
-    </div>
-  )
+	let styles = useCSS({ disabled, type, children, ...styleProps });
+	return (
+		<button css={styles.container} onClick={disabled ? null : onClick}>
+			{icon && <BlockIcon css={styles.icon} />}
+			{children}
+		</button>
+	);
 }

+ 3 - 3
packages/components/src/components/Button/index.ts

@@ -1,3 +1,3 @@
-import { memo } from "react"
-import Button from "./Button"
-export default memo(Button)
+import { memo } from "react";
+import Button from "./Button";
+export default memo(Button);

+ 67 - 15
packages/components/src/theme/fragments.ts

@@ -1,21 +1,73 @@
+import { StyleFn } from "./../utils/style-reducer";
 import { css } from "@emotion/core";
 import spacing from "./spacing";
 import typography from "./typography";
+import colors from "./colors";
+import { StyleObj, stripInline } from "../utils";
 
 export function withSize(size: string) {
-  return css`
-    padding: ${size === "large"
-      ? `${spacing.s4} ${spacing.s2}`
-      : size === "normal" || size === "full"
-      ? `${spacing.s3} ${spacing.s2}`
-      : `${spacing.s2} ${spacing.s1}`};
-
-    font-size: ${size === "large"
-      ? typography.sizes.large
-      : size === "normal" || size === "full"
-      ? typography.sizes.normal
-      : typography.sizes.small};
-
-    width: ${size === "full" ? "100%" : "auto"};
-  `;
+	return css`
+		padding: ${size === "large"
+			? `${spacing.s4} ${spacing.s2}`
+			: size === "normal" || size === "full"
+			? `${spacing.s3} ${spacing.s2}`
+			: `${spacing.s2} ${spacing.s1}`};
+
+		font-size: ${size === "large"
+			? typography.sizes.large
+			: size === "normal" || size === "full"
+			? typography.sizes.normal
+			: typography.sizes.small};
+
+		width: ${size === "full" ? "100%" : "auto"};
+	`;
+}
+
+export function log(styles: StyleObj, props: any) {
+	console.log("styles", styles);
+	console.log("props", props);
+	return styles;
+}
+
+export function dimensionsFromProps(styles: StyleObj, { full }: { full: boolean }) {
+	let display: string;
+	if (styles.display == null) {
+		display = "block";
+	}
+	display = styles.display as string;
+
+	return {
+		...styles,
+		display: full && display.includes("inline") ? stripInline(display) : display,
+		width: full ? "100%" : styles.width || "",
+	};
+}
+
+export function disabled(styles: StyleObj, { disabled }: { disabled: boolean }): StyleObj {
+	if (!disabled) {
+		return styles;
+	}
+
+	return {
+		...unsetStyles(styles),
+		backgroundColor: colors.gray[100],
+		color: colors.white,
+	};
+}
+
+function unsetStyles(styles: StyleObj): StyleObj {
+	// Filter and unset all properties that give color, on all states.
+	// Need to add more?
+	const colorProperties = ["color", "backgroundColor", "borderColor", "boxShadow", "fill", "stroke"];
+
+	const filteredEntries = Object.entries(styles).map(([key, value]) => {
+		// If it has a psuedo selector, we're going to disable color from that as well.
+		if (key.includes("&:hover") || key.includes("&:active") || key.includes("&:focus")) {
+			return unsetStyles(value as StyleObj);
+		} else if (colorProperties.includes(key)) {
+			return [key, "unset"];
+		}
+		return [key, value];
+	});
+	return Object.fromEntries(filteredEntries as any);
 }

+ 1 - 0
packages/components/src/theme/index.ts

@@ -2,3 +2,4 @@ export { default as typography } from "./typography";
 export { default as colors } from "./colors";
 export { default as spacing } from "./spacing";
 export { default as breakpoints } from "./breakpoints";
+export * from "./fragments";

+ 3 - 0
packages/components/src/utils/helpers.ts

@@ -0,0 +1,3 @@
+export function stripInline(str: string) {
+	return str.replace(/inline-?/gi, "") || "block";
+}

+ 2 - 0
packages/components/src/utils/index.ts

@@ -0,0 +1,2 @@
+export * from "./style-reducer";
+export * from "./helpers";

+ 40 - 0
packages/components/src/utils/style-reducer.ts

@@ -0,0 +1,40 @@
+import * as CSS from "csstype";
+import { css } from "@emotion/core";
+
+//FIXME: This is not correctly typed, since it doesn't
+export type StyleObj = { [k in keyof CSS.Properties]: number | string | StyleObj };
+export type StyleFn = (style: StyleObj, x: any) => StyleObj;
+
+export type Modifiers = { [k: string]: StyleFn };
+
+// TODO: Properly type this
+type StyleMonad = (
+	run: StyleFn
+) => {
+	run: StyleFn;
+	map: (f: (args: any) => any) => StyleMonad;
+	contramap: (f: (args: any) => any) => StyleMonad;
+	concat: (other: StyleMonad) => StyleMonad;
+};
+
+export const Reducer = (run: StyleFn) => ({
+	run,
+	concat: (other: any) => Reducer((styles: StyleObj, props: any) => other.run(run(styles, props), props)),
+	map: (f: (x: any) => any) => Reducer((styles: StyleObj, props: any) => f(run(styles, props))),
+	contramap: (f: (x: any) => any) => Reducer((styles: StyleObj, props: any) => run(styles, f(props))),
+});
+
+export function combineReducers(...reducers: StyleFn[]) {
+	return reducers.reduce(
+		(acc, reducer) => acc.concat(Reducer(reducer)),
+		Reducer(() => ({}))
+	);
+}
+
+export function makeStyles(reducers: StyleFn[]) {
+	const reducer = combineReducers(...reducers);
+	return function(props: any) {
+		const styles: any = reducer.run({}, props);
+		return css(styles);
+	};
+}

+ 73 - 53
packages/components/stories/01-Button.stories.tsx

@@ -1,72 +1,92 @@
-import React from "react"
-import { Button } from "../src"
-import { faBan } from "@fortawesome/free-solid-svg-icons"
+import React from "react";
+import { Button } from "../src";
+import { faBan } from "@fortawesome/free-solid-svg-icons";
 
 export default {
-  title: "Button",
-  component: Button,
-}
+	title: "Button",
+	component: Button,
+};
 
 export const Primary = () => (
-  <>
-    <Button text="Button" onClick={() => console.log("Button clicked!")} />
-    <Button text="Button" size="small" onClick={() => console.log("Button clicked!")} />
-    <Button text="Button" size="smaller" onClick={() => console.log("Button clicked!")} />
-  </>
-)
+	<>
+		<Button onClick={() => console.log("Button clicked!")}>Regular</Button>
+		<Button size="small" onClick={() => console.log("Button clicked!")}>
+			Small
+		</Button>
+		<Button size="smaller" onClick={() => console.log("Button clicked!")}>
+			Smaller
+		</Button>
+	</>
+);
 
 export const Secondary = () => (
-  <>
-    <Button text="Button" type="secondary" />
-    <Button text="Button" type="secondary" size="small" />
-    <Button text="Button" type="secondary" size="smaller" />
-  </>
-)
+	<>
+		<Button type="secondary">Regular</Button>
+		<Button type="secondary" size="small">
+			Small
+		</Button>
+		<Button type="secondary" size="smaller">
+			Smaller
+		</Button>
+	</>
+);
 
-export const PrimaryFullSize = () => (
-  <Button text="Button" width="full" />
-)
+export const PrimaryFullSize = () => <Button full>Primary Full Size</Button>;
 
 export const SecondaryFullSize = () => (
-  <Button text="Button" width="full" type="secondary" />
-)
+	<Button full type="secondary">
+		Secondary Full Size
+	</Button>
+);
 
 export const PrimaryWithIcon = () => (
-  <>
-    <Button text="Button" icon={faBan} />
-    <Button text="Button" icon={faBan} size="small" />
-    <Button text="Button" icon={faBan} size="smaller" />
-  </>
-)
+	<>
+		<Button icon>Regular</Button>
+		<Button icon size="small">
+			Small
+		</Button>
+		<Button icon size="smaller">
+			Smaller
+		</Button>
+	</>
+);
 
 export const SecondaryWithIcon = () => (
-  <>
-    <Button text="Button" type="secondary" icon={faBan} />
-    <Button text="Button" type="secondary" icon={faBan} size="small" />
-    <Button text="Button" type="secondary" icon={faBan} size="smaller" />
-  </>
-)
+	<>
+		<Button type="secondary" icon>
+			Regular
+		</Button>
+		<Button type="secondary" icon size="small">
+			Small
+		</Button>
+		<Button type="secondary" icon size="smaller">
+			Smaller
+		</Button>
+	</>
+);
 
 export const PrimaryWithoutText = () => (
-  <>
-    <Button icon={faBan} />
-    <Button icon={faBan} size="small" />
-    <Button icon={faBan} size="smaller" />
-  </>
-)
+	<>
+		<Button icon />
+		<Button icon size="small" />
+		<Button icon size="smaller" />
+	</>
+);
 
 export const SecondaryWithoutText = () => (
-  <>
-    <Button type="secondary" icon={faBan} />
-    <Button type="secondary" icon={faBan} size="small" />
-    <Button type="secondary" icon={faBan} size="smaller" />
-  </>
-)
+	<>
+		<Button type="secondary" icon />
+		<Button type="secondary" icon size="small" />
+		<Button type="secondary" icon size="smaller" />
+	</>
+);
 
 export const Disabled = () => (
-  <>
-    <Button disabled={true} text="Button" onClick={() => console.log("Button clicked!")} />
-    <Button disabled={true} text="Button" icon={faBan} onClick={() => console.log("Button clicked!")} />
-    <Button disabled={true} icon={faBan} onClick={() => console.log("Button clicked!")} />
-  </>
-)
+	<>
+		<Button disabled={true}>Disabled</Button>
+		<Button disabled={true} icon={true}>
+			Disabled with icon
+		</Button>
+		<Button disabled={true} icon />
+	</>
+);

+ 1 - 0
packages/components/tsconfig.json

@@ -3,6 +3,7 @@
 
 	"compilerOptions": {
 		"module": "esnext",
+		"lib": ["ES2019"],
 		"rootDirs": ["src", "stories"],
 		"baseUrl": "src",
 		"jsx": "preserve",

File diff suppressed because it is too large
+ 266 - 328
yarn.lock


Some files were not shown because too many files changed in this diff