Repository: t4t5/sweetalert Branch: master Commit: d927c9bbc66b Files: 111 Total size: 4.8 MB Directory structure: gitextract_94viukuh/ ├── .babelrc ├── .editorconfig ├── .github/ │ └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── docs/ │ ├── CNAME │ ├── assets/ │ │ ├── css/ │ │ │ ├── app.css │ │ │ ├── guide.css │ │ │ ├── header.css │ │ │ ├── highlight.css │ │ │ ├── index.css │ │ │ ├── normalize.css │ │ │ ├── table.css │ │ │ └── variables.css │ │ └── js/ │ │ ├── add-preview-buttons.js │ │ ├── index.js │ │ └── landing-text-rotater.js │ ├── docs/ │ │ └── index.html │ ├── guides/ │ │ └── index.html │ └── index.html ├── docs-src/ │ ├── assets/ │ │ ├── css/ │ │ │ ├── app.styl │ │ │ ├── guide.styl │ │ │ ├── header.styl │ │ │ ├── highlight.styl │ │ │ ├── index.styl │ │ │ ├── normalize.styl │ │ │ ├── table.styl │ │ │ └── variables.styl │ │ └── js/ │ │ ├── add-preview-buttons.js │ │ ├── index.js │ │ └── landing-text-rotater.js │ ├── docs/ │ │ └── index.md │ ├── guides/ │ │ └── index.md │ ├── index.hbs │ ├── layout-docs.hbs │ ├── layout-guides.hbs │ └── layout.hbs ├── package.json ├── postcss.config.js ├── src/ │ ├── core.ts │ ├── css/ │ │ ├── button-loader.css │ │ ├── buttons.css │ │ ├── content.css │ │ ├── icons/ │ │ │ ├── error.css │ │ │ ├── info.css │ │ │ ├── success.css │ │ │ └── warning.css │ │ ├── icons.css │ │ └── text.css │ ├── modules/ │ │ ├── actions.ts │ │ ├── class-list/ │ │ │ └── index.ts │ │ ├── event-listeners.ts │ │ ├── init/ │ │ │ ├── buttons.ts │ │ │ ├── content.ts │ │ │ ├── icon.ts │ │ │ ├── index.ts │ │ │ ├── modal.ts │ │ │ ├── overlay.ts │ │ │ └── text.ts │ │ ├── markup/ │ │ │ ├── buttons.ts │ │ │ ├── content.ts │ │ │ ├── icons.ts │ │ │ ├── index.ts │ │ │ ├── modal.ts │ │ │ └── overlay.ts │ │ ├── options/ │ │ │ ├── buttons.ts │ │ │ ├── content.ts │ │ │ ├── deprecations.ts │ │ │ └── index.ts │ │ ├── state.ts │ │ └── utils.ts │ ├── polyfills.js │ ├── sweetalert.css │ ├── sweetalert.d.ts │ └── sweetalert.js ├── test/ │ ├── actions.test.ts │ ├── button-options.test.ts │ ├── buttons.test.ts │ ├── content.test.ts │ ├── icons.test.ts │ ├── index.test.ts │ └── utils.ts ├── tsconfig.json ├── tslint.json ├── typings/ │ ├── core.d.ts │ ├── modules/ │ │ ├── actions.d.ts │ │ ├── class-list/ │ │ │ └── index.d.ts │ │ ├── event-listeners.d.ts │ │ ├── init/ │ │ │ ├── buttons.d.ts │ │ │ ├── content.d.ts │ │ │ ├── icon.d.ts │ │ │ ├── index.d.ts │ │ │ ├── modal.d.ts │ │ │ ├── overlay.d.ts │ │ │ └── text.d.ts │ │ ├── markup/ │ │ │ ├── buttons.d.ts │ │ │ ├── content.d.ts │ │ │ ├── icons.d.ts │ │ │ ├── index.d.ts │ │ │ ├── modal.d.ts │ │ │ └── overlay.d.ts │ │ ├── options/ │ │ │ ├── buttons.d.ts │ │ │ ├── content.d.ts │ │ │ ├── deprecations.d.ts │ │ │ └── index.d.ts │ │ ├── state.d.ts │ │ └── utils.d.ts │ └── sweetalert.d.ts └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": ["env"], "plugins": [ ["transform-runtime", { "polyfill": false, "regenerator": true }] ] } ================================================ FILE: .editorconfig ================================================ # editorconfig.org root = true [*] charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space insert_final_newline = true trim_trailing_whitespace = true ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ ================================================ FILE: .gitignore ================================================ *.codekit *.sass-cache *.DS_STORE node_modules npm-debug.log types dist ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "6.9.1" script: npm run buildtest ================================================ FILE: LICENSE.md ================================================ The MIT License (MIT) Copyright (c) 2014-present Tristan Edwards Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

SweetAlert

A beautiful replacement for JavaScript's "alert"

npm version Build status

A success modal

## Installation ```bash $ npm install --save sweetalert ``` ## Usage ```javascript import swal from 'sweetalert'; swal("Hello world!"); ``` ## Upgrading from 1.X Many improvements and breaking changes have been introduced in the 2.0 release. Make sure you read the [upgrade guide](https://sweetalert.js.org/guides/#upgrading-from-1x) to avoid nasty surprises! ## Guides - [Installation](https://sweetalert.js.org/guides/#installation) - [Getting started](https://sweetalert.js.org/guides/#getting-started) - [Advanced examples](https://sweetalert.js.org/guides/#advanced-examples) - [Using with libraries](https://sweetalert.js.org/guides/#using-with-libraries) - [Upgrading from 1.X](https://sweetalert.js.org/guides/#upgrading-from-1x) ## Documentation - [Configuration](https://sweetalert.js.org/docs/#configuration) - [Methods](https://sweetalert.js.org/docs/#methods) - [Theming](https://sweetalert.js.org/docs/#theming) ## Examples ### An error message: ```javascript swal("Oops!", "Something went wrong!", "error"); ``` ### A warning message, with a function attached to the confirm message: - Using promises: ```javascript swal({ title: "Are you sure?", text: "Are you sure that you want to leave this page?", icon: "warning", dangerMode: true, }) .then(willDelete => { if (willDelete) { swal("Deleted!", "Your imaginary file has been deleted!", "success"); } }); ``` - Using async/await: ```javascript const willDelete = await swal({ title: "Are you sure?", text: "Are you sure that you want to delete this file?", icon: "warning", dangerMode: true, }); if (willDelete) { swal("Deleted!", "Your imaginary file has been deleted!", "success"); } ``` ### A prompt modal, where the user's input is logged: - Using promises: ```javascript swal("Type something:", { content: "input", }) .then((value) => { swal(`You typed: ${value}`); }); ``` - Using async/await: ```javascript const value = await swal("Type something:", { content: "input", }); swal(`You typed: ${value}`); ``` ### In combination with Fetch: - Using promises: ```javascript swal({ text: "Wanna log some information about Bulbasaur?", button: { text: "Search!", closeModal: false, }, }) .then(willSearch => { if (willSearch) { return fetch("http://pokeapi.co/api/v2/pokemon/1"); } }) .then(result => result.json()) .then(json => console.log(json)) .catch(err => { swal("Oops!", "Seems like we couldn't fetch the info", "error"); }); ``` - Using async/await: ```javascript const willSearch = await swal({ text: "Wanna log some information about Bulbasaur?", button: { text: "Search!", closeModal: false, }, }); if (willSearch) { try { const result = await fetch("http://pokeapi.co/api/v2/pokemon/1"); const json = await result.json(); console.log(json); } catch (err) { swal("Oops!", "Seems like we couldn't fetch the info", "error"); } } ``` ## Using with React SweetAlert has tools for [integrating with your favourite rendering library.](https://sweetalert.js.org/guides/#using-with-libraries). If you're using React, you can install [SweetAlert with React](https://www.npmjs.com/package/@sweetalert/with-react) in addition to the main library, and easily add React components to your alerts like this: ```javascript import React from 'react' import swal from '@sweetalert/with-react' swal(

Hello world!

