Browse Source

Add Checkbox

Francesco Baccetti 4 years ago
parent
commit
db5e899e81

+ 109 - 0
packages/components/src/components/Checkbox/Checkbox.style.ts

@@ -0,0 +1,109 @@
+import { StyleFn, makeStyles } from "./../../utils/style-reducer"
+import { colors, log } from "../../theme"
+
+export type CheckboxStyleProps = {
+	labelPosition?: "end" | "start" | "top" | "bottom"
+	caption?: string
+	error?: boolean
+	selected?: boolean
+	disabled?: boolean
+}
+
+const fillFromProps: StyleFn = (styles, { disabled, error, selected }) => {
+	let fill = error
+		? colors.error
+		: selected && disabled
+		? colors.gray[700]
+		: selected
+		? colors.blue[500]
+		: "transparent"
+
+	return {
+		...styles,
+		backgroundColor: fill
+	}
+}
+
+const borderColorFromProps: StyleFn = (styles, { disabled, error, selected }) => {
+	let borderColor = error
+		? colors.error
+		: selected && disabled
+		? colors.gray[700]
+		: disabled
+		? colors.gray[400]
+		: selected
+		? colors.blue[500]
+		: colors.gray[300]
+
+	return {
+		...styles,
+		borderColor
+	}
+}
+
+const shadowFromProps: StyleFn = (styles, { disabled, error, selected }) => {
+	let shadow = error || disabled ? "transparent" : selected ? colors.blue[900] : colors.gray[800]
+	return {
+		...styles,
+		backgroundColor: shadow
+	}
+}
+
+const outerContainer: StyleFn = (_, { disabled, error, selected }) => ({
+	borderRadius: "999px",
+	color: disabled ? colors.gray[400] : colors.white,
+	maxWidth: "2rem",
+	maxHeight: "2rem",
+	width: "2rem",
+	height: "2rem",
+	display: "flex",
+	justifyContent: "center",
+	alignItems: "center",
+	"&:focus-within": shadowFromProps({}, { disabled, error, selected }),
+	"&:hover": disabled ? {} : shadowFromProps({}, { disabled, error, selected })
+})
+
+const innerContainer: StyleFn = (_, { disabled, error, selected }) => ({
+	position: "relative",
+	borderWidth: "1px",
+	borderStyle: "solid",
+	backgroundColor: selected ? fillFromProps({}, { disabled, error, selected }).backgroundColor : "transparent",
+	width: "1.065rem",
+	height: "1.065rem",
+	maxWidth: "1.065rem",
+	maxHeight: "1.065rem",
+	textAlign: "center",
+	[`& > input[type="checkbox"]:checked`]: {
+		borderColor: !selected ? colors.white : borderColorFromProps({}, { disabled, error, selected }).borderColor,
+		...fillFromProps({}, { disabled, error, selected })
+	},
+	"&:active": fillFromProps({}, { disabled, error, selected })
+})
+
+const input: StyleFn = () => ({
+	position: "absolute",
+	top: 0,
+	bottom: 0,
+	left: 0,
+	right: 0,
+	opacity: 0,
+	width: "100%",
+	height: "100%",
+	padding: "unset",
+	margin: "unset",
+	border: "unset"
+})
+
+const icon: StyleFn = () => ({
+	verticalAlign: "middle"
+})
+
+export const useCSS = ({ selected, error, disabled }: CheckboxStyleProps) => {
+	const props = { selected, error, disabled }
+	return {
+		outerContainer: makeStyles([outerContainer])(props),
+		innerContainer: makeStyles([innerContainer, borderColorFromProps])(props),
+		input: makeStyles([input])(props),
+		icon: makeStyles([icon])(props)
+	}
+}

+ 32 - 0
packages/components/src/components/Checkbox/Checkbox.tsx

