diff --git a/package-lock.json b/package-lock.json
index 0f67a12..fb6ab7f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -52,6 +52,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz",
"integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==",
+ "dev": true,
"requires": {
"@babel/types": "^7.0.0"
}
@@ -960,6 +961,7 @@
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.3.tgz",
"integrity": "sha512-RpPOVfK+yatXyn8n4PB1NW6k9qjinrXrRR8ugBN8fD6hCy5RXI6PSbVqpOJBO9oSaY7Nom4ohj35feb0UR9hSA==",
+ "dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.10",
@@ -998,6 +1000,7 @@
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.6.8.tgz",
"integrity": "sha512-IMSL7ekYhmFlILXcouA6ket3vV7u9BqStlXzbKOF9HBtpUPMMlHU+bBxrLOa2NvleVwNIxeq/zL8LafLbeUXcA==",
+ "dev": true,
"requires": {
"@emotion/memoize": "^0.6.6"
}
@@ -1005,7 +1008,8 @@
"@emotion/memoize": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz",
- "integrity": "sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ=="
+ "integrity": "sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ==",
+ "dev": true
},
"@emotion/serialize": {
"version": "0.9.1",
@@ -2209,7 +2213,8 @@
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
- "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+ "dev": true
},
"asn1": {
"version": "0.2.4",
@@ -2647,6 +2652,7 @@
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.8.0.tgz",
"integrity": "sha512-PcrdbXFO/9Plo9JURIj8G0Dsz+Ct8r+NvjoLh6qPt8Y/3EIAj1gHGW1ocPY1IkQbXZLBEZZSRBAxJem1KFdBXg==",
+ "dev": true,
"requires": {
"@babel/helper-annotate-as-pure": "^7.0.0",
"lodash": "^4.17.10"
@@ -4955,7 +4961,8 @@
"css-color-keywords": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
- "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU="
+ "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=",
+ "dev": true
},
"css-select": {
"version": "2.0.0",
@@ -4979,6 +4986,7 @@
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.2.2.tgz",
"integrity": "sha512-w99Fzop1FO8XKm0VpbQp3y5mnTnaS+rtCvS+ylSEOK76YXO5zoHQx/QMB1N54Cp+Ya9jB9922EHrh14ld4xmmw==",
+ "dev": true,
"requires": {
"css-color-keywords": "^1.0.0",
"fbjs": "^0.8.5",
@@ -5981,6 +5989,7 @@
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+ "dev": true,
"requires": {
"iconv-lite": "~0.4.13"
}
@@ -6714,7 +6723,8 @@
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
- "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
},
"event-stream": {
"version": "3.3.6",
@@ -7295,6 +7305,7 @@
"version": "0.8.17",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
+ "dev": true,
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
@@ -7308,7 +7319,8 @@
"core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
- "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+ "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
+ "dev": true
}
}
},
@@ -9280,6 +9292,7 @@
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
@@ -9777,7 +9790,8 @@
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
},
"is-string": {
"version": "1.0.4",
@@ -9885,6 +9899,7 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+ "dev": true,
"requires": {
"node-fetch": "^1.0.1",
"whatwg-fetch": ">=0.10.0"
@@ -11709,9 +11724,10 @@
}
},
"memoize-one": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-4.0.2.tgz",
- "integrity": "sha512-ucx2DmXTeZTsS4GPPUZCbULAN7kdPT1G+H49Y34JjbQ5ESc6OGhVxKvb1iKhr9v19ZB9OtnHwNnhUnNR/7Wteg=="
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-4.0.3.tgz",
+ "integrity": "sha512-QmpUu4KqDmX0plH4u+tf0riMc1KHE1+lw95cMrLlXQAFOx/xnBtwhZ52XJxd9X2O6kwKBqX32kmhbhlobD0cuw==",
+ "dev": true
},
"memory-fs": {
"version": "0.4.1",
@@ -11950,6 +11966,11 @@
"through2": "^2.0.0"
}
},
+ "mitt": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.1.3.tgz",
+ "integrity": "sha512-mUDCnVNsAi+eD6qA0HkRkwYczbLHJ49z17BGe2PYRhZL4wpZUFZGJHU7/5tmvohoma+Hdn0Vh/oJTiPEmgSruA=="
+ },
"mixin-deep": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
@@ -12215,6 +12236,7 @@
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+ "dev": true,
"requires": {
"encoding": "^0.1.11",
"is-stream": "^1.0.1"
@@ -13215,7 +13237,8 @@
"postcss-value-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
- "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
},
"prebuild-install": {
"version": "5.2.0",
@@ -13372,6 +13395,7 @@
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "dev": true,
"requires": {
"asap": "~2.0.3"
}
@@ -13852,7 +13876,8 @@
"react-is": {
"version": "16.5.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.5.2.tgz",
- "integrity": "sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ=="
+ "integrity": "sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ==",
+ "dev": true
},
"react-lifecycles-compat": {
"version": "3.0.4",
@@ -15120,7 +15145,8 @@
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
},
"sane": {
"version": "2.5.2",
@@ -15572,7 +15598,8 @@
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
- "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
+ "dev": true
},
"setprototypeof": {
"version": "1.1.0",
@@ -16562,18 +16589,28 @@
"dev": true
},
"styled-components": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.0.0.tgz",
- "integrity": "sha512-+SXoKjaXmApQHjQXNY5pxuRpnabR7vnL6J59Gtcj118ll8gsg9MM8gkjWu6Ahc0NB9kLjh3O9Ho2iun6uLunVA==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.0.3.tgz",
+ "integrity": "sha512-oEZovK4xMGAMhOA9h74dCYJsp3IwUFhEvtYe4gwTy0cBZ3a17YMxBfM2oXsEoED9f+HCM5UQZW2h297n4u8hUw==",
+ "dev": true,
"requires": {
"@emotion/is-prop-valid": "^0.6.8",
"babel-plugin-styled-components": ">= 1",
"css-to-react-native": "^2.2.2",
"memoize-one": "^4.0.0",
"prop-types": "^15.5.4",
- "react-is": "^16.3.1",
+ "react-is": "^16.6.0",
"stylis": "^3.5.0",
- "stylis-rule-sheet": "^0.0.10"
+ "stylis-rule-sheet": "^0.0.10",
+ "supports-color": "^5.5.0"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.1.tgz",
+ "integrity": "sha512-wOKsGtvTMYs7WAscmwwdM8sfRRvE17Ym30zFj3n37Qx5tHRfhenPKEPILHaHob6WoLFADmQm1ZNrE5xMCM6sCw==",
+ "dev": true
+ }
}
},
"styled-system": {
@@ -16603,12 +16640,14 @@
"stylis": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.3.tgz",
- "integrity": "sha512-TxU0aAscJghF9I3V9q601xcK3Uw1JbXvpsBGj/HULqexKOKlOEzzlIpLFRbKkCK990ccuxfXUqmPbIIo7Fq/cQ=="
+ "integrity": "sha512-TxU0aAscJghF9I3V9q601xcK3Uw1JbXvpsBGj/HULqexKOKlOEzzlIpLFRbKkCK990ccuxfXUqmPbIIo7Fq/cQ==",
+ "dev": true
},
"stylis-rule-sheet": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
- "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
+ "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==",
+ "dev": true
},
"supports-color": {
"version": "5.5.0",
@@ -17036,7 +17075,8 @@
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "dev": true
},
"to-object-path": {
"version": "0.3.0",
@@ -17264,7 +17304,8 @@
"ua-parser-js": {
"version": "0.7.18",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz",
- "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA=="
+ "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA==",
+ "dev": true
},
"uglify-js": {
"version": "3.4.9",
@@ -18758,7 +18799,8 @@
"whatwg-fetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
- "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
+ "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==",
+ "dev": true
},
"whatwg-mimetype": {
"version": "2.2.0",
diff --git a/package.json b/package.json
index 881ec91..129a86b 100644
--- a/package.json
+++ b/package.json
@@ -29,18 +29,19 @@
},
"dependencies": {
"lodash": "^4.17.11",
- "popper.js": "^1.14.4",
- "react-portal": "^4.1.5",
- "styled-system": "^3.1.11",
+ "mitt": "^1.1.3",
"polished": "^2.0.0",
+ "popper.js": "^1.14.4",
"prop-types": "^15.6.1",
"react-animations": "^1.0.0",
+ "react-portal": "^4.1.5",
"react-transition-group": "^2.5.0",
- "styled-components": "^4.0.0"
+ "styled-system": "^3.1.11"
},
"peerDependencies": {
"react": "^16.5.0",
- "react-dom": "^16.5.0"
+ "react-dom": "^16.5.0",
+ "styled-components": "^4.0.0"
},
"devDependencies": {
"@babel/core": "^7.1.2",
@@ -72,7 +73,7 @@
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-filesize": "^5.0.1",
"standard-version": "^4.4.0",
- "styled-components": "^4.0.0"
+ "styled-components": "^4.0.3"
},
"resolutions": {
"babel-core": "^7.0.0-bridge.0"
diff --git a/src/Dropdown/Dropdown.js b/src/Dropdown/Dropdown.js
index 84e0179..90a5902 100644
--- a/src/Dropdown/Dropdown.js
+++ b/src/Dropdown/Dropdown.js
@@ -4,7 +4,7 @@ import { css } from 'styled-components';
import Popper from 'popper.js';
import Box from '../Box';
import Portal from '../Portal';
-import { createComponent } from '../utils';
+import { createComponent, themeGet } from '../utils';
const DropdownTrigger = createComponent({
name: 'DropdownTrigger',
@@ -162,9 +162,15 @@ export default class Dropdown extends React.Component {
const DropdownHeader = createComponent({
name: 'DropdownHeader',
+ style: css`
+ padding: 0.75rem 1rem 0;
+ `,
+});
+
+const DropdownHeaderInner = createComponent({
+ name: 'DropdownHeaderInner',
style: css`
padding: 0 0 0.25rem;
- margin-bottom: 0.5rem;
border-bottom: 2px solid ${p => p.theme.colors.grayLight};
`,
});
@@ -182,8 +188,10 @@ Dropdown.Title = createComponent({
Dropdown.Header = ({ title, children }) => (
- {title && {title}}
- {children}
+
+ {title && {title}}
+ {children}
+
);
@@ -191,7 +199,7 @@ Dropdown.Body = createComponent({
name: 'DropdownBody',
as: Box,
style: css`
- padding: 12px;
+ padding: 1rem;
`,
});
@@ -216,8 +224,8 @@ Dropdown.Item = createComponent({
text-decoration: none;
color: inherit;
cursor: pointer;
- margin: 0 -12px;
- padding: 4px 12px;
+ margin: 0 -1rem;
+ padding: 0.25rem 1rem;
transition: 125ms background;
& + ${Dropdown.SectionTitle} {
@@ -239,7 +247,7 @@ Dropdown.Footer = createComponent({
},
style: ({ theme }) => css`
background: ${theme.colors.grayLightest};
- padding: 8px 12px;
- border-radius: 0 0 4px 4px;
+ padding: 0.75rem 1rem;
+ border-radius: 0 0 ${themeGet('radius')}px ${themeGet('radius')}px;
`,
});
diff --git a/src/Dropdown/Dropdown.mdx b/src/Dropdown/Dropdown.mdx
index 9a7a0d5..f4a6198 100644
--- a/src/Dropdown/Dropdown.mdx
+++ b/src/Dropdown/Dropdown.mdx
@@ -22,9 +22,9 @@ Easily display contextual overlays using custom trigger elements. Dropdown's pos
>
{({ close }) => (
<>
-
-
+
+
Section One
Item One
Item Two
diff --git a/src/Modal/Modal.js b/src/Modal/Modal.js
index be80eb2..2104737 100644
--- a/src/Modal/Modal.js
+++ b/src/Modal/Modal.js
@@ -15,7 +15,7 @@ const Backdrop = createComponent({
right: 0;
bottom: 0;
z-index: 1000;
- padding: 12px;
+ padding: 1rem;
display: flex;
align-items: center;
position: fixed;
diff --git a/src/Toast/Toast.mdx b/src/Toast/Toast.mdx
new file mode 100644
index 0000000..1239eed
--- /dev/null
+++ b/src/Toast/Toast.mdx
@@ -0,0 +1,61 @@
+---
+menu: Core
+name: Toast
+---
+
+import { Playground, PropsTable } from 'docz'
+import toast from './toast'
+import ToastContainer from './ToastContainer'
+import Button from '../Button';
+
+# Toast
+
+## Props
+
+
+
+## Positioning
+
+Toast positions will default to `top-center`. To change the positioning, you can either pass the `position` prop to the `` to be used as the default. You can also pass the position to each individual toast you're rendering, which will override the default.rendering.
+
+## Examples
+
+
+ <>
+ Variants
+
+
+
+
+
+
+
+
+
+
+ Positioning
+
+
+
+
+
+
+
+ >
+
diff --git a/src/Toast/ToastContainer.js b/src/Toast/ToastContainer.js
new file mode 100644
index 0000000..70be98f
--- /dev/null
+++ b/src/Toast/ToastContainer.js
@@ -0,0 +1,170 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { Transition, TransitionGroup } from 'react-transition-group';
+import * as animations from 'react-animations';
+import { css, keyframes } from 'styled-components';
+import Portal from '../Portal';
+import Flex from '../Flex';
+import Box from '../Box';
+import Icon from '../Icon';
+import { emitter } from './toast';
+import { createComponent, themeGet } from '../utils';
+import { Types, Events, Positions, PositionConfigs } from './config';
+
+const VariantToColorMap = {
+ success: 'green',
+ error: 'red',
+ warn: 'orange',
+ info: 'blue',
+};
+
+const getTransitionStyle = (state, position, duration) => {
+ const { animationIn, animationOut } = PositionConfigs[position];
+
+ switch (state) {
+ case 'entering':
+ return css`
+ animation: ${duration}ms ${keyframes`${animations[animationIn]}`};
+ `;
+
+ case 'exiting':
+ return css`
+ animation: ${duration}ms ${keyframes`${animations[animationOut]}`};
+ `;
+
+ default:
+ return css``;
+ }
+};
+
+const ToastPortal = createComponent({
+ name: 'ToastPortal',
+ style: ({ position }) => PositionConfigs[position].wrapperStyle,
+});
+
+const Toast = createComponent({
+ name: 'Toast',
+ style: ({ state, type, position, animationDuration, theme }) => css`
+ padding: 0.75rem 1rem;
+ min-width: 250px;
+ max-width: 400px;
+ cursor: pointer;
+ color: white;
+ font-weight: 600;
+ border-radius: ${themeGet('radius')}px;
+ box-shadow: ${themeGet('shadow')};
+ background: ${theme.colors[VariantToColorMap[type]]};
+ transition: 175ms;
+
+ ${getTransitionStyle(state, position, animationDuration)};
+
+ & + & {
+ margin-top: 0.5rem;
+ }
+
+ &:hover {
+ box-shadow: ${themeGet('shadowHover')};
+ }
+ `,
+});
+
+export default class ToastContainer extends Component {
+ counter = 0;
+ state = {
+ toasts: [],
+ };
+
+ static propTypes = {
+ type: PropTypes.string,
+ position: PropTypes.string,
+ timeout: PropTypes.number,
+ animationDuration: PropTypes.number,
+ autoClose: PropTypes.bool,
+ closeOnClick: PropTypes.bool,
+ showClose: PropTypes.bool,
+ };
+
+ static defaultProps = {
+ type: Types.INFO,
+ position: Positions.TOP_CENTER,
+ timeout: 5000,
+ animationDuration: 250,
+ autoClose: true,
+ closeOnClick: true,
+ showClose: true,
+ };
+
+ componentDidMount() {
+ emitter.on(Events.ADD, this.add);
+ }
+
+ componentWillUnmount() {
+ emitter.off(Events.ADD, this.add);
+ }
+
+ add = (options = {}) => {
+ const id = ++this.counter; // eslint-disable-line
+
+ const toast = {
+ ...this.props,
+ ...options,
+ id,
+ };
+
+ this.setState(
+ state => ({
+ ...state,
+ toasts: [...state.toasts, toast],
+ }),
+ () => {
+ if (toast.autoClose) {
+ setTimeout(() => {
+ this.remove(id);
+ }, toast.timeout + toast.animationDuration);
+ }
+ }
+ );
+ };
+
+ remove = id => {
+ this.setState(state => ({
+ toasts: state.toasts.filter(t => t.id !== id),
+ }));
+ };
+
+ handleToastClick = toast => {
+ if (toast.closeOnClick) {
+ this.remove(toast.id);
+ }
+ };
+
+ render() {
+ const { toasts } = this.state;
+
+ return Object.keys(Positions).map(key => {
+ const p = Positions[key];
+ return (
+
+
+
+ {toasts
+ .filter(t => t.position === p)
+ .map(toast => (
+
+ {state => (
+ this.handleToastClick(toast)}>
+
+ {toast.message}
+ {toast.showClose && this.remove(toast.id)} />}
+
+
+ )}
+
+ ))}
+
+
+
+ );
+ });
+ }
+}
diff --git a/src/Toast/config.js b/src/Toast/config.js
new file mode 100644
index 0000000..cf1ee1e
--- /dev/null
+++ b/src/Toast/config.js
@@ -0,0 +1,81 @@
+import { css } from 'styled-components';
+
+export const Events = {
+ ADD: 'add',
+ REMOVE: 'remove',
+};
+
+export const Types = {
+ SUCCESS: 'success',
+ WARN: 'warn',
+ INFO: 'info',
+ ERROR: 'error',
+};
+
+export const Positions = {
+ TOP_LEFT: 'top-left',
+ TOP_CENTER: 'top-center',
+ TOP_RIGHT: 'top-right',
+ BOTTOM_LEFT: 'bottom-left',
+ BOTTOM_CENTER: 'bottom-center',
+ BOTTOM_RIGHT: 'bottom-right',
+};
+
+export const PositionConfigs = {
+ [Positions.TOP_LEFT]: {
+ animationIn: 'slideInLeft',
+ animationOut: 'fadeOutUp',
+ wrapperStyle: css`
+ position: fixed;
+ left: 1rem;
+ top: 1rem;
+ `,
+ },
+ [Positions.TOP_CENTER]: {
+ animationIn: 'slideInDown',
+ animationOut: 'fadeOutUp',
+ wrapperStyle: css`
+ position: fixed;
+ left: 50%;
+ transform: translateX(-50%);
+ top: 1rem;
+ `,
+ },
+ [Positions.TOP_RIGHT]: {
+ animationIn: 'slideInRight',
+ animationOut: 'fadeOutUp',
+ wrapperStyle: css`
+ position: fixed;
+ right: 1rem;
+ top: 1rem;
+ `,
+ },
+ [Positions.BOTTOM_LEFT]: {
+ animationIn: 'slideInLeft',
+ animationOut: 'fadeOutDown',
+ wrapperStyle: css`
+ position: fixed;
+ left: 1rem;
+ bottom: 1rem;
+ `,
+ },
+ [Positions.BOTTOM_CENTER]: {
+ animationIn: 'slideInUp',
+ animationOut: 'fadeOutDown',
+ wrapperStyle: css`
+ position: fixed;
+ left: 50%;
+ transform: translateX(-50%);
+ border-bottom-color: 1rem;
+ `,
+ },
+ [Positions.BOTTOM_RIGHT]: {
+ animationIn: 'slideInLeft',
+ animationOut: 'fadeOutDown',
+ wrapperStyle: css`
+ position: fixed;
+ right: 1rem;
+ bottom: 1rem;
+ `,
+ },
+};
diff --git a/src/Toast/index.js b/src/Toast/index.js
new file mode 100644
index 0000000..a9faf5a
--- /dev/null
+++ b/src/Toast/index.js
@@ -0,0 +1,2 @@
+export { default as toast } from './toast';
+export { default as ToastContainer } from './ToastContainer';
diff --git a/src/Toast/toast.js b/src/Toast/toast.js
new file mode 100644
index 0000000..47f63a1
--- /dev/null
+++ b/src/Toast/toast.js
@@ -0,0 +1,30 @@
+import EventEmitter from 'mitt';
+import { Events } from './config';
+
+export const emitter = new EventEmitter();
+
+const toast = (options = {}) => {
+ if (!options.message) {
+ throw new Error('Molekule: Toast requires a message');
+ }
+
+ emitter.emit(Events.ADD, options);
+};
+
+toast.success = (message, options = {}) => {
+ toast({ message, type: 'success', ...options });
+};
+
+toast.error = (message, options = {}) => {
+ toast({ message, type: 'error', ...options });
+};
+
+toast.warn = (message, options = {}) => {
+ toast({ message, type: 'warn', ...options });
+};
+
+toast.info = (message, options = {}) => {
+ toast({ message, type: 'info', ...options });
+};
+
+export default toast;
diff --git a/src/theme.js b/src/theme.js
index 9d41fd5..9531615 100644
--- a/src/theme.js
+++ b/src/theme.js
@@ -2,6 +2,7 @@ import { merge } from 'lodash';
export default (overrides = {}) => {
const shadow = '0 3px 6px hsla(0,0%,60%,.1), 0 3px 6px hsla(0,0%,60%,.15), 0 -1px 2px hsla(0,0%,60%,.02)';
+ const shadowHover = '0 6px 9px hsla(0,0%,60%,.2), 0 6px 9px hsla(0,0%,60%,.2), 0 -1px 2px hsla(0,0%,60%,.08)';
const colors = merge(
{
@@ -91,6 +92,7 @@ export default (overrides = {}) => {
radii,
radius: 4,
shadow,
+ shadowHover,
typography,
variants,
};
diff --git a/src/utils.js b/src/utils.js
index 6580bcf..2eadfa7 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,6 +1,6 @@
import { get, kebabCase } from 'lodash';
-import { themeGet as styledThemeGet } from 'styled-system';
import styled from 'styled-components';
+import { themeGet as styledThemeGet } from 'styled-system';
export const themeGet = styledThemeGet;