This is now rendered with JSX!

) ``` [Read more about integrating with React](https://sweetalert.js.org/guides/#using-react) ## Contributing ### If you're changing the core library: 1. Make changes in the `src` folder. 2. Preview changes by running `npm run docs` 3. Submit pull request ### If you're changing the documentation: 1. Make changes in the `docs-src` folder. 2. Preview changes by running `npm run docs` 3. Run `npm run builddocs` to compile the changes to the `docs` folder 4. Submit pull request ## Contributors This project exists thanks to all the people who contribute. [[Contribute](https://github.com/t4t5/sweetalert#contributing)]. ## Backers Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/SweetAlert#backer)] ## Sponsors Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/SweetAlert#sponsor)] ================================================ FILE: docs/CNAME ================================================ sweetalert.js.org ================================================ FILE: docs/assets/css/app.css ================================================ body { margin: 0; padding: 0; } a { text-decoration: none; color: #f27474; } p a:hover { text-decoration: underline; } button { cursor: pointer; } button:focus { outline: none; } ul { list-style-type: none; margin: 0; } .global-header { background-color: #fff; box-shadow: 0 1px 15px 0 rgba(192,72,25,0.32); height: 80px; position: fixed; top: 0; left: 0; right: 0; z-index: 100; } .global-header .logo { width: 162px; height: 36px; background-image: url("/assets/images/logo.svg"); background-size: contain; background-repeat: no-repeat; background-position: center center; float: left; margin-top: 22px; margin-left: 15px; } @media all and (max-width: 600px) { .global-header .logo { float: none; height: 29px; display: block; margin: 0 auto; margin-top: 10px; } } .global-header nav { font-size: 17px; color: #f27474; float: right; margin-top: 29px; } @media all and (max-width: 600px) { .global-header nav { float: none; text-align: center; font-size: 16px; margin-top: 10px; } } .global-header nav a { position: relative; cursor: pointer; } .global-header nav a::before { content: ""; background-color: #f27474; height: 3px; border-radius: 2px; position: absolute; left: 0; right: 0; bottom: -5px; display: none; } .global-header nav a:hover::before { display: block; } .global-header nav .github-icon { width: 26px; height: 25px; background-image: url("/assets/images/github.svg"); display: inline-block; vertical-align: middle; position: relative; top: -3px; } .global-header ul { white-space: nowrap; padding: 0; } .global-header ul li { display: inline-block; margin: 0 15px; } .highlight { background-color: #f8f8f8; padding: 10px 23px; font-size: 14px; line-height: normal; color: rgba(0,0,0,0.62); overflow-x: auto; } .highlight .editor { font-family: 'Inconsolata', monospace; } .highlight .line { margin: 6px 0; } .highlight.bash .line::before { content: "$ "; opacity: 0.5; } .highlight .string { color: #8858d2; } .highlight .html.name.tag { color: #4ac14a; } .highlight .html.attribute-name { color: #b646c1; } .highlight .js.name.function { color: #f27474; } .highlight .js.boolean, .highlight .js.numeric { color: #4ac14a; } .highlight .js.control, .highlight .js.assignment { color: #b646c1; } .highlight .js.storage, .highlight .js.variable { color: #00a9ff; } .highlight .js.comment { color: rgba(0,0,0,0.3); } .highlight .js.function { color: inherit; } .highlight .js.variable.other, .highlight .js.variable.parameter { color: inherit; } .highlight .js.storage.class, .highlight .js.class + * + .storage.modifier { color: #b646c1; } .highlight .css.selector { color: #4ac14a; } .highlight .css.property-name { color: #00a9ff; } .highlight .css.property-value { color: #8858d2; } .highlight .css.separator, .highlight .css.terminator { color: rgba(0,0,0,0.62); } .landing-top { height: 370px; position: relative; padding-top: 80px; } @media all and (max-width: 1000px) { .landing-top { height: 600px; } } .landing-top .bg { background-image: linear-gradient(-132deg, #ff7d79 0%, #f28b74 92%); position: absolute; left: 0; right: 0; top: 0; bottom: 0; -webkit-clip-path: url("#top-transition-clip-shape"); clip-path: url("#top-transition-clip-shape"); will-change: transform; /* For Safari */ /* * For some reason, clip path makes the whole page * flicker in Mobile Safari. * So we disable it for mobile. */ } @media all and (max-width: 600px) { .landing-top .bg { -webkit-clip-path: none; clip-path: none; } } .landing-top .swal-modal-example { transform: none; opacity: 1; height: 292px; width: 409px; margin: 20px; background-color: #fff; box-shadow: 0 5px 22px 0 rgba(0,0,0,0.2); border-radius: 8px; margin-top: 59px; text-align: center; display: inline-block; vertical-align: middle; overflow: hidden; transition: height 0.3s; position: absolute; z-index: 10; top: 0; left: 0; } @media all and (max-width: 1000px) { .landing-top .swal-modal-example { position: relative; display: block; margin: 20px auto; } } @media all and (max-width: 450px) { .landing-top .swal-modal-example { width: 100%; } } .landing-top .swal-modal-example[data-type="success"] { height: 292px; } .landing-top .swal-modal-example[data-type="warning"] { height: 325px; } .landing-top .swal-modal-example .swal-title { padding-top: 10px; } .landing-top .swal-modal-example .swal-text { color: rgba(0,0,0,0.48); margin-top: 6px; } .landing-top .modal-content-overlay { position: absolute; top: 0; bottom: 0; left: 0; right: 0; background-color: #fff; z-index: 2; pointer-events: none; opacity: 0; transition: opacity 0.2s; } .landing-top .modal-content-overlay.show { opacity: 1; } .landing-top .example-content { display: none; } .landing-top .example-content.show { display: block; } .landing-top .desc { display: inline-block; position: relative; color: #fff; margin-left: 50px; max-width: calc(100% - 409px - 112px); vertical-align: middle; margin-top: 61px; padding-left: 473px; } @media all and (max-width: 1000px) { .landing-top .desc { display: block; max-width: none; padding-left: 0; margin-left: 0; text-align: center; } } .landing-top h2 { font-size: 30px; line-height: 51px; font-weight: 300; } @media all and (max-width: 380px) { .landing-top h2 { font-size: 25px; margin-top: -20px; } } .landing-top .text-rotater { display: block; height: 57px; overflow: hidden; } .landing-top .text-rotater span { display: block; transition: transform 0.4s; } .landing-top .text-rotater.slide-up span { transform: translateY(-51px); } .landing-top .install { background: rgba(120,40,40,0.32); border-radius: 7px; max-width: 357px; padding: 12px; } @media all and (max-width: 1000px) { .landing-top .install { margin: 0 auto; text-align: left; } } .landing-top .install .button { background: rgba(255,255,255,0.39); width: 12px; height: 12px; border-radius: 50%; display: inline-block; } .landing-top .install .command { font-family: 'Inconsolata', monospace; padding: 12px; padding-left: 30px; } .landing-top .install .command::before { content: "$"; opacity: 0.5; transform: rotate(8deg); font-size: 20px; position: absolute; margin-left: -27px; margin-top: -2px; } .comparison-container { padding-bottom: 70px; text-align: center; } .comparison-container h3 { font-size: 22px; color: #b49993; font-weight: 400; display: block; text-align: center; margin-top: 93px; margin-bottom: 80px; } .comparison-container .code-container { text-align: center; width: calc(50% - 60px); display: inline-block; vertical-align: middle; } @media all and (max-width: 600px) { .comparison-container .code-container { width: 100%; } } .comparison-container .versus { width: 35px; height: 33px; background-image: url("/assets/images/vs.svg"); display: inline-block; vertical-align: middle; margin: 0 30px; } @media all and (max-width: 600px) { .comparison-container .versus { margin: 30px; margin-bottom: -10px; } } .comparison-container h5 { font-size: 13px; color: rgba(0,0,0,0.21); text-transform: uppercase; text-align: left; margin-bottom: 15px; } .comparison-container h5.swal-logo { text-indent: -9999999px; margin-top: 2px; } .comparison-container h5.swal-logo::after { content: ""; background-image: url("/assets/images/logo-small.svg"); width: 91px; height: 20px; display: block; } .comparison-container .highlight { text-align: left; padding: 16px 23px; } .comparison-container .highlight span { margin: 0 -4px; } .comparison-container .remark { font-size: 20px; color: #f27474; margin-top: 80px; } .comparison-container .get-started-button { background-color: #f27474; color: #fff; border-radius: 8px; font-size: 22px; padding: 14px 55px; margin: 20px 0; display: inline-block; } .customize-container { background-color: #999eaf; text-align: center; color: #fff; text-align: center; background-image: url("/assets/images/pattern.png"); background-image: -webkit-image-set(url("/assets/images/pattern.png") 1x, url("/assets/images/pattern@2x.png") 2x); padding: 40px 0; -webkit-clip-path: url("#customization-transition-clip-shape"); clip-path: url("#customization-transition-clip-shape"); will-change: transform; /* For Safari */ } @media all and (max-width: 600px) { .customize-container { -webkit-clip-path: none; clip-path: none; } } .customize-container h3 { font-weight: 400; font-size: 20px; padding: 50px 0; } .customize-container .example-modals { background-image: url("/assets/images/modal-examples.png"); background-image: -webkit-image-set(url("/assets/images/modal-examples.png") 1x, url("/assets/images/modal-examples@2x.png") 2x); height: 284px; background-size: auto 100%; background-position: 0 0; animation: scrollExamples 80s infinite linear; } .customize-container .view-api-button { border: 3px solid #fff; border-radius: 6px; color: #fff; padding: 12px 52px; font-size: 18px; margin-top: 60px; display: inline-block; } @-moz-keyframes scrollExamples { 0% { background-position: 0 0; } 100% { background-position: -2146px 0; } } @-webkit-keyframes scrollExamples { 0% { background-position: 0 0; } 100% { background-position: -2146px 0; } } @-o-keyframes scrollExamples { 0% { background-position: 0 0; } 100% { background-position: -2146px 0; } } @keyframes scrollExamples { 0% { background-position: 0 0; } 100% { background-position: -2146px 0; } } .page-content { $mobile-breakpoint: 880px; } .page-content table { border-collapse: collapse; border: none; width: 100%; } .page-content th { font-size: 17px; color: rgba(0,0,0,0.34); padding: 20px 15px; text-transform: capitalize; font-weight: 400; } .page-content thead > tr { border-bottom: 2px solid rgba(0,0,0,0.1); } .page-content tr { text-align: left; box-shadow: 0px -1px 0px rgba(0,0,0,0.15); } .page-content tr:first-child { box-shadow: none; } .page-content td { padding: 17px; } .page-content td:first-child > code { color: #2e9fef; background: none; border: none; font-size: 16px; padding: 0; } @media all and (min-width: mobile-breakpoint) { .page-content tbody tr:nth-child(1) { box-shadow: none; } } @media all and (max-width: mobile-breakpoint) { .page-content table, .page-content thead, .page-content tbody, .page-content th, .page-content td, .page-content tr { display: block; } .page-content thead tr { position: absolute; top: -9999px; left: -9999px; } .page-content tr { margin-top: -1px; box-shadow: 0px -1px 0px 0px rgba(0,0,0,0.27); } .page-content td { /* Behave like a "row" */ border: none; border-bottom: 1px solid #eee; position: relative; padding-left: mobile-padding; min-height: 20px; } .page-content td::before { /* Now like a table header */ position: absolute; /* Top/left values mimic padding */ top: 15px; left: 15px; width: mobile-padding; width: calc((mobile-padding - 35px)); overflow: hidden; text-overflow: ellipsis; padding-right: 10px; white-space: nowrap; color: rgba(0,0,0,0.54); font-family: page-font; font-size: 16px; text-transform: capitalize; content: attr(data-name); } } .doc-container { overflow: hidden; } .side-menu { width: 225px; float: left; padding-left: 20px; position: fixed; top: 88px; } @media all and (max-width: 600px) { .side-menu { float: none; position: static; margin-top: 120px; text-align: center; width: 100%; margin-bottom: -60px; padding-left: 0; } } .side-menu .title { font-size: 20px; color: rgba(0,0,0,0.63); font-weight: 600; margin-top: 50px; margin-bottom: 36px; } .side-menu a { font-size: 17px; color: rgba(0,0,0,0.42); display: block; margin: 18px 0; } .side-menu a:hover { color: rgba(0,0,0,0.6); } .page-content { float: left; width: calc(100% - 225px - 20px); margin-left: 225px; min-height: 220px; margin-top: 16px; font-size: 16px; color: rgba(0,0,0,0.59); line-height: 29px; } @media all and (max-width: 600px) { .page-content { float: none; margin-left: 0; width: 100%; } } .page-content h1 { padding-top: 90px; border-bottom: 1px solid rgba(0,0,0,0.15); padding-bottom: 20px; margin-bottom: 0; } .page-content h1::before { content: "#"; position: absolute; margin-left: -23px; font-size: 24px; margin-top: 3px; color: #f38270; font-weight: 500; } .page-content h1 a { font-size: 30px; color: rgba(0,0,0,0.85); font-weight: 600; } .page-content h2, .page-content h3 { font-size: 20px; margin-top: -70px; padding-top: 100px; } .page-content h2 a, .page-content h3 a { color: rgba(0,0,0,0.7); } .page-content ul { list-style-type: disc; margin-bottom: 20px; } .page-content ul li { margin: 5px 0; } .page-content img { max-width: 100%; } .page-content.api > ul { list-style-type: none; padding-left: 30px; } .page-content.api > ul > li h3::before { content: ""; width: 8px; height: 8px; position: absolute; border-radius: 50%; background-color: #f27474; margin-left: -27px; margin-top: 12px; } .page-content code { font-family: 'Inconsolata', monospace; padding: 3px 6px; border-radius: 2px; border: 1px solid rgba(0,0,0,0.12); background: #f8f8f8; font-size: 14px; color: #f27474; } .page-content kbd { display: inline-block; padding: 3px 5px; font-size: 11px; line-height: 10px; color: #444d56; vertical-align: middle; background-color: #fafbfc; border: solid 1px #c6cbd1; border-bottom-color: #959da5; border-radius: 3px; box-shadow: inset 0 -1px 0 #959da5; font-family: sans-serif; } .page-content preview-button { /* Matches the "real" button's height: */ display: block; height: 44px; } .page-content button.preview { margin: 20px auto; width: 110px; display: block; position: relative; z-index: 2; } .page-content figcaption { font-style: italic; } .swal-modal.red-bg { background-color: rgba(255,0,0,0.28); } .mood-btn { background: none; border: none; width: 28px; height: 28px; background-image: url("/assets/images/mood-sad.png"); background-size: 28px 28px; padding: 4px; background-position: center center; box-sizing: content-box; background-repeat: no-repeat; margin: 0 7px; position: relative; overflow: hidden; border-radius: 3px; } .mood-btn:hover::after { content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.03); } .mood-btn[data-rating="2"] { background-image: url("/assets/images/mood-neutral.png"); } .mood-btn[data-rating="3"] { background-image: url("/assets/images/mood-happy.png"); } body { font-family: 'Lato', 'Helvetica Neue', Helvetica, sans-serif; } svg.hidden { display: block; } .page-container { max-width: 1000px; margin: 0 auto; padding: 0 10px; position: relative; } .preview { background-color: #a3dd82; box-shadow: 0 2px 8px 0 rgba(0,0,0,0.07); border-radius: 4px; border: none; color: #fff; font-size: 15px; color: #fff; padding: 9px 18px; margin-top: 20px; } .preview::before { content: ""; width: 0; height: 0; border-top: 6px solid transparent; border-bottom: 6px solid transparent; border-left: 10px solid #fff; display: inline-block; margin-right: 5px; } .preview:hover { background-color: #98d973; } footer { padding: 40px 20px; text-align: center; color: #728194; } footer .love-icon { background-image: url("/assets/images/heart-icon.svg"); width: 22px; height: 20px; display: inline-block; vertical-align: middle; margin: 0 5px; position: relative; top: -2px; } ================================================ FILE: docs/assets/css/guide.css ================================================ .page-content { $mobile-breakpoint: 880px; } .page-content table { border-collapse: collapse; border: none; width: 100%; } .page-content th { font-size: 17px; color: rgba(0,0,0,0.34); padding: 20px 15px; text-transform: capitalize; font-weight: 400; } .page-content thead > tr { border-bottom: 2px solid rgba(0,0,0,0.1); } .page-content tr { text-align: left; box-shadow: 0px -1px 0px rgba(0,0,0,0.15); } .page-content tr:first-child { box-shadow: none; } .page-content td { padding: 17px; } .page-content td:first-child > code { color: #2e9fef; background: none; border: none; font-size: 16px; padding: 0; } @media all and (min-width: mobile-breakpoint) { .page-content tbody tr:nth-child(1) { box-shadow: none; } } @media all and (max-width: mobile-breakpoint) { .page-content table, .page-content thead, .page-content tbody, .page-content th, .page-content td, .page-content tr { display: block; } .page-content thead tr { position: absolute; top: -9999px; left: -9999px; } .page-content tr { margin-top: -1px; box-shadow: 0px -1px 0px 0px rgba(0,0,0,0.27); } .page-content td { /* Behave like a "row" */ border: none; border-bottom: 1px solid #eee; position: relative; padding-left: mobile-padding; min-height: 20px; } .page-content td::before { /* Now like a table header */ position: absolute; /* Top/left values mimic padding */ top: 15px; left: 15px; width: mobile-padding; width: calc((mobile-padding - 35px)); overflow: hidden; text-overflow: ellipsis; padding-right: 10px; white-space: nowrap; color: rgba(0,0,0,0.54); font-family: page-font; font-size: 16px; text-transform: capitalize; content: attr(data-name); } } .doc-container { overflow: hidden; } .side-menu { width: 225px; float: left; padding-left: 20px; position: fixed; top: 88px; } @media all and (max-width: $phablet-width) { .side-menu { float: none; position: static; margin-top: 120px; text-align: center; width: 100%; margin-bottom: -60px; padding-left: 0; } } .side-menu .title { font-size: 20px; color: rgba(0,0,0,0.63); font-weight: 600; margin-top: 50px; margin-bottom: 36px; } .side-menu a { font-size: 17px; color: rgba(0,0,0,0.42); display: block; margin: 18px 0; } .side-menu a:hover { color: rgba(0,0,0,0.6); } .page-content { float: left; width: calc(100% - 225px - 20px); margin-left: 225px; min-height: 220px; margin-top: 16px; font-size: 16px; color: rgba(0,0,0,0.59); line-height: 29px; } @media all and (max-width: $phablet-width) { .page-content { float: none; margin-left: 0; width: 100%; } } .page-content h1 { padding-top: 90px; border-bottom: 1px solid rgba(0,0,0,0.15); padding-bottom: 20px; margin-bottom: 0; } .page-content h1::before { content: "#"; position: absolute; margin-left: -23px; font-size: 24px; margin-top: 3px; color: #f38270; font-weight: 500; } .page-content h1 a { font-size: 30px; color: rgba(0,0,0,0.85); font-weight: 600; } .page-content h2, .page-content h3 { font-size: 20px; margin-top: -70px; padding-top: 100px; } .page-content h2 a, .page-content h3 a { color: rgba(0,0,0,0.7); } .page-content ul { list-style-type: disc; margin-bottom: 20px; } .page-content ul li { margin: 5px 0; } .page-content img { max-width: 100%; } .page-content.api > ul { list-style-type: none; padding-left: 30px; } .page-content.api > ul > li h3::before { content: ""; width: 8px; height: 8px; position: absolute; border-radius: 50%; background-color: $main-color; margin-left: -27px; margin-top: 12px; } .page-content code { font-family: $code-font; padding: 3px 6px; border-radius: 2px; border: 1px solid rgba(0,0,0,0.12); background: #f8f8f8; font-size: 14px; color: $main-color; } .page-content kbd { display: inline-block; padding: 3px 5px; font-size: 11px; line-height: 10px; color: #444d56; vertical-align: middle; background-color: #fafbfc; border: solid 1px #c6cbd1; border-bottom-color: #959da5; border-radius: 3px; box-shadow: inset 0 -1px 0 #959da5; font-family: sans-serif; } .page-content preview-button { /* Matches the "real" button's height: */ display: block; height: 44px; } .page-content button.preview { margin: 20px auto; width: 110px; display: block; position: relative; z-index: 2; } .page-content figcaption { font-style: italic; } .swal-modal.red-bg { background-color: rgba(255,0,0,0.28); } .mood-btn { background: none; border: none; width: 28px; height: 28px; background-image: url("/assets/images/mood-sad.png"); background-size: 28px 28px; padding: 4px; background-position: center center; box-sizing: content-box; background-repeat: no-repeat; margin: 0 7px; position: relative; overflow: hidden; border-radius: 3px; } .mood-btn:hover::after { content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.03); } .mood-btn[data-rating="2"] { background-image: url("/assets/images/mood-neutral.png"); } .mood-btn[data-rating="3"] { background-image: url("/assets/images/mood-happy.png"); } ================================================ FILE: docs/assets/css/header.css ================================================ .global-header { background-color: #fff; box-shadow: 0 1px 15px 0 rgba(192,72,25,0.32); height: $header-height; position: fixed; top: 0; left: 0; right: 0; z-index: 100; } .global-header .logo { width: 162px; height: 36px; background-image: url("/assets/images/logo.svg"); background-size: contain; background-repeat: no-repeat; background-position: center center; float: left; margin-top: 22px; margin-left: 15px; } @media all and (max-width: $phablet-width) { .global-header .logo { float: none; height: 29px; display: block; margin: 0 auto; margin-top: 10px; } } .global-header nav { font-size: 17px; color: $main-color; float: right; margin-top: 29px; } @media all and (max-width: $phablet-width) { .global-header nav { float: none; text-align: center; font-size: 16px; margin-top: 10px; } } .global-header nav a { position: relative; cursor: pointer; } .global-header nav a::before { content: ""; background-color: $main-color; height: 3px; border-radius: 2px; position: absolute; left: 0; right: 0; bottom: -5px; display: none; } .global-header nav a:hover::before { display: block; } .global-header nav .github-icon { width: 26px; height: 25px; background-image: url("/assets/images/github.svg"); display: inline-block; vertical-align: middle; position: relative; top: -3px; } .global-header ul { white-space: nowrap; padding: 0; } .global-header ul li { display: inline-block; margin: 0 15px; } ================================================ FILE: docs/assets/css/highlight.css ================================================ .highlight { background-color: #f8f8f8; padding: 10px 23px; font-size: 14px; line-height: normal; color: rgba(0,0,0,0.62); overflow-x: auto; } .highlight .editor { font-family: $code-font; } .highlight .line { margin: 6px 0; } .highlight.bash .line::before { content: "$ "; opacity: 0.5; } .highlight .string { color: #8858d2; } .highlight .html.name.tag { color: #4ac14a; } .highlight .html.attribute-name { color: #b646c1; } .highlight .js.name.function { color: $main-color; } .highlight .js.boolean, .highlight .js.numeric { color: #4ac14a; } .highlight .js.control, .highlight .js.assignment { color: #b646c1; } .highlight .js.storage, .highlight .js.variable { color: #00a9ff; } .highlight .js.comment { color: rgba(0,0,0,0.3); } .highlight .js.function { color: inherit; } .highlight .js.variable.other, .highlight .js.variable.parameter { color: inherit; } .highlight .js.storage.class, .highlight .js.class + * + .storage.modifier { color: #b646c1; } .highlight .css.selector { color: #4ac14a; } .highlight .css.property-name { color: #00a9ff; } .highlight .css.property-value { color: #8858d2; } .highlight .css.separator, .highlight .css.terminator { color: rgba(0,0,0,0.62); } ================================================ FILE: docs/assets/css/index.css ================================================ .landing-top { height: 370px; position: relative; padding-top: $header-height; } @media all and (max-width: $tablet-width) { .landing-top { height: 600px; } } .landing-top .bg { background-image: linear-gradient(-132deg, #ff7d79 0%, #f28b74 92%); position: absolute; left: 0; right: 0; top: 0; bottom: 0; -webkit-clip-path: url("#top-transition-clip-shape"); clip-path: url("#top-transition-clip-shape"); will-change: transform; /* For Safari */ /* * For some reason, clip path makes the whole page * flicker in Mobile Safari. * So we disable it for mobile. */ } @media all and (max-width: $phablet-width) { .landing-top .bg { -webkit-clip-path: none; clip-path: none; } } .landing-top .swal-modal-example { transform: none; opacity: 1; height: 292px; width: 409px; margin: 20px; background-color: #fff; box-shadow: 0 5px 22px 0 rgba(0,0,0,0.2); border-radius: 8px; margin-top: 59px; text-align: center; display: inline-block; vertical-align: middle; overflow: hidden; transition: height 0.3s; position: absolute; z-index: 10; top: 0; left: 0; } @media all and (max-width: $tablet-width) { .landing-top .swal-modal-example { position: relative; display: block; margin: 20px auto; } } @media all and (max-width: $mobile-width) { .landing-top .swal-modal-example { width: 100%; } } .landing-top .swal-modal-example[data-type="success"] { height: 292px; } .landing-top .swal-modal-example[data-type="warning"] { height: 325px; } .landing-top .swal-modal-example .swal-title { padding-top: 10px; } .landing-top .swal-modal-example .swal-text { color: rgba(0,0,0,0.48); margin-top: 6px; } .landing-top .modal-content-overlay { position: absolute; top: 0; bottom: 0; left: 0; right: 0; background-color: #fff; z-index: 2; pointer-events: none; opacity: 0; transition: opacity 0.2s; } .landing-top .modal-content-overlay.show { opacity: 1; } .landing-top .example-content { display: none; } .landing-top .example-content.show { display: block; } .landing-top .desc { display: inline-block; position: relative; color: #fff; margin-left: 50px; max-width: calc(100% - 409px - 112px); vertical-align: middle; margin-top: 61px; padding-left: 473px; } @media all and (max-width: $tablet-width) { .landing-top .desc { display: block; max-width: none; padding-left: 0; margin-left: 0; text-align: center; } } .landing-top h2 { font-size: 30px; line-height: 51px; font-weight: 300; } @media all and (max-width: $small-width) { .landing-top h2 { font-size: 25px; margin-top: -20px; } } .landing-top .text-rotater { display: block; height: 57px; overflow: hidden; } .landing-top .text-rotater span { display: block; transition: transform 0.4s; } .landing-top .text-rotater.slide-up span { transform: translateY(-51px); } .landing-top .install { background: rgba(120,40,40,0.32); border-radius: 7px; max-width: 357px; padding: 12px; } @media all and (max-width: $tablet-width) { .landing-top .install { margin: 0 auto; text-align: left; } } .landing-top .install .button { background: rgba(255,255,255,0.39); width: 12px; height: 12px; border-radius: 50%; display: inline-block; } .landing-top .install .command { font-family: $code-font; padding: 12px; padding-left: 30px; } .landing-top .install .command::before { content: "$"; opacity: 0.5; transform: rotate(8deg); font-size: 20px; position: absolute; margin-left: -27px; margin-top: -2px; } .comparison-container { padding-bottom: 70px; text-align: center; } .comparison-container h3 { font-size: 22px; color: #b49993; font-weight: 400; display: block; text-align: center; margin-top: 93px; margin-bottom: 80px; } .comparison-container .code-container { text-align: center; width: calc(50% - 60px); display: inline-block; vertical-align: middle; } @media all and (max-width: $phablet-width) { .comparison-container .code-container { width: 100%; } } .comparison-container .versus { width: 35px; height: 33px; background-image: url("/assets/images/vs.svg"); display: inline-block; vertical-align: middle; margin: 0 30px; } @media all and (max-width: $phablet-width) { .comparison-container .versus { margin: 30px; margin-bottom: -10px; } } .comparison-container h5 { font-size: 13px; color: rgba(0,0,0,0.21); text-transform: uppercase; text-align: left; margin-bottom: 15px; } .comparison-container h5.swal-logo { text-indent: -9999999px; margin-top: 2px; } .comparison-container h5.swal-logo::after { content: ""; background-image: url("/assets/images/logo-small.svg"); width: 91px; height: 20px; display: block; } .comparison-container .highlight { text-align: left; padding: 16px 23px; } .comparison-container .highlight span { margin: 0 -4px; } .comparison-container .remark { font-size: 20px; color: $main-color; margin-top: 80px; } .comparison-container .get-started-button { background-color: $main-color; color: #fff; border-radius: 8px; font-size: 22px; padding: 14px 55px; margin: 20px 0; display: inline-block; } .customize-container { background-color: #999eaf; text-align: center; color: #fff; text-align: center; background-image: url("/assets/images/pattern.png"); background-image: -webkit-image-set(url("/assets/images/pattern.png") 1x, url("/assets/images/pattern@2x.png") 2x); padding: 40px 0; -webkit-clip-path: url("#customization-transition-clip-shape"); clip-path: url("#customization-transition-clip-shape"); will-change: transform; /* For Safari */ } @media all and (max-width: $phablet-width) { .customize-container { -webkit-clip-path: none; clip-path: none; } } .customize-container h3 { font-weight: 400; font-size: 20px; padding: 50px 0; } .customize-container .example-modals { background-image: url("/assets/images/modal-examples.png"); background-image: -webkit-image-set(url("/assets/images/modal-examples.png") 1x, url("/assets/images/modal-examples@2x.png") 2x); height: 284px; background-size: auto 100%; background-position: 0 0; animation: scrollExamples 80s infinite linear; } .customize-container .view-api-button { border: 3px solid #fff; border-radius: 6px; color: #fff; padding: 12px 52px; font-size: 18px; margin-top: 60px; display: inline-block; } @-moz-keyframes scrollExamples { 0% { background-position: 0 0; } 100% { background-position: -2146px 0; } } @-webkit-keyframes scrollExamples { 0% { background-position: 0 0; } 100% { background-position: -2146px 0; } } @-o-keyframes scrollExamples { 0% { background-position: 0 0; } 100% { background-position: -2146px 0; } } @keyframes scrollExamples { 0% { background-position: 0 0; } 100% { background-position: -2146px 0; } } ================================================ FILE: docs/assets/css/normalize.css ================================================ body { margin: 0; padding: 0; } a { text-decoration: none; color: $main-color; } p a:hover { text-decoration: underline; } button { cursor: pointer; } button:focus { outline: none; } ul { list-style-type: none; margin: 0; } ================================================ FILE: docs/assets/css/table.css ================================================ .page-content { $mobile-breakpoint: 880px; } .page-content table { border-collapse: collapse; border: none; width: 100%; } .page-content th { font-size: 17px; color: rgba(0,0,0,0.34); padding: 20px 15px; text-transform: capitalize; font-weight: 400; } .page-content thead > tr { border-bottom: 2px solid rgba(0,0,0,0.1); } .page-content tr { text-align: left; box-shadow: 0px -1px 0px rgba(0,0,0,0.15); } .page-content tr:first-child { box-shadow: none; } .page-content td { padding: 17px; } .page-content td:first-child > code { color: #2e9fef; background: none; border: none; font-size: 16px; padding: 0; } @media all and (min-width: mobile-breakpoint) { .page-content tbody tr:nth-child(1) { box-shadow: none; } } @media all and (max-width: mobile-breakpoint) { .page-content table, .page-content thead, .page-content tbody, .page-content th, .page-content td, .page-content tr { display: block; } .page-content thead tr { position: absolute; top: -9999px; left: -9999px; } .page-content tr { margin-top: -1px; box-shadow: 0px -1px 0px 0px rgba(0,0,0,0.27); } .page-content td { /* Behave like a "row" */ border: none; border-bottom: 1px solid #eee; position: relative; padding-left: mobile-padding; min-height: 20px; } .page-content td::before { /* Now like a table header */ position: absolute; /* Top/left values mimic padding */ top: 15px; left: 15px; width: mobile-padding; width: calc((mobile-padding - 35px)); overflow: hidden; text-overflow: ellipsis; padding-right: 10px; white-space: nowrap; color: rgba(0,0,0,0.54); font-family: page-font; font-size: 16px; text-transform: capitalize; content: attr(data-name); } } ================================================ FILE: docs/assets/css/variables.css ================================================ ================================================ FILE: docs/assets/js/add-preview-buttons.js ================================================ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o tags. * We want to transform these into button.preview, * which onclick will run the JS code right above them! */ var previewPlaceholders = document.querySelectorAll('preview-button'); var createButton = function createButton(placeholder) { var button = document.createElement('button'); button.className = "preview"; button.innerText = "Preview"; // Add button right above placeholder placeholder.parentNode.insertBefore(button, placeholder); return button; }; var getCodeEl = function getCodeEl(placeholder) { return placeholder.parentNode.previousSibling.previousSibling; }; var getCode = function getCode(highlightEl) { return highlightEl.innerText.trim(); }; var resetStyles = function resetStyles() { var swalOverlay = document.querySelector('.swal-overlay'); var allSwalEls = swalOverlay.querySelectorAll('*'); swalOverlay.removeAttribute('style'); allSwalEls.forEach(function (el) { el.removeAttribute('style'); }); }; var setStyles = function setStyles(code) { var array = code.split(/[{}]/g); var selector = array[0].trim(); var el = document.querySelector(selector); var css = array[1].trim(); css = css.replace(/\s+/g, ' '); css = css.replace(/;\s?/g, '; '); css = css.replace(/:\s?/g, ': '); el.style.cssText = css; }; previewPlaceholders.forEach(function (placeholder) { var highlightEl = getCodeEl(placeholder); var code = getCode(highlightEl); var button = createButton(placeholder); var givenFunction = placeholder.dataset.function; var lang = highlightEl.classList[1]; /* * If there's a specified data-function on , call that. * Othwerwise, just use the code from the highlightjs above it: */ button.addEventListener('click', function () { if (givenFunction) { window[givenFunction](); } else if (lang === "css") { swal("Sweet!", "I like customizing!"); resetStyles(); setStyles(code); } else { var transpiledCode = Babel.transform(code, { presets: ['es2015'] }).code; eval(transpiledCode); } }); placeholder.remove(); }); },{"babel-standalone":2}],2:[function(require,module,exports){ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["Babel"] = factory(); else root["Babel"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ((function(modules) { // Check all modules for deduplicated modules for(var i in modules) { if(Object.prototype.hasOwnProperty.call(modules, i)) { switch(typeof modules[i]) { case "function": break; case "object": // Module can be created from a template modules[i] = (function(_m) { var args = _m.slice(1), fn = modules[_m[0]]; return function (a,b,c) { fn.apply(this, [a,b,c].concat(args)); }; }(modules[i])); break; default: // Module is a copy of another module modules[i] = modules[modules[i]]; break; } } } return modules; }([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.version = exports.buildExternalHelpers = exports.availablePresets = exports.availablePlugins = undefined; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; exports.transform = transform; exports.transformFromAst = transformFromAst; exports.registerPlugin = registerPlugin; exports.registerPlugins = registerPlugins; exports.registerPreset = registerPreset; exports.registerPresets = registerPresets; exports.transformScriptTags = transformScriptTags; exports.disableScriptTags = disableScriptTags; var _babelCore = __webpack_require__(290); var Babel = _interopRequireWildcard(_babelCore); var _transformScriptTags = __webpack_require__(629); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } var isArray = Array.isArray || function (arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; /** * Loads the given name (or [name, options] pair) from the given table object * holding the available presets or plugins. * * Returns undefined if the preset or plugin is not available; passes through * name unmodified if it (or the first element of the pair) is not a string. */ function loadBuiltin(builtinTable, name) { if (isArray(name) && typeof name[0] === 'string') { if (builtinTable.hasOwnProperty(name[0])) { return [builtinTable[name[0]]].concat(name.slice(1)); } return; } else if (typeof name === 'string') { return builtinTable[name]; } // Could be an actual preset/plugin module return name; } /** * Parses plugin names and presets from the specified options. */ function processOptions(options) { // Parse preset names var presets = (options.presets || []).map(function (presetName) { var preset = loadBuiltin(availablePresets, presetName); if (preset) { // workaround for babel issue // at some point, babel copies the preset, losing the non-enumerable // buildPreset key; convert it into an enumerable key. if (isArray(preset) && _typeof(preset[0]) === 'object' && preset[0].hasOwnProperty('buildPreset')) { preset[0] = _extends({}, preset[0], { buildPreset: preset[0].buildPreset }); } } else { throw new Error('Invalid preset specified in Babel options: "' + presetName + '"'); } return preset; }); // Parse plugin names var plugins = (options.plugins || []).map(function (pluginName) { var plugin = loadBuiltin(availablePlugins, pluginName); if (!plugin) { throw new Error('Invalid plugin specified in Babel options: "' + pluginName + '"'); } return plugin; }); return _extends({ babelrc: false }, options, { presets: presets, plugins: plugins }); } function transform(code, options) { return Babel.transform(code, processOptions(options)); } function transformFromAst(ast, code, options) { return Babel.transformFromAst(ast, code, processOptions(options)); } var availablePlugins = exports.availablePlugins = {}; var availablePresets = exports.availablePresets = {}; var buildExternalHelpers = exports.buildExternalHelpers = Babel.buildExternalHelpers; /** * Registers a named plugin for use with Babel. */ function registerPlugin(name, plugin) { if (availablePlugins.hasOwnProperty(name)) { console.warn('A plugin named "' + name + '" is already registered, it will be overridden'); } availablePlugins[name] = plugin; } /** * Registers multiple plugins for use with Babel. `newPlugins` should be an object where the key * is the name of the plugin, and the value is the plugin itself. */ function registerPlugins(newPlugins) { Object.keys(newPlugins).forEach(function (name) { return registerPlugin(name, newPlugins[name]); }); } /** * Registers a named preset for use with Babel. */ function registerPreset(name, preset) { if (availablePresets.hasOwnProperty(name)) { console.warn('A preset named "' + name + '" is already registered, it will be overridden'); } availablePresets[name] = preset; } /** * Registers multiple presets for use with Babel. `newPresets` should be an object where the key * is the name of the preset, and the value is the preset itself. */ function registerPresets(newPresets) { Object.keys(newPresets).forEach(function (name) { return registerPreset(name, newPresets[name]); }); } // All the plugins we should bundle registerPlugins({ 'check-es2015-constants': __webpack_require__(66), 'external-helpers': __webpack_require__(322), 'inline-replace-variables': __webpack_require__(323), 'syntax-async-functions': __webpack_require__(67), 'syntax-async-generators': __webpack_require__(195), 'syntax-class-constructor-call': __webpack_require__(196), 'syntax-class-properties': __webpack_require__(197), 'syntax-decorators': __webpack_require__(125), 'syntax-do-expressions': __webpack_require__(198), 'syntax-exponentiation-operator': __webpack_require__(199), 'syntax-export-extensions': __webpack_require__(200), 'syntax-flow': __webpack_require__(126), 'syntax-function-bind': __webpack_require__(201), 'syntax-function-sent': __webpack_require__(325), 'syntax-jsx': __webpack_require__(127), 'syntax-object-rest-spread': __webpack_require__(202), 'syntax-trailing-function-commas': __webpack_require__(128), 'transform-async-functions': __webpack_require__(326), 'transform-async-to-generator': __webpack_require__(129), 'transform-async-to-module-method': __webpack_require__(328), 'transform-class-constructor-call': __webpack_require__(203), 'transform-class-properties': __webpack_require__(204), 'transform-decorators': __webpack_require__(205), 'transform-decorators-legacy': __webpack_require__(329).default, // <- No clue. Nope. 'transform-do-expressions': __webpack_require__(206), 'transform-es2015-arrow-functions': __webpack_require__(68), 'transform-es2015-block-scoped-functions': __webpack_require__(69), 'transform-es2015-block-scoping': __webpack_require__(70), 'transform-es2015-classes': __webpack_require__(71), 'transform-es2015-computed-properties': __webpack_require__(72), 'transform-es2015-destructuring': __webpack_require__(73), 'transform-es2015-duplicate-keys': __webpack_require__(130), 'transform-es2015-for-of': __webpack_require__(74), 'transform-es2015-function-name': __webpack_require__(75), 'transform-es2015-instanceof': __webpack_require__(332), 'transform-es2015-literals': __webpack_require__(76), 'transform-es2015-modules-amd': __webpack_require__(131), 'transform-es2015-modules-commonjs': __webpack_require__(77), 'transform-es2015-modules-systemjs': __webpack_require__(208), 'transform-es2015-modules-umd': __webpack_require__(209), 'transform-es2015-object-super': __webpack_require__(78), 'transform-es2015-parameters': __webpack_require__(79), 'transform-es2015-shorthand-properties': __webpack_require__(80), 'transform-es2015-spread': __webpack_require__(81), 'transform-es2015-sticky-regex': __webpack_require__(82), 'transform-es2015-template-literals': __webpack_require__(83), 'transform-es2015-typeof-symbol': __webpack_require__(84), 'transform-es2015-unicode-regex': __webpack_require__(85), 'transform-es3-member-expression-literals': __webpack_require__(336), 'transform-es3-property-literals': __webpack_require__(337), 'transform-es5-property-mutators': __webpack_require__(338), 'transform-eval': __webpack_require__(339), 'transform-exponentiation-operator': __webpack_require__(132), 'transform-export-extensions': __webpack_require__(210), 'transform-flow-comments': __webpack_require__(340), 'transform-flow-strip-types': __webpack_require__(211), 'transform-function-bind': __webpack_require__(212), 'transform-jscript': __webpack_require__(341), 'transform-object-assign': __webpack_require__(342), 'transform-object-rest-spread': __webpack_require__(213), 'transform-object-set-prototype-of-to-assign': __webpack_require__(343), 'transform-proto-to-assign': __webpack_require__(344), 'transform-react-constant-elements': __webpack_require__(345), 'transform-react-display-name': __webpack_require__(214), 'transform-react-inline-elements': __webpack_require__(346), 'transform-react-jsx': __webpack_require__(215), 'transform-react-jsx-compat': __webpack_require__(347), 'transform-react-jsx-self': __webpack_require__(349), 'transform-react-jsx-source': __webpack_require__(350), 'transform-regenerator': __webpack_require__(86), 'transform-runtime': __webpack_require__(353), 'transform-strict-mode': __webpack_require__(216), 'undeclared-variables-check': __webpack_require__(354) }); // All the presets we should bundle registerPresets({ es2015: __webpack_require__(217), es2016: __webpack_require__(218), es2017: __webpack_require__(219), latest: __webpack_require__(356), react: __webpack_require__(357), 'stage-0': __webpack_require__(358), 'stage-1': __webpack_require__(220), 'stage-2': __webpack_require__(221), 'stage-3': __webpack_require__(222), // ES2015 preset with es2015-modules-commonjs removed // Plugin list copied from babel-preset-es2015/index.js 'es2015-no-commonjs': { plugins: [__webpack_require__(83), __webpack_require__(76), __webpack_require__(75), __webpack_require__(68), __webpack_require__(69), __webpack_require__(71), __webpack_require__(78), __webpack_require__(80), __webpack_require__(72), __webpack_require__(74), __webpack_require__(82), __webpack_require__(85), __webpack_require__(66), __webpack_require__(81), __webpack_require__(79), __webpack_require__(73), __webpack_require__(70), __webpack_require__(84), [__webpack_require__(86), { async: false, asyncGenerators: false }]] }, // ES2015 preset with plugins set to loose mode. // Based off https://github.com/bkonkle/babel-preset-es2015-loose/blob/master/index.js 'es2015-loose': { plugins: [[__webpack_require__(83), { loose: true }], __webpack_require__(76), __webpack_require__(75), __webpack_require__(68), __webpack_require__(69), [__webpack_require__(71), { loose: true }], __webpack_require__(78), __webpack_require__(80), __webpack_require__(130), [__webpack_require__(72), { loose: true }], [__webpack_require__(74), { loose: true }], __webpack_require__(82), __webpack_require__(85), __webpack_require__(66), [__webpack_require__(81), { loose: true }], __webpack_require__(79), [__webpack_require__(73), { loose: true }], __webpack_require__(70), __webpack_require__(84), [__webpack_require__(77), { loose: true }], [__webpack_require__(86), { async: false, asyncGenerators: false }]] } }); var version = exports.version = ("6.26.0"); // Listen for load event if we're in a browser and then kick off finding and // running of scripts with "text/babel" type. if (typeof window !== 'undefined' && window && window.addEventListener) { window.addEventListener('DOMContentLoaded', function () { return transformScriptTags(); }, false); } /** * Transform

Configuration

  • Type: string

    Default: "" (empty string)

    Description:

    The modal's text. It can either be added as a configuration under the key text (as in the example below), or passed as the first and only parameter (e.g. swal("Hello world!")), or the second one if you have multiple string parameters (e.g. swal("A title", "Hello world!")).

    Example:

    swal({
      text: "Hello world!",
    });

  • Type: string

    Default: "" (empty string)

    Description:

    The title of the modal. It can either be added as a configuration under the key title (as in the example below), or passed as the first string parameter – as long as it's not the only one – of the swal function (e.g. swal("Here's a title!", "Here's some text")).

    Example:

    swal({
      title: "Here's a title!",
    });

  • Type: string

    Default: "" (empty string)

    Description:

    An icon for the modal. SweetAlert comes with 4 built-in icons that you can use:

    • "warning"
    • "error"
    • "success"
    • "info"

    It can either be added as a configuration under the key icon, or passed as the third string parameter of the swal function (e.g. swal("Title", "Text", "success")).

    Example:

    swal({
      icon: "success",
    });

  • Type: string|boolean|ButtonOptions

    Default:

    {
      text: "OK",
      value: true,
      visible: true,
      className: "",
      closeModal: true,
    }

    Description:

    The confirm button that's shown by default. You can change its text by setting button to a string, or you can tweak more setting by passing a ButtonOptions object. Setting it to false hides the button.

    Examples:

    swal({
      button: "Coolio",
    });

    swal({
      button: {
        text: "Hey ho!",
      },
    });

    swal("Hello world!", {
      button: false,
    });

  • Type: boolean|string[]|ButtonOptions[]|ButtonList

    Default:

    {
      cancel: {
        text: "Cancel",
        value: null,
        visible: false,
        className: "",
        closeModal: true,
      },
      confirm: {
        text: "OK",
        value: true,
        visible: true,
        className: "",
        closeModal: true
      }
    }

    Description:

    Specify the exact amount of buttons and their behaviour. If you use an array, you can set the elements as strings (to set only the text), a list of ButtonOptions, or a combination of both. You can also set one of the elements to true to simply get the default options.

    If you want more than just the predefined cancel and confirm buttons, you need to specify a ButtonList object, with keys (the button's namespace) pointing to ButtonOptions.

    You can also specify false to hide all buttons (same behaviour as the button option).

    Examples:

    swal({
      buttons: ["Stop", "Do it!"],
    });

    swal({
      buttons: [true, "Do it!"],
    });

    swal("Hello world!", {
      buttons: false,
    });

    swal({
      buttons: {
        cancel: true,
        confirm: true,
      },
    });

    swal({
      buttons: {
        cancel: true,
        confirm: "Confirm",
        roll: {
          text: "Do a barrell roll!",
          value: "roll",
        },
      },
    });

  • Type: Node|string

    Default: null

    Description:

    For custom content, beyond just text and icons.

    Examples:

    swal({
      content: "input",
    });

    swal({
      content: {
        element: "input",
        attributes: {
          placeholder: "Type your password",
          type: "password",
        },
      },
    });

    var slider = document.createElement("input");
    slider.type = "range";
     
    swal({
      content: slider,
    });

  • Type: string

    Default: "" (empty string)

    Description:

    Add a custom class to the SweetAlert modal. This is handy for changing the appearance.

    Example:

      swal("Hello world!", {
        className: "red-bg",
      });

  • Type: boolean

    Default: true

    Description:

    Decide whether the user should be able to dismiss the modal by clicking outside of it, or not.

    Example:

    swal({
      closeOnClickOutside: false,
    });

  • Type: boolean

    Default: true

    Description:

    Decide whether the user should be able to dismiss the modal by hitting the ESC key, or not.

    Example:

    swal({
      closeOnEsc: false,
    });

  • Type: boolean

    Default: false

    Description:

    If set to true, the confirm button turns red and the default focus is set on the cancel button instead. This is handy when showing warning modals where the confirm action is dangerous (e.g. deleting an item).

    Example:

    swal("Are you sure?", {
      dangerMode: true,
      buttons: true,
    });

  • Type: number

    Default: null

    Description:

    Closes the modal after a certain amount of time (specified in ms). Useful to combine with buttons: false.

    Example:

    swal("This modal will disappear soon!", {
      buttons: false,
      timer: 3000,
    });

Methods

Name Description Example
close Closes the currently open SweetAlert, as if you pressed the cancel button. swal.close()
getState Get the state of the current SweetAlert modal. swal.getState()
setActionValue Change the promised value of one of the modal's buttons. You can either pass in just a string (by default it changes the value of the confirm button), or an object. swal.setActionValue({ confirm: 'Text from input' })
stopLoading Removes all loading states on the modal's buttons. Use it in combination with the button option closeModal: false. swal.stopLoading()

Theming

  • Example:

    .swal-overlay {
      background-color: rgba(43, 165, 137, 0.45);
    }

  • Example:

    .swal-modal {
      background-color: rgba(63,255,106,0.69);
      border: 3px solid white;
    }

  • Example:

    .swal-title {
      margin: 0px;
      font-size: 16px;
      box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.21);
      margin-bottom: 28px;
    }

  • Example:

    .swal-text {
      background-color: #FEFAE3;
      padding: 17px;
      border: 1px solid #F0E1A1;
      display: block;
      margin: 22px;
      text-align: center;
      color: #61534e;
    }

  • Example:

    .swal-footer {
      background-color: rgb(245, 248, 250);
      margin-top: 32px;
      border-top: 1px solid #E9EEF1;
      overflow: hidden;
    }

  • Description:

    The modal's button(s). It has an extra class that changes depending on the button's type, in the format swal-button--{type}. The extra class for the confirm button for example is swal-button--confirm.

    Example:

    .swal-button {
      padding: 7px 19px;
      border-radius: 2px;
      background-color: #4962B3;
      font-size: 12px;
      border: 1px solid #3e549a;
      text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.3);
    }

================================================ FILE: docs/guides/index.html ================================================ SweetAlert

Installation

NPM combined with a tool like Browserify or Webpack is the recommended method of installing SweetAlert.

npm install sweetalert --save

Then, simply import it into your application:

import swal from 'sweetalert';

You can also find SweetAlert on unpkg and jsDelivr and use the global swal variable.

<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>

Getting started

After importing the files into your application, you can call the swal function (make sure it's called after the DOM has loaded!)

swal("Hello world!");

If you pass two arguments, the first one will be the modal's title, and the second one its text.

swal("Here's the title!", "...and here's the text!");

And with a third argument, you can add an icon to your alert! There are 4 predefined ones: "warning", "error", "success" and "info".

swal("Good job!", "You clicked the button!", "success");

The last example can also be rewritten using an object as the only parameter:

swal({
  title: "Good job!",
  text: "You clicked the button!",
  icon: "success",
});

With this format, we can specify many more options to customize our alert. For example we can change the text on the confirm button to "Aww yiss!":

swal({
  title: "Good job!",
  text: "You clicked the button!",
  icon: "success",
  button: "Aww yiss!",
});

You can even combine the first syntax with the second one, which might save you some typing:

swal("Good job!", "You clicked the button!", "success", {
  button: "Aww yiss!",
});

For a full list of all the available options, check out the API docs!

SweetAlert uses promises to keep track of how the user interacts with the alert.

If the user clicks the confirm button, the promise resolves to true. If the alert is dismissed (by clicking outside of it), the promise resolves to null.

swal("Click on either the button or outside the modal.")
.then((value) => {
  swal(`The returned value is: ${value}`);
});

This comes in handy if you want to warn the user before they perform a dangerous action. We can make our alert even better by setting some more options:

  • icon can be set to the predefined "warning" to show a nice warning icon.
  • By setting buttons (plural) to true, SweetAlert will show a cancel button in addition to the default confirm button.
  • By setting dangerMode to true, the focus will automatically be set on the cancel button instead of the confirm button, and the confirm button will be red instead of blue to emphasize the dangerous action.
swal({
  title: "Are you sure?",
  text: "Once deleted, you will not be able to recover this imaginary file!",
  icon: "warning",
  buttons: true,
  dangerMode: true,
})
.then((willDelete) => {
  if (willDelete) {
    swal("Poof! Your imaginary file has been deleted!", {
      icon: "success",
    });
  } else {
    swal("Your imaginary file is safe!");
  }
});

Advanced examples

We've already seen how we can set the text on the confirm button using button: "Aww yiss!".

If we also want to show and customize the cancel button, we can instead set buttons to an array of strings, where the first value is the cancel button's text and the second one is the confirm button's text:

swal("Are you sure you want to do this?", {
  buttons: ["Oh noez!", "Aww yiss!"],
});

If you want one of the buttons to just have their default text, you can set the value to true instead of a string:

swal("Are you sure you want to do this?", {
  buttons: ["Oh noez!", true],
});

So what if you need more than just a cancel and a confirm button? Don't worry, SweetAlert's got you covered!

By specifying an object for buttons, you can set exactly as many buttons as you like, and specify the value that they resolve to when they're clicked!

In the example below, we set 3 buttons:

  • cancel, which by default resolves to null and has a custom "Run away!" text.
  • catch, which will resolve to the value we've specified ("catch") and has the custom text "Throw Pokéball!".
  • defeat. Here, we specify true to let SweetAlert set some default configurations for the button. In this case, it will set the text to "Defeat" (capitalized) and the resolved value to defeat. Had we set the cancel button to true, it would still resolve to null as expected.
swal("A wild Pikachu appeared! What do you want to do?", {
  buttons: {
    cancel: "Run away!",
    catch: {
      text: "Throw Pokéball!",
      value: "catch",
    },
    defeat: true,
  },
})
.then((value) => {
  switch (value) {
 
    case "defeat":
      swal("Pikachu fainted! You gained 500 XP!");
      break;
 
    case "catch":
      swal("Gotcha!", "Pikachu was caught!", "success");
      break;
 
    default:
      swal("Got away safely!");
  }
});

You can check out all the available button options in the docs.

Since SweetAlert is promise-based, it makes sense to pair it with AJAX functions that are also promise-based. Below is an example of using fetch to search for artists on the iTunes API. Note that we're using content: "input" in order to both show an input-field and retrieve its value when the user clicks the confirm button:

swal({
  text: 'Search for a movie. e.g. "La La Land".',
  content: "input",
  button: {
    text: "Search!",
    closeModal: false,
  },
})
.then(name => {
  if (!name) throw null;
 
  return fetch(`https://itunes.apple.com/search?term=${name}&entity=movie`);
})
.then(results => {
  return results.json();
})
.then(json => {
  const movie = json.results[0];
 
  if (!movie) {
    return swal("No movie was found!");
  }
 
  const name = movie.trackName;
  const imageURL = movie.artworkUrl100;
 
  swal({
    title: "Top result:",
    text: name,
    icon: imageURL,
  });
})
.catch(err => {
  if (err) {
    swal("Oh noes!", "The AJAX request failed!", "error");
  } else {
    swal.stopLoading();
    swal.close();
  }
});