@@ -0,0 +1,32 @@
+import React, { useState } from "react";
+import { useCSS, CheckboxStyleProps } from "./Checkbox.style";
+import CheckIcon from "../../../assets/check.svg";
+import DashIcon from "../../../assets/dash.svg";
+
+type CheckboxProps = {
+	label?: string;
+	icon?: "check" | "dash";
+	onChange?: (e: React.ChangeEvent) => void;
+} & CheckboxStyleProps;
+
+export default function Checkbox({
+	label = "",
+	disabled = false,
+	error = false,
+	selected = false,
+	icon = "check",
+	labelPosition = "end",
+	onChange = () => {},
+	...styleProps
+}: CheckboxProps) {
+	const styles = useCSS({ ...styleProps, selected, error, disabled });
+	return (
+		<div css={styles.outerContainer}>
+			<div css={styles.innerContainer}>
+				<input css={styles.input} type="checkbox" checked={selected} disabled={disabled} onChange={onChange} />
+				{selected && (icon === "check" ? <CheckIcon css={styles.icon} /> : <DashIcon css={styles.icon} />)}
+			</div>
+			{(labelPosition === "end" || labelPosition === "bottom") && <label css={styles.label}>{label}</label>}
+		</div>
+	);
+}

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

@@ -1,3 +1,5 @@
+import Checkbox from "./components/Checkbox"
+
 export { default as Button } from "./components/Button"
 export { default as Carousel } from "./components/Carousel"
 export { default as Dropdown } from "./components/Dropdown"
@@ -6,6 +8,7 @@ export { default as Header } from "./components/Header"
 export { default as Link } from "./components/Link"
 export { default as NavButton } from "./components/NavButton"
 export { default as RadioButton } from "./components/RadioButton"
+export { default as Checkbox } from "./components/Checkbox"
 export { default as TagButton } from "./components/TagButton"
 export { default as Tabs } from "./components/Tabs"
 export { default as Tab } from "./components/Tabs/Tab"

+ 84 - 0
packages/components/stories/08-Checkbox.stories.tsx

@@ -0,0 +1,84 @@
+import React, { useState } from "react";
+import { Checkbox } from "./../src";
+
+export default {
+	title: "Checkbox",
+	component: Checkbox,
+};
+
+export const Primary = () => {
+	const [selected, setSelected] = useState(false);
+	return (
+		<div style={{ padding: "50px 20px", background: "black" }}>
+			<Checkbox
+				selected={selected}
+				onChange={() => {
+					setSelected(!selected);
+				}}
+			/>
+		</div>
+	);
+};
+
+export const PrimaryWithDash = () => {
+	const [selected, setSelected] = useState(false);
+	return (
+		<div style={{ padding: "50px 20px", background: "black" }}>
+			<Checkbox
+				icon="dash"
+				selected={selected}
+				onChange={() => {
+					setSelected(!selected);
+				}}
+			/>
+		</div>
+	);
+};
+
+export const Error = () => {
+	const [selected, setSelected] = useState(false);
+	return (
+		<div style={{ padding: "50px 20px", background: "black" }}>
+			<Checkbox
+				selected={selected}
+				error
+				onChange={() => {
+					setSelected(!selected);
+				}}
+			/>
+		</div>
+	);
+};
+
+export const ErrorWithDash = () => {
+	const [selected, setSelected] = useState(false);
+	return (
+		<div style={{ padding: "50px 20px", background: "black" }}>
+			<Checkbox
+				selected={selected}
+				error
+				icon="dash"
+				onChange={() => {
+					setSelected(!selected);
+				}}
+			/>
+		</div>
+	);
+};
+
+export const Disabled = () => {
+	const [selected, setSelected] = useState(false);
+	return (
+		<div style={{ padding: "50px 20px", background: "black" }}>
+			<Checkbox
+				disabled
+				onChange={() => {
+					setSelected(!selected);
+				}}
+				selected={selected}
+			/>
+			<Checkbox disabled icon="check" selected />
+			<Checkbox disabled icon="dash" selected />
+		</div>
+	);
+};