Sometimes, you might run into a scenario where it would be nice to use the out-of-the box functionality that SweetAlert offers, but with some custom UI that goes beyond just styling buttons and text. For that, there's the content option.

In the previous example, we saw how we could set content to "input" to get an <input /> element in our modal that changes the resolved value of the confirm button based on its value. "input" is a predefined option that exists for convenience, but you can also set content to any DOM node!

Let's see how we can recreate the functionality of the following modal...

swal("Write something here:", {
  content: "input",
})
.then((value) => {
  swal(`You typed: ${value}`);
});

...using a custom DOM node!

We're going to use React here, since it's a well-known UI library that can help us understand how to create more complex SweetAlert interfaces, but you can use any library you want, as long as you can extract a DOM node from it!

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
 
const DEFAULT_INPUT_TEXT = "";
 
class MyInput extends Component {
  constructor(props) {
    super(props);
 
    this.state = {
      text: DEFAULT_INPUT_TEXT,
    };
  }
 
  changeText(e) {
    let text = e.target.value;
 
    this.setState({
      text,
    });
 
    /*
     * This will update the value that the confirm
     * button resolves to:
     */
    swal.setActionValue(text);
  }
 
  render() {
    return (
      <input
        value={this.state.text}
        onChange={this.changeText.bind(this)}
      />
    )
  }
}
 
// We want to retrieve MyInput as a pure DOM node: 
let wrapper = document.createElement('div');
ReactDOM.render(<MyInput />, wrapper);
let el = wrapper.firstChild;
 
swal({
  text: "Write something here:",
  content: el,
  buttons: {
    confirm: {
      /*
       * We need to initialize the value of the button to
       * an empty string instead of "true":
       */
      value: DEFAULT_INPUT_TEXT,
    },
  },
})
.then((value) => {
  swal(`You typed: ${value}`);
});

This might look very complex at first, but it's actually pretty simple. All we're doing is creating an input tag as a React component. We then extract its DOM node and pass it into under the swal function's content option to render it as an unstyled element.

The only code that's specific to SweetAlert is the swal.setActionValue() and the swal() call at the end. The rest is just basic React and JavaScript.

Facebook modal
Using this technique, we can create modals with more interactive UIs, such as this one from Facebook.

Using with libraries

While the method documented above for creating more advanced modal designs works, it gets quite tedious to manually create nested DOM nodes. That's why we've also made it easy to integrate your favourite template library into SweetAlert, using the SweetAlert Transformer.

In order to use SweetAlert with JSX syntax, you need to install SweetAlert with React. Note that you need to have both sweetalert and @sweetalert/with-react as dependencies in your package.json.

After that, it's easy. Whenever you want to use JSX in your SweetAlert modal, simply import swal from @sweetalert/with-react instead of from sweetalert.

import React from 'react'
import swal from '@sweetalert/with-react'
 
swal(
  <div>
    <h1>Hello world!</h1>
    <p>
      This is now rendered with JSX!
    </p>
  </div>
)

The JSX syntax replaces the modal's content option, so you can still use all of SweetAlert's other features. Here's how you could implement that Facebook modal that we saw earlier:

import React from 'react'
import swal from '@sweetalert/with-react'
 
const onPick = value => {
  swal("Thanks for your rating!", `You rated us ${value}/3`, "success")
}
 
const MoodButton = ({ rating, onClick }) => (
  <button 
    data-rating={rating}
    className="mood-btn" 
    onClick={() => onClick(rating)}
  />
)
 
swal({
  text: "How was your experience getting help with this issue?",
  buttons: {
    cancel: "Close",
  },
  content: (
    <div>
      <MoodButton 
        rating={1} 
        onClick={onPick}
      />
      <MoodButton 
        rating={2} 
        onClick={onPick}
      />
      <MoodButton 
        rating={3} 
        onClick={onPick}
      />
    </div>
  )
})

Upgrading from 1.X

SweetAlert 2.0 introduces some important breaking changes in order to make the library easier to use and more flexible.

The most important change is that callback functions have been deprecated in favour of promises, and that you no longer have to import any external CSS file (since the styles are now bundled in the .js-file).

Below are some additional deprecated options along with their replacements:

  • When using a single string parameter (e.g. swal("Hello world!")), that parameter will be the modal's text instead of its title.
  • type and imageUrl have been replaced with a single icon option. If you're using the shorthand version (swal("Hi", "Hello world", "warning")) you don't have to change anything.
  • customClass is now className.
  • imageSize is no longer used. Instead, you should specify dimension restrictions in CSS if necessary. If you have a special use case, you can give your modal a custom class.
  • showCancelButton and showConfirmButton are no longer needed. Instead, you can set buttons: true to show both buttons, or buttons: false to hide all buttons. By default, only the confirm button is shown.
  • confirmButtonText and cancelButtonText are no longer needed. Instead, you can set button: "foo" to set the text on the confirm button to "foo", or buttons: ["foo", "bar"] to set the text on the cancel button to "foo" and the text on the confirm button to "bar".
  • confirmButtonColor is no longer used. Instead, you should specify all stylistic changes through CSS. As a useful shorthand, you can set dangerMode: true to make the confirm button red. Otherwise, you can specify a class in the button object.
  • closeOnConfirm and closeOnCancel are no longer used. Instead, you can set the closeModal parameter in the button options.
  • showLoaderOnConfirm is no longer necessary. Your button will automatically show a loding animation when its closeModal parameter is set to false.
  • animation has been deprecated. All stylistic changes can instead be applied through CSS and a custom modal class.
  • type: "input", inputType, inputValue and inputPlaceholder have all been replaced with the content option. You can either specify content: "input" to get the default options, or you can customize it further using the content object.
  • html is no longer used. Instead use the content object.
  • allowEscapeKey is now closeOnEsc for clarity.
  • allowClickOutside is now closeOnClickOutside for clarity.
================================================ FILE: docs/index.html ================================================ SweetAlert

A beautiful replacement for success messages

npm install sweetalert

You've arrived!

How lovely. Let me take your coat.

Oops!

Seems like something went wrong!

Delete important stuff?

That doesn't seem like a good idea. Are you sure you want to do that?

SweetAlert makes popup messages easy and pretty.

Normal alert
alert ( "Oops, something went wrong!" )
swal ( "Oops" ,  "Something went wrong!" ,  "error" )

Pretty cool, huh?

Get started!

You can customize SweetAlert to fit your needs

View docs
================================================ FILE: docs-src/assets/css/app.styl ================================================ @import 'variables'; @import 'normalize'; @import 'header'; @import 'highlight'; @import 'index'; @import 'guide'; body { font-family: 'Lato', 'Helvetica Neue', Helvetica, sans-serif; } svg.hidden { display: block; } .page-container { max-width: 1000px; margin: 0 auto; padding: 0 10px; position: relative; } $preview-color = #A3DD82; .preview { background-color: $preview-color; box-shadow: 0 2px 8px 0 rgba(0,0,0,0.07); border-radius: 4px; border: none; color: white; font-size: 15px; color: white; padding: 9px 18px; margin-top: 20px; &::before { content: ""; width: 0; height: 0; border-top: 6px solid transparent; border-bottom: 6px solid transparent; border-left: 10px solid white; display: inline-block; margin-right: 5px; } &:hover { background-color: lightness($preview-color, 65%) } } footer { padding: 40px 20px; text-align: center; color: #728194; .love-icon { background-image: url("/assets/images/heart-icon.svg"); width: 22px; height: 20px; display: inline-block; vertical-align: middle; margin: 0 5px; position: relative; top: -2px; } } ================================================ FILE: docs-src/assets/css/guide.styl ================================================ @import 'table'; .doc-container { overflow: hidden; } $side-menu-width = 225px; .side-menu { width: $side-menu-width; float: left; padding-left: 20px; position: fixed; top: 88px; @media all and (max-width: $phablet-width) { float: none; position: static; margin-top: 120px; text-align: center; width: 100%; margin-bottom: -60px; padding-left: 0; } .title { font-size: 20px; color: rgba(0,0,0,0.63); font-weight: 600; margin-top: 50px; margin-bottom: 36px; } a { font-size: 17px; color: rgba(0, 0, 0, 0.42); display: block; margin: 18px 0; &:hover { color: rgba(0, 0, 0, 0.6); } } } .page-content { float: left; width: s('calc(100% - %s - 20px)', $side-menu-width); margin-left: $side-menu-width; min-height: 220px; margin-top: 16px; font-size: 16px; color: rgba(0,0,0,0.59); line-height: 29px; @media all and (max-width: $phablet-width) { float: none; margin-left: 0; width: 100%; } h1 { padding-top: 90px; border-bottom: 1px solid rgba(0, 0, 0, 0.15); padding-bottom: 20px; margin-bottom: 0; &::before { content: "#"; position: absolute; margin-left: -23px; font-size: 24px; margin-top: 3px; color: #f38270; font-weight: 500; } a { font-size: 30px; color: rgba(0,0,0,0.85); font-weight: 600; } } h2, h3 { font-size: 20px; margin-top: -70px; padding-top: 100px; a { color: rgba(0, 0, 0, 0.7); } } ul { list-style-type: disc; margin-bottom: 20px; li { margin: 5px 0; } } img { max-width: 100%; } &.api > ul { list-style-type: none; padding-left: 30px; > li h3::before { content: ""; width: 8px; height: 8px; position: absolute; border-radius: 50%; background-color: $main-color; margin-left: -27px; margin-top: 12px; } } code { font-family: $code-font; padding: 3px 6px; border-radius: 2px; border: 1px solid rgba(0,0,0,.12); background: rgb(248, 248, 248); font-size: 14px; color: $main-color; } kbd { display: inline-block; padding: 3px 5px; font-size: 11px; line-height: 10px; color: #444d56; vertical-align: middle; background-color: #fafbfc; border: solid 1px #c6cbd1; border-bottom-color: #959da5; border-radius: 3px; box-shadow: inset 0 -1px 0 #959da5; font-family: sans-serif; } // Special web component: preview-button { /* Matches the "real" button's height: */ display: block; height: 44px; } button.preview { margin: 20px auto; width: 110px; display: block; position: relative; z-index: 2; } figcaption { font-style: italic; } } .swal-modal.red-bg { background-color: rgba(255, 0, 0, 0.28); } .mood-btn { background: none; border: none; width: 28px; height: 28px; background-image: url(/assets/images/mood-sad.png); background-size: 28px 28px; padding: 4px; background-position: center center; box-sizing: content-box; background-repeat: no-repeat; margin: 0 7px; position: relative; overflow: hidden; border-radius: 3px; } .mood-btn:hover::after { content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.03); } .mood-btn[data-rating="2"] { background-image: url(/assets/images/mood-neutral.png); } .mood-btn[data-rating="3"] { background-image: url(/assets/images/mood-happy.png); } ================================================ FILE: docs-src/assets/css/header.styl ================================================ .global-header { background-color: white; box-shadow: 0 1px 15px 0 rgba(192,72,25,0.32); height: $header-height; position: fixed; top: 0; left: 0; right: 0; z-index: 100; .logo { width: 162px; height: 36px; background-image: url("/assets/images/logo.svg"); background-size: contain; background-repeat: no-repeat; background-position: center center; float: left; margin-top: 22px; margin-left: 15px; @media all and (max-width: $phablet-width) { float: none; height: 29px; display: block; margin: 0 auto; margin-top: 10px; } } nav { font-size: 17px; color: $main-color; float: right; margin-top: 29px; @media all and (max-width: $phablet-width) { float: none; text-align: center; font-size: 16px; margin-top: 10px; } a { position: relative; cursor: pointer; &::before { content: ""; background-color: $main-color; height: 3px; border-radius: 2px; position: absolute; left: 0; right: 0; bottom: -5px; display: none; } &:hover::before { display: block; } } .github-icon { width: 26px; height: 25px; background-image: url("/assets/images/github.svg"); display: inline-block; vertical-align: middle; position: relative; top: -3px; } } ul { white-space: nowrap; padding: 0; li { display: inline-block; margin: 0 15px; } } } ================================================ FILE: docs-src/assets/css/highlight.styl ================================================ .highlight { $code-color = rgba(0,0,0,0.62); background-color: #F8F8F8; padding: 10px 23px; font-size: 14px; line-height: normal; color: $code-color; overflow-x: auto; .editor { font-family: $code-font; } .line { margin: 6px 0; } &.bash .line::before { content: "$ "; opacity: 0.5; } // Colors: $purple = #8858d2; $green = #4ac14a; $pink = #b646c1; $blue = #00a9ff; $orange = #f7af00; $gray = rgba(0, 0, 0, 0.3); .string { color: $purple; } .html { &.name.tag { color: $green; } &.attribute-name { color: $pink; } } .js { &.name.function { color: $main-color; } &.boolean, &.numeric { color: $green; } &.control, &.assignment { color: $pink; } &.storage, &.storage, &.variable { color: $blue; } &.comment { color: $gray; } &.function { color: inherit; } &.variable { &.other, &.parameter { color: inherit; } } &.storage.class, // "extends": &.class + * + .storage.modifier { color: $pink; } } .css { &.selector { color: $green; } &.property-name { color: $blue; } &.property-value { color: $purple; } &.separator, &.terminator { color: $code-color; } } } ================================================ FILE: docs-src/assets/css/index.styl ================================================ $modal-width = 409px; .landing-top { height: 370px; position: relative; padding-top: $header-height; @media all and (max-width: $tablet-width) { height: 600px; } .bg { background-image: linear-gradient(-132deg, #FF7D79 0%, #F28B74 92%); position: absolute; left: 0; right: 0; top: 0; bottom: 0; -webkit-clip-path: url("#top-transition-clip-shape"); clip-path: url("#top-transition-clip-shape"); will-change: transform; /* For Safari */ /* * For some reason, clip path makes the whole page * flicker in Mobile Safari. * So we disable it for mobile. */ @media all and (max-width: $phablet-width) { -webkit-clip-path: none; clip-path: none; } } .swal-modal-example { transform: none; opacity: 1; height: 292px; width: $modal-width; margin: 20px; background-color: white; box-shadow: 0 5px 22px 0 rgba(0,0,0,0.20); border-radius: 8px; margin-top: 59px; text-align: center; display: inline-block; vertical-align: middle; overflow: hidden; transition: height 0.3s; position: absolute; z-index: 10; top: 0; left: 0; @media all and (max-width: $tablet-width) { position: relative; display: block; margin: 20px auto; } @media all and (max-width: $mobile-width) { width: 100%; } &[data-type="success"] { height: 292px; } &[data-type="warning"] { height: 325px; } .swal-title { padding-top: 10px; } .swal-text { color: rgba(0,0,0,0.48); margin-top: 6px; } } .modal-content-overlay { position: absolute; top: 0; bottom: 0; left: 0; right: 0; background-color: white; z-index: 2; pointer-events: none; opacity: 0; transition: opacity 0.2s; &.show { opacity: 1; } } .example-content { display: none; &.show { display: block; } } .desc { display: inline-block; position: relative; color: white; margin-left: 50px; max-width: s('calc(100% - %s - 112px)', $modal-width); vertical-align: middle; margin-top: 61px; padding-left: 473px; @media all and (max-width: $tablet-width) { display: block; max-width: none; padding-left: 0; margin-left: 0; text-align: center; } } h2 { font-size: 30px; line-height: 51px; font-weight: 300; @media all and (max-width: $small-width) { font-size: 25px; margin-top: -20px; } } .text-rotater { display: block; height: 57px; overflow: hidden; span { display: block; transition: transform 0.4s; } &.slide-up span { transform: translateY(-51px); } } .install { background: rgba(120,40,40,0.32); border-radius: 7px; max-width: 357px; padding: 12px; @media all and (max-width: $tablet-width) { margin: 0 auto; text-align: left; } .button { background: rgba(255,255,255,0.39); width: 12px; height: 12px; border-radius: 50%; display: inline-block; } .command { font-family: $code-font; padding: 12px; padding-left: 30px; &::before { content: "$"; opacity: 0.5; transform: rotate(8deg); font-size: 20px; position: absolute; margin-left: -27px; margin-top: -2px; } } } } .comparison-container { padding-bottom: 70px; text-align: center; h3 { font-size: 22px; color: #B49993; font-weight: 400; display: block; text-align: center; margin-top: 93px; margin-bottom: 80px; } .code-container { text-align: center; width: calc(50% - 60px); display: inline-block; vertical-align: middle; @media all and (max-width: $phablet-width) { width: 100%; } } .versus { width: 35px; height: 33px; background-image: url("/assets/images/vs.svg"); display: inline-block; vertical-align: middle; margin: 0 30px; @media all and (max-width: $phablet-width) { margin: 30px; margin-bottom: -10px; } } h5 { font-size: 13px; color: rgba(0,0,0,0.21); text-transform: uppercase; text-align: left; margin-bottom: 15px; &.swal-logo { text-indent: -9999999px; margin-top: 2px; &::after { content: ""; background-image: url("/assets/images/logo-small.svg"); width: 91px; height: 20px; display: block; } } } .highlight { text-align: left; padding: 16px 23px; span { margin: 0 -4px; } } .remark { font-size: 20px; color: $main-color; margin-top: 80px; } .get-started-button { background-color: $main-color; color: white; border-radius: 8px; font-size: 22px; padding: 14px 55px; margin: 20px 0; display: inline-block; } } retina-bg(namespace, extension = 'png') { $path = "/assets/images/"; background-image: url($path + namespace + '.' + extension); background-image: -webkit-image-set(url($path + namespace + '.' + extension) 1x, url($path + namespace + '@2x' + '.' + extension) 2x); } .customize-container { background-color: #999EAF; text-align: center; color: white; text-align: center; retina-bg("pattern") padding: 40px 0; -webkit-clip-path: url("#customization-transition-clip-shape"); clip-path: url("#customization-transition-clip-shape"); will-change: transform; /* For Safari */ @media all and (max-width: $phablet-width) { -webkit-clip-path: none; clip-path: none; } h3 { font-weight: 400; font-size: 20px; padding: 50px 0; } .example-modals { retina-bg("modal-examples") height: 284px; background-size: auto 100%; background-position: 0 0; animation: scrollExamples 80s infinite linear; } .view-api-button { border: 3px solid #FFFFFF; border-radius: 6px; color: white; padding: 12px 52px; font-size: 18px; margin-top: 60px; display: inline-block; } } @keyframes scrollExamples { 0% { background-position: 0 0; } 100% { background-position: -2146px 0; } } ================================================ FILE: docs-src/assets/css/normalize.styl ================================================ body { margin: 0; padding: 0; } a { text-decoration: none; color: $main-color; } p a:hover { text-decoration: underline; } button { cursor: pointer; &:focus { outline: none; } } ul { list-style-type: none; margin: 0; } ================================================ FILE: docs-src/assets/css/table.styl ================================================ .page-content { $mobile-breakpoint: 880px; table { border-collapse: collapse; border: none; width: 100%; } th { font-size: 17px; color: rgba(0,0,0,0.34); padding: 20px 15px; text-transform: capitalize; font-weight: 400; } thead > tr { border-bottom: 2px solid rgba(0,0,0,0.10); } tr { text-align: left; box-shadow: 0px -1px 0px rgba(0,0,0,0.15); &:first-child { box-shadow: none; } } td { padding: 17px; &:first-child > code { color: #2E9FEF; background: none; border: none; font-size: 16px; padding: 0; } } tbody tr { @media all and (min-width: mobile-breakpoint) { &:nth-child(1) { box-shadow: none; } } } @media all and (max-width: mobile-breakpoint) { /* Force table to not be like tables anymore */ & table, & thead, & tbody, & th, & td, & tr { display: block; } /* Hide table headers (but not display: none;, for accessibility) */ & thead tr { position: absolute; top: -9999px; left: -9999px; } & tr { margin-top: -1px; box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.27) } & td { /* Behave like a "row" */ border: none; border-bottom: 1px solid #eee; position: relative; padding-left: mobile-padding; min-height: 20px; } & td::before { /* Now like a table header */ position: absolute; /* Top/left values mimic padding */ top: 15px; left: 15px; width: mobile-padding; width: calc(mobile-padding - 35px); overflow: hidden; text-overflow: ellipsis; padding-right: 10px; white-space: nowrap; color: rgba(0, 0, 0, 0.54); font-family: page-font; font-size: 16px; text-transform: capitalize; content: attr(data-name); } } } ================================================ FILE: docs-src/assets/css/variables.styl ================================================ $main-color = #F27474; $header-height = 80px; $code-font = 'Inconsolata', monospace; $tablet-width = 1000px; $phablet-width = 600px; $mobile-width = 450px; $small-width = 380px; ================================================ FILE: docs-src/assets/js/add-preview-buttons.js ================================================ const Babel = require('babel-standalone'); /* * In our Markdown files, we have some tags. * We want to transform these into button.preview, * which onclick will run the JS code right above them! */ const previewPlaceholders = document.querySelectorAll('preview-button'); const createButton = placeholder => { let button = document.createElement('button'); button.className = "preview"; button.innerText = "Preview"; // Add button right above placeholder placeholder.parentNode.insertBefore(button, placeholder) return button; }; const getCodeEl = placeholder => { return placeholder.parentNode.previousSibling.previousSibling; }; const getCode = highlightEl => highlightEl.innerText.trim(); const resetStyles = () => { const swalOverlay = document.querySelector('.swal-overlay'); const allSwalEls = swalOverlay.querySelectorAll('*'); swalOverlay.removeAttribute('style'); allSwalEls.forEach((el) => { el.removeAttribute('style'); }); }; const setStyles = (code) => { const array = code.split(/[{}]/g); const selector = array[0].trim(); const el = document.querySelector(selector); let css = array[1].trim(); css = css.replace(/\s+/g, ' '); css = css.replace(/;\s?/g, '; '); css = css.replace(/:\s?/g, ': '); el.style.cssText = css; }; previewPlaceholders.forEach((placeholder) => { const highlightEl = getCodeEl(placeholder); const code = getCode(highlightEl); const button = createButton(placeholder); const givenFunction = placeholder.dataset.function; let lang = highlightEl.classList[1]; /* * If there's a specified data-function on , call that. * Othwerwise, just use the code from the highlightjs above it: */ button.addEventListener('click', () => { if (givenFunction) { window[givenFunction](); } else if (lang === "css") { swal("Sweet!", "I like customizing!"); resetStyles(); setStyles(code); } else { const transpiledCode = Babel.transform(code, { presets: ['es2015'] }).code; eval(transpiledCode); } }); placeholder.remove(); }); ================================================ FILE: docs-src/assets/js/index.js ================================================ // Fetch polyfill import 'whatwg-fetch'; // NodeList.forEach() polyfill import 'nodelist-foreach-polyfill'; import './add-preview-buttons'; import './landing-text-rotater'; /* * FOR GUIDES EXAMPLES: */ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import swalWithReact from '@sweetalert/with-react'; const DEFAULT_INPUT_TEXT = ""; class MyInput extends Component { constructor(props) { super(props); this.state = { text: DEFAULT_INPUT_TEXT, }; } changeText(e) { let text = e.target.value; this.setState({ text, }); /* * This will update the value that the confirm * button resolves to: */ swal.setActionValue(text); } render() { return ( ) } } // We want to retrieve MyInput as a pure DOM node: let wrapper = document.createElement('div'); ReactDOM.render(, wrapper); let el = wrapper.firstChild; window.reactExample = () => { swal({ text: "Write something here:", content: el, buttons: { confirm: { value: DEFAULT_INPUT_TEXT, }, }, }) .then((value) => { swal(`You typed: ${value}`); }); }; window.withReactExample = () => { swalWithReact(

Hello world!

This is now rendered with JSX!

); }; window.withReactOptionsExample = () => { const onPick = value => { swal("Thanks for your rating!", `You rated us ${value}/3`, "success") } const MoodButton = ({ rating, onClick }) => (

SweetAlert makes popup messages easy and pretty.

Normal alert
alert ( "Oops, something went wrong!" )
swal ( "Oops" ,  "Something went wrong!" ,  "error" )

Pretty cool, huh?

Get started!

You can customize SweetAlert to fit your needs

View docs
================================================ FILE: docs-src/layout-docs.hbs ================================================ ================================================ FILE: docs-src/layout-guides.hbs ================================================ ================================================ FILE: docs-src/layout.hbs ================================================ SweetAlert {{{body}}}
================================================ FILE: package.json ================================================ { "name": "sweetalert", "version": "2.1.2", "description": "A beautiful replacement for JavaScript's \"alert\"", "main": "dist/sweetalert.min.js", "types": "typings/sweetalert.d.ts", "scripts": { "build": "node_modules/.bin/webpack -p", "buildtest": "npm run build && jest", "test": "node_modules/.bin/jest", "builddocs": "node_modules/jus/cli.js build docs-src docs", "docs": "npm run build && node_modules/jus/cli.js serve docs-src", "prepare": "npm run build && npm run builddocs", "prepublishOnly": "npm run build" }, "repository": { "type": "git", "url": "https://github.com/t4t5/sweetalert" }, "keywords": [ "sweetalert", "alert", "modal", "popup" ], "author": "Tristan Edwards (https://tristanedwards.me)", "license": "MIT", "bugs": { "url": "https://github.com/t4t5/sweetalert/issues" }, "homepage": "https://sweetalert.js.org/", "devDependencies": { "@types/jest": "19.2.3", "autoprefixer": "6.7.7", "babel-core": "6.24.1", "babel-loader": "6.4.1", "babel-plugin-transform-es2015-modules-commonjs": "6.24.1", "babel-plugin-transform-runtime": "6.23.0", "babel-preset-env": "1.4.0", "babel-preset-es2015": "6.24.1", "babel-preset-react": "6.24.1", "babel-standalone": "^6.26.0", "babelify": "^6.0.2", "browserify": "^9.0.8", "copy-webpack-plugin": "^4.0.1", "css-loader": "0.28.7", "dts-bundle": "0.7.3", "exports-loader": "0.6.4", "expose-loader": "0.7.3", "glob": "^5.0.3", "jest": "19.0.2", "jquery": "3.2.1", "jus": "0.24.1", "nodelist-foreach-polyfill": "^1.2.0", "opencollective": "^1.0.3", "path": "^0.11.14", "postcss-color-function": "3.0.0", "postcss-custom-properties": "5.0.2", "postcss-easy-import": "2.0.0", "postcss-loader": "1.3.3", "postcss-nesting": "2.3.1", "react": "15.5.4", "react-dom": "15.5.4", "source-map-loader": "0.2.1", "sweetalert": "file:./", "@sweetalert/with-react": "^0.1.1", "style-loader": "0.18.2", "ts-jest": "19.0.14", "ts-loader": "2.0.3", "tslint": "5.1.0", "tslint-loader": "3.5.2", "typescript": "2.2.2", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", "webpack": "3.5.5", "webpack-bundle-analyzer": "2.9.0", "webpack-dev-server": "2.4.2", "webpack-merge": "4.1.0", "whatwg-fetch": "^2.0.3" }, "jest": { "verbose": true, "transform": { "^.+\\.tsx?$": "/node_modules/ts-jest/preprocessor.js" }, "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", "moduleFileExtensions": [ "ts", "tsx", "js", "json" ] }, "files": [ "dist", "LICENSE.md", "README.md", "typings" ], "dependencies": { "es6-object-assign": "^1.1.0", "promise-polyfill": "^6.0.2" }, "collective": { "type": "opencollective", "url": "https://opencollective.com/SweetAlert" } } ================================================ FILE: postcss.config.js ================================================ module.exports = { map: true, plugins: [ require('postcss-easy-import'), require('postcss-nesting'), require('postcss-custom-properties'), require('postcss-color-function'), require('autoprefixer'), ] } ================================================ FILE: src/core.ts ================================================ /* * SweetAlert * 2014-2017 – Tristan Edwards * https://github.com/t4t5/sweetalert */ import init from './modules/init'; import { openModal, onAction, getState, stopLoading, } from './modules/actions'; import state, { setActionValue, ActionOptions, SwalState, } from './modules/state'; import { SwalOptions, getOpts, setDefaults, } from './modules/options'; export type SwalParams = (string|Partial)[]; export interface SweetAlert { (...params: SwalParams): Promise, close? (namespace?: string): void, getState? (): SwalState, setActionValue? (opts: string|ActionOptions): void, stopLoading? (): void, setDefaults? (opts: object): void, }; const swal:SweetAlert = (...args) => { // Prevent library to be run in Node env: if (typeof window === 'undefined') return; const opts: SwalOptions = getOpts(...args); return new Promise((resolve, reject) => { state.promise = { resolve, reject }; init(opts); // For fade animation to work: setTimeout(() => { openModal(); }); }); }; swal.close = onAction; swal.getState = getState; swal.setActionValue = setActionValue; swal.stopLoading = stopLoading; swal.setDefaults = setDefaults; export default swal; ================================================ FILE: src/css/button-loader.css ================================================ .swal-button--loading { color: transparent; & ~ .swal-button__loader { opacity: 1; } } .swal-button__loader { position: absolute; height: auto; width: 43px; z-index: 2; left: 50%; top: 50%; transform: translateX(-50%) translateY(-50%); text-align: center; pointer-events: none; opacity: 0; & div { display: inline-block; float: none; vertical-align: baseline; width: 9px; height: 9px; padding: 0; border: none; margin: 2px; opacity: 0.4; border-radius: 7px; background-color: rgba(255, 255, 255, 0.9); transition: background 0.2s; animation: swal-loading-anim 1s infinite; &:nth-child(3n+2) { animation-delay: 0.15s; } &:nth-child(3n+3) { animation-delay: 0.3s; } } } @keyframes swal-loading-anim { 0% { opacity: 0.4; } 20% { opacity: 0.4; } 50% { opacity: 1.0; } 100% { opacity: 0.4; } } ================================================ FILE: src/css/buttons.css ================================================ :root { --swal-btn-confirm: #7cd1f9; --swal-btn-confirm-hover: color(var(--swal-btn-confirm) shade(3%)); --swal-btn-confirm-active: color(var(--swal-btn-confirm) shade(10%)); --swal-btn-cancel: #EFEFEF; --swal-btn-cancel-hover: color(var(--swal-btn-cancel) shade(3%)); --swal-btn-cancel-active: color(var(--swal-btn-cancel) shade(10%)); --swal-btn-danger: #e64942; --swal-btn-danger-hover: color(var(--swal-btn-danger) shade(3%)); --swal-btn-danger-active: color(var(--swal-btn-danger) shade(10%)); --swal-focus-color: rgba(43, 114, 165, 0.3); } .swal-footer { text-align: right; padding-top: 13px; margin-top: 13px; padding: 13px 16px; border-radius: inherit; border-top-left-radius: 0; border-top-right-radius: 0; } .swal-button-container { margin: 5px; display: inline-block; position: relative; } .swal-button { background-color: var(--swal-btn-confirm); color: white; border: none; box-shadow: none; border-radius: 5px; font-weight: 600; font-size: 14px; padding: 10px 24px; margin: 0; cursor: pointer; &:not([disabled]):hover { background-color: var(--swal-btn-confirm-hover); } &:active { background-color: var(--swal-btn-confirm-active); } &:focus { outline: none; box-shadow: 0px 0px 0px 1px white, 0px 0px 0px 3px rgba(43, 114, 165, 0.29); } &[disabled] { opacity: 0.5; cursor: default; } /* Remove ugly dotted lines in FireFox: */ &::-moz-focus-inner { border: 0; } &--cancel { color: #555555; background-color: var(--swal-btn-cancel); &:not([disabled]):hover { background-color: var(--swal-btn-cancel-hover); } &:active { background-color: var(--swal-btn-cancel-active); } &:focus { box-shadow: 0px 0px 0px 1px white, 0px 0px 0px 3px rgba(116, 136, 150, 0.29); } } &--danger { background-color: var(--swal-btn-danger); &:not([disabled]):hover { background-color: var(--swal-btn-danger-hover); } &:active { background-color: var(--swal-btn-danger-active); } &:focus { box-shadow: 0px 0px 0px 1px white, 0px 0px 0px 3px rgba(165, 43, 43, 0.29); } } } ================================================ FILE: src/css/content.css ================================================ .swal-content { padding: 0 20px; margin-top: 20px; font-size: initial; &:last-child { margin-bottom: 20px; } &__input, &__textarea { -webkit-appearance: none; background-color: white; border: none; font-size: 14px; display: block; box-sizing: border-box; width: 100%; border: 1px solid rgba(0, 0, 0, 0.14); padding: 10px 13px; border-radius: 2px; transition: border-color 0.2s; &:focus { outline: none; border-color: #6DB8FF; } } &__textarea { resize: vertical; } } ================================================ FILE: src/css/icons/error.css ================================================ :root { --swal-red: #F27474; } .swal-icon--error { border-color: var(--swal-red); animation: animateErrorIcon 0.5s; &__x-mark { position: relative; display: block; animation: animateXMark 0.5s; } &__line { position: absolute; height: 5px; width: 47px; background-color: var(--swal-red); display: block; top: 37px; border-radius: 2px; &--left { transform: rotate(45deg); left: 17px; } &--right { transform: rotate(-45deg); transform: rotate(-45deg); right: 16px; } } } @keyframes animateErrorIcon { from { transform: rotateX(100deg); opacity: 0; } to { transform: rotateX(0deg); opacity: 1; } } @keyframes animateXMark { 0% { transform: scale(0.4); margin-top: 26px; opacity: 0; } 50% { transform: scale(0.4); margin-top: 26px; opacity: 0; } 80% { transform: scale(1.15); margin-top: -6px; } 100% { transform: scale(1); margin-top: 0; opacity: 1; } } ================================================ FILE: src/css/icons/info.css ================================================ :root { --swal-blue: #C9DAE1; } .swal-icon--info { border-color: var(--swal-blue); /* "i"-letter body */ &::before { content: ""; position: absolute; width: 5px; height: 29px; left: 50%; bottom: 17px; border-radius: 2px; margin-left: -2px; background-color: var(--swal-blue); } /* "i"-letter dot */ &::after { content: ""; position: absolute; width: 7px; height: 7px; border-radius: 50%; margin-left: -3px; top: 19px; background-color: var(--swal-blue); left: 50%; } } ================================================ FILE: src/css/icons/success.css ================================================ :root { --swal-green: #A5DC86; --swal-green-light: rgba(165, 220, 134, 0.2); } .swal-icon--success { border-color: var(--swal-green); /* Moving circular line */ &::before, &::after { content: ''; border-radius: 50%; position: absolute; width: 60px; height: 120px; background: white; transform: rotate(45deg); } &::before { border-radius: 120px 0 0 120px; top: -7px; left: -33px; transform: rotate(-45deg); transform-origin: 60px 60px; } &::after { border-radius: 0 120px 120px 0; top: -11px; left: 30px; transform: rotate(-45deg); transform-origin: 0px 60px; animation: rotatePlaceholder 4.25s ease-in; } /* Ring */ &__ring { width: 80px; height: 80px; border: 4px solid var(--swal-green-light); border-radius: 50%; box-sizing: content-box; position: absolute; left: -4px; top: -4px; z-index: 2; } /* Hide corners left from animation */ &__hide-corners { width: 5px; height: 90px; background-color: white; padding: 1px; position: absolute; left: 28px; top: 8px; z-index: 1; transform: rotate(-45deg); } &__line { height: 5px; background-color: var(--swal-green); display: block; border-radius: 2px; position: absolute; z-index: 2; &--tip { width: 25px; left: 14px; top: 46px; transform: rotate(45deg); animation: animateSuccessTip 0.75s; } &--long { width: 47px; right: 8px; top: 38px; transform: rotate(-45deg); animation: animateSuccessLong 0.75s; } } } @keyframes rotatePlaceholder { 0% { transform: rotate(-45deg); } 5% { transform: rotate(-45deg); } 12% { transform: rotate(-405deg); } 100% { transform: rotate(-405deg); } } @keyframes animateSuccessTip { 0% { width: 0; left: 1px; top: 19px; } 54% { width: 0; left: 1px; top: 19px; } 70% { width: 50px; left: -8px; top: 37px; } 84% { width: 17px; left: 21px; top: 48px; } 100% { width: 25px; left: 14px; top: 45px; } } @keyframes animateSuccessLong { 0% { width: 0; right: 46px; top: 54px; } 65% { width: 0; right: 46px; top: 54px; } 84% { width: 55px; right: 0px; top: 35px; } 100% { width: 47px; right: 8px; top: 38px; } } ================================================ FILE: src/css/icons/warning.css ================================================ :root { --swal-orange: #F8BB86; } .swal-icon--warning { border-color: var(--swal-orange); animation: pulseWarning 0.75s infinite alternate; /* Exclamation mark */ &__body { position: absolute; width: 5px; height: 47px; left: 50%; top: 10px; border-radius: 2px; margin-left: -2px; background-color: var(--swal-orange); } &__dot { position: absolute; width: 7px; height: 7px; border-radius: 50%; margin-left: -4px; left: 50%; bottom: -11px; background-color: var(--swal-orange); } } @keyframes pulseWarning { from { border-color: #F8D486; } to { border-color: var(--swal-orange); } } ================================================ FILE: src/css/icons.css ================================================ @import './icons/error'; @import './icons/warning'; @import './icons/success'; @import './icons/info'; .swal-icon { width: 80px; height: 80px; border-width: 4px; border-style: solid; border-radius: 50%; padding: 0; position: relative; box-sizing: content-box; margin: 20px auto; &:first-child { margin-top: 32px; } &--custom { width: auto; height: auto; max-width: 100%; border: none; border-radius: 0; } & img { max-width: 100%; max-height: 100%; } } ================================================ FILE: src/css/text.css ================================================ .swal-title { color: rgba(0, 0, 0, 0.65); font-weight: 600; text-transform: none; position: relative; display: block; padding: 13px 16px; font-size: 27px; line-height: normal; text-align: center; margin-bottom: 0px; &:first-child { margin-top: 26px; } &:not(:first-child) { padding-bottom: 0; } &:not(:last-child) { margin-bottom: 13px; } } .swal-text { font-size: 16px; position: relative; float: none; line-height: normal; vertical-align: top; text-align: left; display: inline-block; margin: 0; padding: 0 10px; font-weight: 400; color: rgba(0, 0, 0, 0.64); max-width: calc(100% - 20px); overflow-wrap: break-word; box-sizing: border-box; &:first-child { margin-top: 45px; } &:last-child { margin-bottom: 45px; } } ================================================ FILE: src/modules/actions.ts ================================================ import { getNode } from './utils'; import { CANCEL_KEY } from './options/buttons'; import CLASS_NAMES from './class-list'; const { OVERLAY, SHOW_MODAL, BUTTON, BUTTON_LOADING, } = CLASS_NAMES; import state, { SwalState } from './state'; export const openModal = (): void => { let overlay = getNode(OVERLAY); overlay.classList.add(SHOW_MODAL); state.isOpen = true; }; const hideModal = (): void => { let overlay = getNode(OVERLAY); overlay.classList.remove(SHOW_MODAL); state.isOpen = false; }; /* * Triggers when the user presses any button, or * hits Enter inside the input: */ export const onAction = (namespace: string = CANCEL_KEY): void => { const { value, closeModal } = state.actions[namespace]; if (closeModal === false) { const buttonClass = `${BUTTON}--${namespace}`; const button = getNode(buttonClass); button.classList.add(BUTTON_LOADING); } else { hideModal(); } state.promise.resolve(value); }; /* * Filter the state object. Remove the stuff * that's only for internal use */ export const getState = (): SwalState => { const publicState = Object.assign({}, state); delete publicState.promise; delete publicState.timer; return publicState; }; /* * Stop showing loading animation on button * (to display error message in input for example) */ export const stopLoading = (): void => { const buttons: NodeListOf = document.querySelectorAll(`.${BUTTON}`); for (let i = 0; i < buttons.length; i++) { const button: Element = buttons[i]; button.classList.remove(BUTTON_LOADING); } }; ================================================ FILE: src/modules/class-list/index.ts ================================================ /* * List of class names that we * use throughout the library to * manipulate the DOM. */ export interface ClassNameList { [key: string]: string, }; const OVERLAY: string = 'swal-overlay'; const BUTTON: string = 'swal-button'; const ICON: string = 'swal-icon'; export const CLASS_NAMES: ClassNameList = { MODAL: 'swal-modal', OVERLAY, SHOW_MODAL: `${OVERLAY}--show-modal`, MODAL_TITLE: `swal-title`, MODAL_TEXT: `swal-text`, ICON, ICON_CUSTOM: `${ICON}--custom`, CONTENT: 'swal-content', FOOTER: 'swal-footer', BUTTON_CONTAINER: 'swal-button-container', BUTTON, CONFIRM_BUTTON: `${BUTTON}--confirm`, CANCEL_BUTTON: `${BUTTON}--cancel`, DANGER_BUTTON: `${BUTTON}--danger`, BUTTON_LOADING: `${BUTTON}--loading`, BUTTON_LOADER: `${BUTTON}__loader`, }; export default CLASS_NAMES; ================================================ FILE: src/modules/event-listeners.ts ================================================ import state from './state'; import { onAction } from './actions'; import { getNode } from './utils'; import { SwalOptions } from './options'; import { CANCEL_KEY } from './options/buttons'; import CLASS_NAMES from './class-list'; const { MODAL, BUTTON, OVERLAY } = CLASS_NAMES; const onTabAwayLastButton = (e: KeyboardEvent): void => { e.preventDefault(); setFirstButtonFocus(); }; const onTabBackFirstButton = (e: KeyboardEvent): void => { e.preventDefault(); setLastButtonFocus(); }; const onKeyUp = (e: KeyboardEvent):void => { if (!state.isOpen) return; switch (e.key) { case "Escape": return onAction(CANCEL_KEY); } }; const onKeyDownLastButton = (e: KeyboardEvent): void => { if (!state.isOpen) return; switch (e.key) { case "Tab": return onTabAwayLastButton(e); } }; const onKeyDownFirstButton = (e: KeyboardEvent): void => { if (!state.isOpen) return; if (e.key === "Tab" && e.shiftKey) { return onTabBackFirstButton(e); } }; /* * Set default focus on Confirm-button */ const setFirstButtonFocus = (): void => { const button:HTMLElement = getNode(BUTTON); if (button) { button.tabIndex = 0; button.focus(); } }; const setLastButtonFocus = (): void => { const modal: HTMLElement = getNode(MODAL); const buttons: NodeListOf = modal.querySelectorAll(`.${BUTTON}`); const lastIndex: number = buttons.length - 1; const lastButton: any = buttons[lastIndex]; if (lastButton) { lastButton.focus(); } }; const setTabbingForLastButton = (buttons: NodeListOf): void => { const lastIndex: number = buttons.length - 1; const lastButton: Element = buttons[lastIndex]; lastButton.addEventListener('keydown', onKeyDownLastButton); }; const setTabbingForFirstButton = (buttons: NodeListOf): void => { const firstButton: Element = buttons[0]; firstButton.addEventListener('keydown', onKeyDownFirstButton); }; const setButtonTabbing = (): void => { const modal: HTMLElement = getNode(MODAL); const buttons: NodeListOf = modal.querySelectorAll(`.${BUTTON}`); if (!buttons.length) return; setTabbingForLastButton(buttons); setTabbingForFirstButton(buttons); }; const onOutsideClick = (e: MouseEvent): void => { const overlay: HTMLElement = getNode(OVERLAY); // Don't trigger for children: if (overlay !== e.target) return; return onAction(CANCEL_KEY); }; const setClickOutside = (allow: boolean): void => { const overlay: HTMLElement = getNode(OVERLAY); overlay.removeEventListener('click', onOutsideClick); if (allow) { overlay.addEventListener('click', onOutsideClick); } }; const setTimer = (ms: number): void => { if (state.timer) { clearTimeout(state.timer); } if (ms) { state.timer = window.setTimeout(() => { return onAction(CANCEL_KEY); }, ms); } }; const addEventListeners = (opts: SwalOptions):void => { if (opts.closeOnEsc) { document.addEventListener('keyup', onKeyUp); } else { document.removeEventListener('keyup', onKeyUp); } /* So that you don't accidentally confirm something * dangerous by clicking enter */ if (opts.dangerMode) { setFirstButtonFocus(); } else { setLastButtonFocus(); } setButtonTabbing(); setClickOutside(opts.closeOnClickOutside); setTimer(opts.timer); }; export default addEventListeners; ================================================ FILE: src/modules/init/buttons.ts ================================================ import { stringToNode } from '../utils'; import { injectElIntoModal } from './modal'; import CLASS_NAMES from '../class-list'; const { BUTTON, DANGER_BUTTON } = CLASS_NAMES; import { ButtonList, ButtonOptions, CONFIRM_KEY } from '../options/buttons'; import { footerMarkup, buttonMarkup } from '../markup'; import { onAction } from '../actions'; import { setActionValue, setActionOptionsFor, ActionOptions, } from '../state'; /* * Generate a button, with a container element, * the right class names, the text, and an event listener. * IMPORTANT: This will also add the button's action, which can be triggered even if the button element itself isn't added to the modal. */ const getButton = (namespace: string, { text, value, className, closeModal, }: ButtonOptions, dangerMode: boolean): Node => { const buttonContainer: any = stringToNode(buttonMarkup); const buttonEl: HTMLElement = buttonContainer.querySelector(`.${BUTTON}`); const btnNamespaceClass = `${BUTTON}--${namespace}`; buttonEl.classList.add(btnNamespaceClass); if (className) { const classNameArray = Array.isArray(className) ? className : className.split(' '); classNameArray .filter(name => name.length > 0) .forEach(name => { buttonEl.classList.add(name); }); } if (dangerMode && namespace === CONFIRM_KEY) { buttonEl.classList.add(DANGER_BUTTON); } buttonEl.textContent = text; let actionValues: ActionOptions = {}; actionValues[namespace] = value; setActionValue(actionValues); setActionOptionsFor(namespace, { closeModal, }); buttonEl.addEventListener('click', () => { return onAction(namespace); }); return buttonContainer; }; /* * Create the buttons-container, * then loop through the ButtonList object * and append every button to it. */ const initButtons = (buttons: ButtonList, dangerMode: boolean): void => { const footerEl: Element = injectElIntoModal(footerMarkup); for (let key in buttons) { const buttonOpts: ButtonOptions = buttons[key] as ButtonOptions; const buttonEl: Node = getButton(key, buttonOpts, dangerMode); if (buttonOpts.visible) { footerEl.appendChild(buttonEl); } } /* * If the footer has no buttons, there's no * point in keeping it: */ if (footerEl.children.length === 0) { footerEl.remove(); } }; export default initButtons; ================================================ FILE: src/modules/init/content.ts ================================================ import { ContentOptions } from '../options/content'; import { CONFIRM_KEY } from '../options/buttons'; import { injectElIntoModal } from './modal'; import { contentMarkup } from '../markup'; import { setActionValue } from '../state'; import { onAction } from '../actions'; import CLASS_NAMES from '../class-list'; const { CONTENT } = CLASS_NAMES; /* * Add an to the content container. * Update the "promised" value of the confirm button whenever * the user types into the input (+ make it "" by default) * Set the default focus on the input. */ const addInputEvents = (input: HTMLElement): void => { input.addEventListener('input', (e) => { const target = e.target as HTMLInputElement; const text = target.value; setActionValue(text); }); input.addEventListener('keyup', (e) => { if (e.key === "Enter") { return onAction(CONFIRM_KEY); } }); /* * FIXME (this is a bit hacky) * We're overwriting the default value of confirm button, * as well as overwriting the default focus on the button */ setTimeout(() => { input.focus(); setActionValue(''); }, 0); }; const initPredefinedContent = (content: Node, elName: string, attrs: any): void => { const el: HTMLElement = document.createElement(elName); const elClass = `${CONTENT}__${elName}`; el.classList.add(elClass); // Set things like "placeholder": for (let key in attrs) { let value: string = attrs[key]; (el)[key] = value; } if (elName === "input") { addInputEvents(el); } content.appendChild(el); }; const initContent = (opts: ContentOptions): void => { if (!opts) return; const content: Node = injectElIntoModal(contentMarkup); const { element, attributes } = opts; if (typeof element === "string") { initPredefinedContent(content, element, attributes); } else { content.appendChild(element); } }; export default initContent; ================================================ FILE: src/modules/init/icon.ts ================================================ //import { stringToNode } from '../utils'; import { injectElIntoModal } from './modal'; import { iconMarkup, errorIconMarkup, warningIconMarkup, successIconMarkup, } from '../markup'; import CLASS_NAMES from '../class-list'; const { ICON, ICON_CUSTOM } = CLASS_NAMES; const PREDEFINED_ICONS: string[] = ["error", "warning", "success", "info"]; const ICON_CONTENTS: any = { error: errorIconMarkup(), warning: warningIconMarkup(), success: successIconMarkup(), } /* * Set the warning, error, success or info icons: */ const initPredefinedIcon = (type: string, iconEl: Element): void => { const iconTypeClass: string = `${ICON}--${type}`; iconEl.classList.add(iconTypeClass); const iconContent: string = ICON_CONTENTS[type]; if (iconContent) { iconEl.innerHTML = iconContent; } }; const initImageURL = (url: string, iconEl: Element): void => { iconEl.classList.add(ICON_CUSTOM); let img = document.createElement('img'); img.src = url; iconEl.appendChild(img); }; const initIcon = (str: string): void => { if (!str) return; let iconEl: Element = injectElIntoModal(iconMarkup); if (PREDEFINED_ICONS.includes(str)) { initPredefinedIcon(str, iconEl); } else { initImageURL(str, iconEl); } }; export default initIcon; ================================================ FILE: src/modules/init/index.ts ================================================ import { getNode } from '../utils'; import { SwalOptions } from '../options'; import CLASS_NAMES from '../class-list'; const { MODAL } = CLASS_NAMES; import initModalOnce, { initModalContent, } from './modal'; import initOverlayOnce from './overlay'; import addEventListeners from '../event-listeners'; import { throwErr } from '../utils'; /* * Inject modal and overlay into the DOM * Then format the modal according to the given opts */ export const init = (opts: SwalOptions): void => { const modal: Element = getNode(MODAL); if (!modal) { if (!document.body) { throwErr("You can only use SweetAlert AFTER the DOM has loaded!"); } initOverlayOnce(); initModalOnce(); } initModalContent(opts); addEventListeners(opts); }; export default init; ================================================ FILE: src/modules/init/modal.ts ================================================ import { ButtonList } from './../options/buttons'; import { stringToNode, getNode } from '../utils'; import { modalMarkup } from '../markup'; import { SwalOptions } from '../options'; import CLASS_NAMES from '../class-list'; const { MODAL, OVERLAY } = CLASS_NAMES; import initIcon from './icon'; import { initTitle, initText } from './text'; import initButtons from './buttons'; import initContent from './content'; export const injectElIntoModal = (markup: string): HTMLElement => { const modal: Element = getNode(MODAL); const el: HTMLElement = stringToNode(markup); modal.appendChild(el); return el; }; /* * Remove eventual added classes + * reset all content inside: */ const resetModalElement = (modal: Element): void => { modal.className = MODAL; modal.textContent = ''; }; /* * Add custom class to modal element */ const customizeModalElement = (modal: Element, opts: SwalOptions): void => { resetModalElement(modal); const { className } = opts; if (className) { modal.classList.add(className); } }; /* * It's important to run the following functions in this particular order, * so that the elements get appended one after the other. */ export const initModalContent = (opts: SwalOptions): void => { // Start from scratch: const modal: Element = getNode(MODAL); customizeModalElement(modal, opts); initIcon(opts.icon); initTitle(opts.title); initText(opts.text); initContent(opts.content); initButtons(opts.buttons as ButtonList, opts.dangerMode); }; const initModalOnce = (): void => { const overlay: Element = getNode(OVERLAY); const modal = stringToNode(modalMarkup); overlay.appendChild(modal); }; export default initModalOnce; ================================================ FILE: src/modules/init/overlay.ts ================================================ import { stringToNode } from '../utils'; import { overlayMarkup } from '../markup'; const initOverlayOnce = (): void => { const overlay = stringToNode(overlayMarkup); document.body.appendChild(overlay); }; export default initOverlayOnce; ================================================ FILE: src/modules/init/text.ts ================================================ import { titleMarkup, textMarkup, } from '../markup'; import { injectElIntoModal } from './modal'; /* * Fixes a weird bug that doesn't wrap long text in modal * This is visible in the Safari browser for example. * https://stackoverflow.com/a/3485654/2679245 */ const webkitRerender = (el: HTMLElement) => { if (navigator.userAgent.includes('AppleWebKit')) { el.style.display = 'none'; el.offsetHeight; el.style.display = ''; } } export const initTitle = (title: string): void => { if (title) { const titleEl: HTMLElement = injectElIntoModal(titleMarkup); titleEl.textContent = title; webkitRerender(titleEl); } }; export const initText = (text: string): void => { if (text) { let textNode = document.createDocumentFragment(); text.split('\n').forEach((textFragment, index, array) => { textNode.appendChild(document.createTextNode(textFragment)); // unless we are on the last element, append a
if (index < array.length - 1) { textNode.appendChild(document.createElement('br')); } }); const textEl: HTMLElement = injectElIntoModal(textMarkup); textEl.appendChild(textNode); webkitRerender(textEl); } }; ================================================ FILE: src/modules/markup/buttons.ts ================================================ import CLASS_NAMES from '../class-list'; const { BUTTON_CONTAINER, BUTTON, BUTTON_LOADER, } = CLASS_NAMES; export const buttonMarkup: string = `
`; ================================================ FILE: src/modules/markup/content.ts ================================================ import CLASS_NAMES from '../class-list'; const { CONTENT } = CLASS_NAMES; export const contentMarkup: string = `
`; ================================================ FILE: src/modules/markup/icons.ts ================================================ import CLASS_NAMES from '../class-list'; const { ICON } = CLASS_NAMES; export const errorIconMarkup = (): string => { const icon = `${ICON}--error`; const line = `${icon}__line`; const markup = `
`; return markup; } export const warningIconMarkup = (): string => { const icon = `${ICON}--warning`; return ` `; }; export const successIconMarkup = (): string => { const icon = `${ICON}--success`; return `
`; }; ================================================ FILE: src/modules/markup/index.ts ================================================ export * from './modal'; export { default as overlayMarkup } from './overlay'; export * from './icons'; export * from './content'; export * from './buttons'; import CLASS_NAMES from '../class-list'; const { MODAL_TITLE, MODAL_TEXT, ICON, FOOTER, } = CLASS_NAMES; export const iconMarkup: string = `
` ; export const titleMarkup: string = `
`; export const textMarkup: string = `
` ; export const footerMarkup: string = `
`; ================================================ FILE: src/modules/markup/modal.ts ================================================ import CLASS_NAMES from '../class-list'; const { MODAL, } = CLASS_NAMES; export const modalMarkup: string =` ` ; export default modalMarkup; ================================================ FILE: src/modules/markup/overlay.ts ================================================ import CLASS_NAMES from '../class-list'; const { OVERLAY, } = CLASS_NAMES; const overlay: string = `
` ; export default overlay; ================================================ FILE: src/modules/options/buttons.ts ================================================ import { isPlainObject, throwErr } from '../utils'; export interface ButtonOptions { visible?: boolean, text?: string, value?: any, className?: string | Array, closeModal?: boolean, }; export interface ButtonList { [buttonNamespace: string]: ButtonOptions | boolean, }; export const CONFIRM_KEY = 'confirm'; export const CANCEL_KEY = 'cancel'; const defaultButton: ButtonOptions = { visible: true, text: null, value: null, className: '', closeModal: true, }; const defaultCancelButton: ButtonOptions = Object.assign({}, defaultButton, { visible: false, text: "Cancel", value: null, } ); const defaultConfirmButton: ButtonOptions = Object.assign({}, defaultButton, { text: "OK", value: true, } ); export const defaultButtonList: ButtonList = { cancel: defaultCancelButton, confirm: defaultConfirmButton, }; const getDefaultButton = (key: string): ButtonOptions => { switch (key) { case CONFIRM_KEY: return defaultConfirmButton; case CANCEL_KEY: return defaultCancelButton; default: // Capitalize: const text = key.charAt(0).toUpperCase() + key.slice(1); return Object.assign({}, defaultButton, { text, value: key, }); } }; const normalizeButton = (key: string, param: string | object | boolean): ButtonOptions => { const button: ButtonOptions = getDefaultButton(key); /* * Use the default button + make it visible */ if (param === true) { return Object.assign({}, button, { visible: true, }); } /* Set the text of the button: */ if (typeof param === "string") { return Object.assign({}, button, { visible: true, text: param, }); } /* A specified button should always be visible, * unless "visible" is explicitly set to "false" */ if (isPlainObject(param)) { return Object.assign({ visible: true, }, button, param); } return Object.assign({}, button, { visible: false, }); }; const normalizeButtonListObj = (obj: any): ButtonList => { let buttons: ButtonList = {}; for (let key of Object.keys(obj)) { const opts: any = obj[key]; const button: ButtonOptions = normalizeButton(key, opts); buttons[key] = button; } /* * We always need a cancel action, * even if the button isn't visible */ if (!buttons.cancel) { buttons.cancel = defaultCancelButton; } return buttons; }; const normalizeButtonArray = (arr: any[]): ButtonList => { let buttonListObj: ButtonList = {}; switch (arr.length) { /* input: ["Accept"] * result: only set the confirm button text to "Accept" */ case 1: buttonListObj[CANCEL_KEY] = Object.assign({}, defaultCancelButton, { visible: false, }); break; /* input: ["No", "Ok!"] * result: Set cancel button to "No", and confirm to "Ok!" */ case 2: buttonListObj[CANCEL_KEY] = normalizeButton(CANCEL_KEY, arr[0]); buttonListObj[CONFIRM_KEY] = normalizeButton(CONFIRM_KEY, arr[1]); break; default: throwErr(`Invalid number of 'buttons' in array (${arr.length}). If you want more than 2 buttons, you need to use an object!`); } return buttonListObj; }; export const getButtonListOpts = (opts: string | object | boolean): ButtonList => { let buttonListObj: ButtonList = defaultButtonList; if (typeof opts === "string") { buttonListObj[CONFIRM_KEY] = normalizeButton(CONFIRM_KEY, opts); } else if (Array.isArray(opts)) { buttonListObj = normalizeButtonArray(opts); } else if (isPlainObject(opts)) { buttonListObj = normalizeButtonListObj(opts); } else if (opts === true) { buttonListObj = normalizeButtonArray([true, true]); } else if (opts === false) { buttonListObj = normalizeButtonArray([false, false]); } else if (opts === undefined) { buttonListObj = defaultButtonList; } return buttonListObj; }; ================================================ FILE: src/modules/options/content.ts ================================================ import { isPlainObject, } from '../utils'; export interface ContentOptions { element: string|Node, attributes?: object, }; const defaultInputOptions: ContentOptions = { element: 'input', attributes: { placeholder: "", }, }; export const getContentOpts = (contentParam: string|object): ContentOptions => { let opts = {}; if (isPlainObject(contentParam)) { return Object.assign(opts, contentParam); } if (contentParam instanceof Element) { return { element: contentParam, }; } if (contentParam === 'input') { return defaultInputOptions; } return null; }; ================================================ FILE: src/modules/options/deprecations.ts ================================================ /* * A list of all the deprecated options from SweetAlert 1.X * These should log a warning telling users how to upgrade. */ export const logDeprecation = (name: string): void => { const details: OptionReplacement = DEPRECATED_OPTS[name]; const { onlyRename, replacement, subOption, link } = details; const destiny = (onlyRename) ? 'renamed' : 'deprecated'; let message = `SweetAlert warning: "${name}" option has been ${destiny}.`; if (replacement) { const subOptionText = (subOption) ? ` "${subOption}" in ` : ' '; message += ` Please use${subOptionText}"${replacement}" instead.`; } const DOMAIN = 'https://sweetalert.js.org'; if (link) { message += ` More details: ${DOMAIN}${link}`; } else { message += ` More details: ${DOMAIN}/guides/#upgrading-from-1x`; } console.warn(message); }; export interface OptionReplacement { replacement?: string, onlyRename?: boolean, subOption?: string, link?: string, }; export interface OptionReplacementsList { [name: string]: OptionReplacement, }; export const DEPRECATED_OPTS: OptionReplacementsList = { 'type': { replacement: 'icon', link: '/docs/#icon', }, 'imageUrl': { replacement: 'icon', link: '/docs/#icon', }, 'customClass': { replacement: 'className', onlyRename: true, link: '/docs/#classname', }, 'imageSize': {}, 'showCancelButton': { replacement: 'buttons', link: '/docs/#buttons', }, 'showConfirmButton': { replacement: 'button', link: '/docs/#button', }, 'confirmButtonText': { replacement: 'button', link: '/docs/#button', }, 'confirmButtonColor': {}, 'cancelButtonText': { replacement: 'buttons', link: '/docs/#buttons', }, 'closeOnConfirm': { replacement: 'button', subOption: 'closeModal', link: '/docs/#button', }, 'closeOnCancel': { replacement: 'buttons', subOption: 'closeModal', link: '/docs/#buttons', }, 'showLoaderOnConfirm': { replacement: 'buttons', }, 'animation': {}, 'inputType': { replacement: 'content', link: '/docs/#content', }, 'inputValue': { replacement: 'content', link: '/docs/#content', }, 'inputPlaceholder': { replacement: 'content', link: '/docs/#content', }, 'html': { replacement: 'content', link: '/docs/#content', }, 'allowEscapeKey': { replacement: 'closeOnEsc', onlyRename: true, link: '/docs/#closeonesc', }, 'allowClickOutside': { replacement: 'closeOnClickOutside', onlyRename: true, link: '/docs/#closeonclickoutside', }, }; ================================================ FILE: src/modules/options/index.ts ================================================ import { SwalParams } from '../../core'; import { throwErr, isPlainObject, ordinalSuffixOf, } from '../utils'; import { ButtonList, getButtonListOpts, defaultButtonList } from './buttons'; import { getContentOpts, ContentOptions, } from './content'; import { DEPRECATED_OPTS, logDeprecation, } from './deprecations'; /* * The final object that we transform the given params into */ export interface SwalOptions { title: string, text: string, icon: string, buttons: ButtonList | Array, content: ContentOptions, className: string, closeOnClickOutside: boolean, closeOnEsc: boolean, dangerMode: boolean, timer: number, }; const defaultOpts: SwalOptions = { title: null, text: null, icon: null, buttons: defaultButtonList, content: null, className: null, closeOnClickOutside: true, closeOnEsc: true, dangerMode: false, timer: null, }; /* * Default options customizeable through "setDefaults": */ let userDefaults: SwalOptions = Object.assign({}, defaultOpts); export const setDefaults = (opts: object): void => { userDefaults = Object.assign({}, defaultOpts, opts); }; /* * Since the user can set both "button" and "buttons", * we need to make sure we pick one of the options */ const pickButtonParam = (opts: any): object => { const singleButton: string|object = opts && opts.button; const buttonList: object = opts && opts.buttons; if (singleButton !== undefined && buttonList !== undefined) { throwErr(`Cannot set both 'button' and 'buttons' options!`); } if (singleButton !== undefined) { return { confirm: singleButton, }; } else { return buttonList; } }; // Example 0 -> 1st const indexToOrdinal = (index: number): string => ordinalSuffixOf(index + 1); const invalidParam = (param: any, index: number): void => { throwErr(`${indexToOrdinal(index)} argument ('${param}') is invalid`); }; const expectOptionsOrNothingAfter = (index: number, allParams: SwalParams): void => { let nextIndex = (index + 1); let nextParam = allParams[nextIndex]; if (!isPlainObject(nextParam) && nextParam !== undefined) { throwErr(`Expected ${indexToOrdinal(nextIndex)} argument ('${nextParam}') to be a plain object`); } }; const expectNothingAfter = (index: number, allParams: SwalParams): void => { let nextIndex = (index + 1); let nextParam = allParams[nextIndex]; if (nextParam !== undefined) { throwErr(`Unexpected ${indexToOrdinal(nextIndex)} argument (${nextParam})`); } }; /* * Based on the number of arguments, their position and their type, * we return an object that's merged into the final SwalOptions */ const paramToOption = (opts: any, param: any, index: number, allParams: SwalParams): object => { const paramType = (typeof param); const isString = (paramType === "string"); const isDOMNode = (param instanceof Element); if (isString) { if (index === 0) { // Example: swal("Hi there!"); return { text: param, }; } else if (index === 1) { // Example: swal("Wait!", "Are you sure you want to do this?"); // (The text is now the second argument) return { text: param, title: allParams[0], }; } else if (index === 2) { // Example: swal("Wait!", "Are you sure?", "warning"); expectOptionsOrNothingAfter(index, allParams); return { icon: param, }; } else { invalidParam(param, index); } } else if (isDOMNode && index === 0) { // Example: swal(); expectOptionsOrNothingAfter(index, allParams); return { content: param, }; } else if (isPlainObject(param)) { expectNothingAfter(index, allParams); return param; } else { invalidParam(param, index); } }; /* * No matter if the user calls swal with * - swal("Oops!", "An error occurred!", "error") or * - swal({ title: "Oops!", text: "An error occurred!", icon: "error" }) * ... we always want to transform the params into the second version */ export const getOpts = (...params: SwalParams): SwalOptions => { let opts = {}; params.forEach((param, index) => { let changes: object = paramToOption(opts, param, index, params); Object.assign(opts, changes); }); // Since Object.assign doesn't deep clone, // we need to do this: let buttonListOpts = pickButtonParam(opts); opts.buttons = getButtonListOpts(buttonListOpts); delete opts.button; opts.content = getContentOpts(opts.content); const finalOptions: SwalOptions = Object.assign({}, defaultOpts, userDefaults, opts); // Check if the users uses any deprecated options: Object.keys(finalOptions).forEach(optionName => { if (DEPRECATED_OPTS[optionName]) { logDeprecation(optionName); } }); return finalOptions; }; ================================================ FILE: src/modules/state.ts ================================================ import { CONFIRM_KEY } from './options/buttons'; export interface SwalState { isOpen: boolean, promise: { resolve?(value: string): void, reject?(): void, }, actions: { [namespace: string]: { value?: string | any, closeModal?: boolean }, }, timer: number, }; export interface ActionOptions { [buttonNamespace: string]: { value?: string, closeModal?: boolean }, }; const defaultState: SwalState = { isOpen: false, promise: null, actions: {}, timer: null, }; let state: SwalState = Object.assign({}, defaultState); export const resetState = (): void => { state = Object.assign({}, defaultState); } /* * Change what the promise resolves to when the user clicks the button. * This is called internally when using { input: true } for example. */ export const setActionValue = (opts: string|ActionOptions) => { if (typeof opts === "string") { return setActionValueForButton(CONFIRM_KEY, opts); } for (let namespace in opts) { setActionValueForButton(namespace, opts[namespace]); } }; const setActionValueForButton = (namespace: string, value: string | any) => { if (!state.actions[namespace]) { state.actions[namespace] = {}; } Object.assign(state.actions[namespace], { value, }); }; /* * Sets other button options, e.g. * whether the button should close the modal or not */ export const setActionOptionsFor = (buttonKey: string, { closeModal = true, } = {}) => { Object.assign(state.actions[buttonKey], { closeModal, }); }; export default state; ================================================ FILE: src/modules/utils.ts ================================================ /* * Get a DOM element from a class name: */ export const getNode = (className: string): HTMLElement => { const selector = `.${className}`; return document.querySelector(selector); }; export const stringToNode = (html: string): HTMLElement => { let wrapper: HTMLElement = document.createElement('div'); wrapper.innerHTML = html.trim(); return wrapper.firstChild; }; export const insertAfter = (newNode: Node, referenceNode: Node) => { let nextNode = referenceNode.nextSibling; let parentNode = referenceNode.parentNode; parentNode.insertBefore(newNode, nextNode); }; export const removeNode = (node: Node) => { node.parentElement.removeChild(node); }; export const throwErr = (message: string) => { // Remove multiple spaces: message = message.replace(/ +(?= )/g,''); message = message.trim(); throw `SweetAlert: ${message}`; }; /* * Match plain objects ({}) but NOT null */ export const isPlainObject = (value: any): boolean => { if (Object.prototype.toString.call(value) !== '[object Object]') { return false; } else { var prototype = Object.getPrototypeOf(value); return prototype === null || prototype === Object.prototype; } }; /* * Take a number and return a version with ordinal suffix * Example: 1 => 1st */ export const ordinalSuffixOf = (num: number): string => { let j = num % 10; let k = num % 100; if (j === 1 && k !== 11) { return `${num}st`; } if (j === 2 && k !== 12) { return `${num}nd`; } if (j === 3 && k !== 13) { return `${num}rd`; } return `${num}th`; }; ================================================ FILE: src/polyfills.js ================================================ /** * Promise polyfill */ var Promise = require('promise-polyfill'); if (typeof window !== 'undefined' && !window.Promise) { window.Promise = Promise; } /** * Object.assign() polyfill */ require('es6-object-assign/auto'); /** * String.prototype.includes() polyfill * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Polyfill */ if (!String.prototype.includes) { String.prototype.includes = function(search, start) { 'use strict'; if (typeof start !== 'number') { start = 0; } if (start + search.length > this.length) { return false; } else { return this.indexOf(search, start) !== -1; } }; } /** * Array.prototype.includes() polyfill * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes#Polyfill */ if (!Array.prototype.includes) { Object.defineProperty(Array.prototype, 'includes', { value: function(searchElement, fromIndex) { // 1. Let O be ? ToObject(this value). if (this == null) { throw new TypeError('"this" is null or not defined'); } var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If len is 0, return false. if (len === 0) { return false; } // 4. Let n be ? ToInteger(fromIndex). // (If fromIndex is undefined, this step produces the value 0.) var n = fromIndex | 0; // 5. If n ≥ 0, then // a. Let k be n. // 6. Else n < 0, // a. Let k be len + n. // b. If k < 0, let k be 0. var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); function sameValueZero(x, y) { return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)); } // 7. Repeat, while k < len while (k < len) { // a. Let elementK be the result of ? Get(O, ! ToString(k)). // b. If SameValueZero(searchElement, elementK) is true, return true. // c. Increase k by 1. if (sameValueZero(o[k], searchElement)) { return true; } k++; } // 8. Return false return false; } }); } /** * ChildNode.remove() polyfill * @see https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/remove()/remove().md */ if (typeof window !== 'undefined') { (function (arr) { arr.forEach(function (item) { if (item.hasOwnProperty('remove')) { return; } Object.defineProperty(item, 'remove', { configurable: true, enumerable: true, writable: true, value: function remove() { this.parentNode.removeChild(this); } }); }); })([Element.prototype, CharacterData.prototype, DocumentType.prototype]); } ================================================ FILE: src/sweetalert.css ================================================ @import './css/icons'; @import './css/text'; @import './css/buttons'; @import './css/content'; @import './css/button-loader'; :root { --swal-modal-width: 478px; } .swal-overlay { position: fixed; top: 0; bottom: 0; left: 0; right: 0; text-align: center; font-size: 0; /* Remove gap between inline-block elements */ overflow-y: auto; background-color: rgba(0, 0, 0, 0.4); z-index: 10000; pointer-events: none; opacity: 0; transition: opacity 0.3s; &::before { content: ' '; display: inline-block; vertical-align: middle; /* vertical alignment of the inline element */ height: 100%; } &--show-modal { opacity: 1; pointer-events: auto; & .swal-modal { opacity: 1; pointer-events: auto; box-sizing: border-box; animation: showSweetAlert 0.3s; will-change: transform; } } } .swal-modal { width: var(--swal-modal-width); opacity: 0; pointer-events: none; background-color: white; text-align: center; border-radius: 5px; position: static; margin: 20px auto; display: inline-block; vertical-align: middle; transform: scale(1); transform-origin: 50% 50%; z-index: 10001; transition: transform 0.3s, opacity 0.2s; @media all and (max-width: 500px) { width: calc(100% - 20px); } } @keyframes showSweetAlert { 0% { transform: scale(1); } 1% { transform: scale(0.5); } 45% { transform: scale(1.05); } 80% { transform: scale(0.95); } 100% { transform: scale(1); } } /* Target IE8-IE10 due to incompability with the css `pointer-event` property. @see https://github.com/t4t5/sweetalert/issues/863 */ @media screen\0 { .swal-overlay { visibility: hidden; } .swal-overlay--show-modal { visibility: visible; } .swal-button__loader { visibility: hidden; } .swal-overlay--show-modal .swal-modal { visibility: visible; } .swal-modal { visibility: hidden; } } ================================================ FILE: src/sweetalert.d.ts ================================================ import swal, { SweetAlert } from "./core"; declare global { const swal: SweetAlert; const sweetAlert: SweetAlert; } export default swal; export as namespace swal; ================================================ FILE: src/sweetalert.js ================================================ /* * This makes sure that we can use the global * swal() function, instead of swal.default() * See: https://github.com/webpack/webpack/issues/3929 */ if (typeof window !== 'undefined') { require('./sweetalert.css'); } require('./polyfills'); var swal = require('./core').default; module.exports = swal; ================================================ FILE: test/actions.test.ts ================================================ import { swal, removeSwal, $$, CLASS_NAMES, } from './utils'; const { OVERLAY, CONFIRM_BUTTON, TITLE, } = CLASS_NAMES; afterEach(() => removeSwal()); describe("promise value", () => { test("dismisses modal by clicking on overlay", async () => { expect.assertions(1); setTimeout(() => { $$(OVERLAY).click(); }, 500); const value = await swal(); expect(value).toBeNull(); }); test("changes value with setActionValue", async () => { setTimeout(() => { swal.setActionValue("test"); $$(CONFIRM_BUTTON).click(); }, 500); const value = await swal(); expect(value).toEqual("test"); }); test("changes cancel value with setActionValue", async () => { setTimeout(() => { swal.setActionValue({ cancel: "test", }); $$(OVERLAY).click(); }, 500); const value = await swal(); expect(value).toEqual("test"); }); /* * @TODO! * test("cannot dismiss if 'clickOutside' is false", async () => { }); */ }); ================================================ FILE: test/button-options.test.ts ================================================ import { getButtonListOpts } from '../src/modules/options/buttons'; describe("return buttons options", () => { test("returns invisible buttons on false", () => { const opts = getButtonListOpts(false); expect(opts).toMatchObject({ cancel: { visible: false, }, confirm: { visible: false, }, }); }); test("returns default obj on true", () => { const opts = getButtonListOpts(true); expect(opts).toMatchObject({ cancel: { className: "", closeModal: true, text: "Cancel", value: null, visible: true, }, confirm: { className: "", closeModal: true, text: "OK", value: true, visible: true, }, }); }); test("returns single button on string", () => { const opts = getButtonListOpts("Test"); expect(opts).toMatchObject({ cancel: { visible: false, }, confirm: { closeModal: true, text: "Test", value: true, visible: true, }, }); }); test("returns two buttons on array of strings", () => { const opts = getButtonListOpts(["Annuler", "Confirmer"]); expect(opts).toMatchObject({ cancel: { closeModal: true, text: "Annuler", value: null, visible: true, }, confirm: { closeModal: true, text: "Confirmer", value: true, visible: true, }, }); }); test("returns only cancel button when using boolean in array", () => { const opts = getButtonListOpts([true, false]); expect(opts).toMatchObject({ cancel: { closeModal: true, text: "Cancel", value: null, visible: true, }, confirm: { visible: false, }, }); }); }); ================================================ FILE: test/buttons.test.ts ================================================ import { $, swal, removeSwal, $$, onAction, CLASS_NAMES, delay, } from './utils'; const { BUTTON, CONFIRM_BUTTON, CANCEL_BUTTON, MODAL, } = CLASS_NAMES; afterEach(() => removeSwal()); describe("show buttons", () => { test("shows only confirm button by default", () => { swal(); expect($$(BUTTON).length).toBe(1); expect($$(BUTTON).hasClass(CONFIRM_BUTTON)).toBeTruthy(); }); test("hides all buttons", () => { swal({ buttons: false, }); expect($$(BUTTON).length).toBe(0); }); test("shows confirm and cancel buttons", () => { swal({ buttons: true, }); expect($$(BUTTON).length).toBe(2); expect($$(CONFIRM_BUTTON).length).toBe(1); expect($$(CANCEL_BUTTON).length).toBe(1); }); test("sets button text", () => { swal({ button: "Test", }); expect($$(CONFIRM_BUTTON).text()).toBe("Test"); }); test("sets button texts with array", () => { swal({ buttons: ["Stop", "Do it"], }); expect($$(CONFIRM_BUTTON).text()).toBe("Do it"); expect($$(CANCEL_BUTTON).text()).toBe("Stop"); }); test("sets default button texts with array", () => { swal({ buttons: [true, true], }); expect($$(CONFIRM_BUTTON).text()).toBe("OK"); expect($$(CANCEL_BUTTON).text()).toBe("Cancel"); }); test("uses button object", () => { swal({ buttons: { cancel: "Run away!", confirm: true, } }); expect($$(CANCEL_BUTTON).text()).toBe("Run away!"); expect($$(CONFIRM_BUTTON).text()).toBe("OK"); }); test("sets more than 2 buttons", () => { swal({ buttons: { cancel: "Run away!", catch: { text: "Throw Pokéball!", }, defeat: true, }, }); expect($$(BUTTON).length).toBe(3); expect($$(CANCEL_BUTTON).text()).toBe("Run away!"); expect($$(CONFIRM_BUTTON).length).toBe(0); expect($$(`${BUTTON}--catch`).length).toBe(1); expect($$(`${BUTTON}--catch`).text()).toBe("Throw Pokéball!"); expect($$(`${BUTTON}--defeat`).length).toBe(1); expect($$(`${BUTTON}--defeat`).text()).toBe("Defeat"); }); }); describe("buttons resolve values", () => { test("confirm button resolves to true", async () => { expect.assertions(1); setTimeout(() => { $$(CONFIRM_BUTTON).click(); }, 500); const value = await swal(); expect(value).toBeTruthy(); }); test("cancel button resolves to null", async () => { expect.assertions(1); setTimeout(() => { $$(CANCEL_BUTTON).click(); }, 500); const value = await swal({ buttons: true, }); expect(value).toBeNull(); }); test("can specify resolve value", async () => { expect.assertions(1); setTimeout(() => { $$(CONFIRM_BUTTON).click(); }, 500); const value = await swal({ button: { value: "test", }, }); expect(value).toBe("test"); }); test("extra button resolves to string by default", async () => { expect.assertions(1); setTimeout(() => { $(`.${BUTTON}--test`).click(); }, 500); const value = await swal({ buttons: { test: true, }, }); expect(value).toBe("test"); }); }); describe("loading", () => { test("shows loading state", async () => { swal({ button: { text: "HEPP", closeModal: false, }, }); const $button = $(`.${BUTTON}--confirm`); expect($button.hasClass('swal-button--loading')).toBeFalsy(); $button.click(); expect($button.hasClass('swal-button--loading')).toBeTruthy(); }); }); describe("set class name", () => { test("sets single class name as string", async () => { swal({ button: { text: "TEST", closeModal: true, className: 'single-class' }, }); const $button = $(`.${BUTTON}--confirm`); expect($button.hasClass('single-class')).toBeTruthy(); }); test("sets multiple class names as string", async () => { swal({ button: { text: "TEST", closeModal: true, className: 'class1 class2' }, }); const $button = $(`.${BUTTON}--confirm`); expect($button.hasClass('class1')).toBeTruthy(); expect($button.hasClass('class2')).toBeTruthy(); }); test("sets multiple class names as array", async () => { swal({ button: { text: "TEST", closeModal: true, className: ["class1", "class2"] }, }); const $button = $(`.${BUTTON}--confirm`); expect($button.hasClass('class1')).toBeTruthy(); expect($button.hasClass('class2')).toBeTruthy(); }); }); ================================================ FILE: test/content.test.ts ================================================ import { swal, removeSwal, $, $$, CLASS_NAMES, } from './utils'; const { CONTENT, MODAL_TEXT } = CLASS_NAMES; afterEach(() => removeSwal()); describe("show content", () => { test("shows no content by default", () => { swal("Hello"); expect($$(CONTENT).length).toBe(0); }); test("shows input when using content: 'input'", () => { swal({ content: "input", }); const inputSelector = `${CONTENT}__input`; expect($$(CONTENT).length).toBe(1); expect($$(inputSelector).length).toBe(1); expect($$(inputSelector).get(0).getAttribute('type')).toBeNull(); }); test("can customize input with more advanced content options", () => { swal({ content: { element: "input", attributes: { placeholder: "Type your password", type: "password", }, }, }); const inputSelector = `${CONTENT}__input`; expect($$(CONTENT).length).toBe(1); expect($$(inputSelector).length).toBe(1); expect($$(inputSelector).get(0).getAttribute('type')).toBe('password'); expect($$(inputSelector).get(0).getAttribute('placeholder')).toBe('Type your password'); }); test("can set content to custom DOM node", () => { let btn = document.createElement('button'); btn.classList.add('custom-element'); swal({ content: btn, }); expect($$(CONTENT).length).toBe(1); expect($$('custom-element').length).toBe(1); }); }); describe("show modal text", () => { test("transforms newline to break", () => { swal('Hello\nWorld\n'); expect($(`.${MODAL_TEXT} br`).length).toBe(2); }); test("escapes HTML elements", () => { const text = ''; swal(text); expect($(`.${MODAL_TEXT} script`).length).toBe(0); expect($$(MODAL_TEXT).text()).toEqual(expect.stringMatching(text)); }); }); ================================================ FILE: test/icons.test.ts ================================================ import { swal, $$, CLASS_NAMES, removeSwal, } from './utils'; const { ICON, } = CLASS_NAMES; afterEach(() => removeSwal()); describe("show icons", () => { test("shows icon depending on third argument", () => { swal("Error", "An error occurred!", "error"); expect($$(ICON).length).toBe(1); expect($$(ICON).hasClass(`${ICON}--error`)).toBeTruthy(); }); test("shows icon when using 'icon' object key", () => { swal({ icon: 'warning', }); expect($$(ICON).length).toBe(1); expect($$(ICON).hasClass(`${ICON}--warning`)).toBeTruthy(); }); test("hides icon when setting 'icon' key to 'false'", () => { swal({ icon: false, }); expect($$(ICON).length).toBe(0); }); }); ================================================ FILE: test/index.test.ts ================================================ import { $, swal, removeSwal, $$, CLASS_NAMES, } from './utils'; const { MODAL, OVERLAY, MODAL_TITLE, MODAL_TEXT, ICON, FOOTER, } = CLASS_NAMES; afterEach(() => removeSwal()); describe("init", () => { test("adds elements on first call", () => { expect($$(OVERLAY).length).toEqual(0); swal("Hello world!"); expect($$(OVERLAY).length).toBe(1); expect($$(MODAL).length).toBe(1); }); }); describe("string parameters", () => { test("shows text when using 1 param", () => { swal("Hello world!"); expect($$(MODAL_TEXT).is(':first-child')).toBeTruthy(); expect($$(MODAL_TEXT).text()).toBe("Hello world!"); expect($$(MODAL_TEXT).next().hasClass(FOOTER)).toBeTruthy(); }); test("shows title and text when using 2 params", () => { swal("Title", "text"); expect($$(MODAL_TITLE).is(':first-child')).toBeTruthy(); expect($$(MODAL_TITLE).text()).toBe("Title"); expect($$(MODAL_TEXT).text()).toBe("text"); expect($$(MODAL_TEXT).next().hasClass(FOOTER)).toBeTruthy(); }); test("shows icon, title and text when using 3 params", () => { swal("Oops", "text", "error"); expect($$(ICON).is(':first-child')).toBeTruthy(); expect($$(ICON).hasClass(`${ICON}--error`)).toBeTruthy(); expect($$(MODAL_TITLE).is(':nth-child(2)')).toBeTruthy(); expect($$(MODAL_TITLE).text()).toBe("Oops"); expect($$(MODAL_TEXT).is(':nth-child(3)')).toBeTruthy(); expect($$(MODAL_TEXT).text()).toBe("text"); expect($$(MODAL_TEXT).next().hasClass(FOOTER)).toBeTruthy(); }); }); ================================================ FILE: test/utils.ts ================================================ export const swal = require('../dist/sweetalert.min'); export const $ = require('jquery'); export const removeSwal = () => $('.swal-overlay').remove(); export const $$ = (className) => $(`.${className}`); export { CLASS_NAMES } from '../src/modules/class-list'; export const delay = (ms) => { return new Promise(resolve => setTimeout(resolve, ms)); } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "sourceMap": true, "noImplicitAny": true, "module": "commonjs", "declaration": true, "declarationDir": "typings", "target": "es5", "outDir": "tmp", "lib": ["es2015", "es2016", "dom"] }, "include": [ "./src/*.ts" ] } ================================================ FILE: tslint.json ================================================ { "rules": { "no-unused-variable": true } } ================================================ FILE: typings/core.d.ts ================================================ import { ActionOptions, SwalState } from './modules/state'; import { SwalOptions } from './modules/options'; export declare type SwalParams = (string | Partial)[]; export interface SweetAlert { (...params: SwalParams): Promise; close?(namespace?: string): void; getState?(): SwalState; setActionValue?(opts: string | ActionOptions): void; stopLoading?(): void; setDefaults?(opts: object): void; } declare const swal: SweetAlert; export default swal; ================================================ FILE: typings/modules/actions.d.ts ================================================ import { SwalState } from './state'; export declare const openModal: () => void; export declare const onAction: (namespace?: string) => void; export declare const getState: () => SwalState; export declare const stopLoading: () => void; ================================================ FILE: typings/modules/class-list/index.d.ts ================================================ export interface ClassNameList { [key: string]: string; } export declare const CLASS_NAMES: ClassNameList; export default CLASS_NAMES; ================================================ FILE: typings/modules/event-listeners.d.ts ================================================ import { SwalOptions } from './options'; declare const addEventListeners: (opts: SwalOptions) => void; export default addEventListeners; ================================================ FILE: typings/modules/init/buttons.d.ts ================================================ import { ButtonList } from '../options/buttons'; declare const initButtons: (buttons: ButtonList, dangerMode: boolean) => void; export default initButtons; ================================================ FILE: typings/modules/init/content.d.ts ================================================ import { ContentOptions } from '../options/content'; declare const initContent: (opts: ContentOptions) => void; export default initContent; ================================================ FILE: typings/modules/init/icon.d.ts ================================================ declare const initIcon: (str: string) => void; export default initIcon; ================================================ FILE: typings/modules/init/index.d.ts ================================================ import { SwalOptions } from '../options'; export declare const init: (opts: SwalOptions) => void; export default init; ================================================ FILE: typings/modules/init/modal.d.ts ================================================ import { SwalOptions } from '../options'; export declare const injectElIntoModal: (markup: string) => HTMLElement; export declare const initModalContent: (opts: SwalOptions) => void; declare const initModalOnce: () => void; export default initModalOnce; ================================================ FILE: typings/modules/init/overlay.d.ts ================================================ declare const initOverlayOnce: () => void; export default initOverlayOnce; ================================================ FILE: typings/modules/init/text.d.ts ================================================ export declare const initTitle: (title: string) => void; export declare const initText: (text: string) => void; ================================================ FILE: typings/modules/markup/buttons.d.ts ================================================ export declare const buttonMarkup: string; ================================================ FILE: typings/modules/markup/content.d.ts ================================================ export declare const contentMarkup: string; ================================================ FILE: typings/modules/markup/icons.d.ts ================================================ export declare const errorIconMarkup: () => string; export declare const warningIconMarkup: () => string; export declare const successIconMarkup: () => string; ================================================ FILE: typings/modules/markup/index.d.ts ================================================ export * from './modal'; export { default as overlayMarkup } from './overlay'; export * from './icons'; export * from './content'; export * from './buttons'; export declare const iconMarkup: string; export declare const titleMarkup: string; export declare const textMarkup: string; export declare const footerMarkup: string; ================================================ FILE: typings/modules/markup/modal.d.ts ================================================ export declare const modalMarkup: string; export default modalMarkup; ================================================ FILE: typings/modules/markup/overlay.d.ts ================================================ declare const overlay: string; export default overlay; ================================================ FILE: typings/modules/options/buttons.d.ts ================================================ export interface ButtonOptions { visible?: boolean; text?: string; value?: any; className?: string | Array; closeModal?: boolean; } export interface ButtonList { [buttonNamespace: string]: ButtonOptions | boolean; } export declare const CONFIRM_KEY = "confirm"; export declare const CANCEL_KEY = "cancel"; export declare const defaultButtonList: ButtonList; export declare const getButtonListOpts: (opts: string | boolean | object) => ButtonList; ================================================ FILE: typings/modules/options/content.d.ts ================================================ export interface ContentOptions { element: string | Node; attributes?: object; } export declare const getContentOpts: (contentParam: string | object) => ContentOptions; ================================================ FILE: typings/modules/options/deprecations.d.ts ================================================ export declare const logDeprecation: (name: string) => void; export interface OptionReplacement { replacement?: string; onlyRename?: boolean; subOption?: string; link?: string; } export interface OptionReplacementsList { [name: string]: OptionReplacement; } export declare const DEPRECATED_OPTS: OptionReplacementsList; ================================================ FILE: typings/modules/options/index.d.ts ================================================ import { ButtonList } from './buttons'; import { ContentOptions } from './content'; export interface SwalOptions { title: string; text: string; icon: string; buttons: ButtonList | Array; content: ContentOptions; className: string; closeOnClickOutside: boolean; closeOnEsc: boolean; dangerMode: boolean; timer: number; } export declare const setDefaults: (opts: object) => void; export declare const getOpts: (...params: (string | Partial)[]) => SwalOptions; ================================================ FILE: typings/modules/state.d.ts ================================================ export interface SwalState { isOpen: boolean; promise: { resolve?(value: string): void; reject?(): void; }; actions: { [namespace: string]: { value?: string | any; closeModal?: boolean; }; }; timer: number; } export interface ActionOptions { [buttonNamespace: string]: { value?: string; closeModal?: boolean; }; } declare let state: SwalState; export declare const resetState: () => void; export declare const setActionValue: (opts: string | ActionOptions) => void; export declare const setActionOptionsFor: (buttonKey: string, {closeModal}?: { closeModal?: boolean; }) => void; export default state; ================================================ FILE: typings/modules/utils.d.ts ================================================ export declare const getNode: (className: string) => HTMLElement; export declare const stringToNode: (html: string) => HTMLElement; export declare const insertAfter: (newNode: Node, referenceNode: Node) => void; export declare const removeNode: (node: Node) => void; export declare const throwErr: (message: string) => never; export declare const isPlainObject: (value: any) => boolean; export declare const ordinalSuffixOf: (num: number) => string; ================================================ FILE: typings/sweetalert.d.ts ================================================ import { SweetAlert } from "./core"; declare global { const sweetAlert: SweetAlert; } declare const swal: SweetAlert; export default swal; export as namespace swal; ================================================ FILE: webpack.config.js ================================================ const path = require('path'); const webpack = require('webpack'); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = (_env, args) => { const IS_PROD = args.p; const BUILD_PATH = 'dist'; const JS_FILE_NAME = 'sweetalert.min.js'; const devtool = IS_PROD ? false : 'source-map'; const plugins = [ new webpack.optimize.ModuleConcatenationPlugin(), //new BundleAnalyzerPlugin(), ]; if (IS_PROD) { plugins.push( new CopyWebpackPlugin([{ from: 'sweetalert.d.ts', to: '../typings/sweetalert.d.ts', }]) ); } return { context: path.resolve(__dirname, 'src'), entry: './sweetalert.js', plugins, output: { path: path.resolve(__dirname, BUILD_PATH), filename: JS_FILE_NAME, library: 'swal', libraryTarget: 'umd', }, resolve: { extensions: [ ".ts", ".js", ], }, module: { rules: [ { // Expose global swal() function test: require.resolve("./src/sweetalert"), use: [{ loader: 'expose-loader', options: 'sweetAlert' }, { loader: 'expose-loader', options: 'swal' }], }, { enforce: 'pre', test: /\.ts?$/, loader: 'tslint-loader', options: { configFile: './tslint.json', typeCheck: true, }, exclude: /(node_modules)/, }, { /* Compile TypeScript */ test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/, }, { /* Use PostCSS */ test: /\.css$/, use: [ { loader: 'style-loader', options: { insertAt: 'top', }, }, { loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader', ] } ], }, stats: { colors: true, }, devtool, } };