Repository: hakimel/Ladda Branch: master Commit: c7a9c44c5e9d Files: 23 Total size: 57.6 KB Directory structure: gitextract_hiwuh9bh/ ├── .gitattributes ├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gruntfile.cjs ├── LICENSE ├── README.md ├── css/ │ ├── ladda-themed.scss │ └── ladda.scss ├── js/ │ ├── ladda.d.ts │ └── ladda.js ├── package.json ├── rollup.config.js └── site/ ├── bootstrap.html ├── bootstrap3.html ├── bootstrap4.html ├── demo.css ├── form.html ├── form.js ├── index.html ├── index.js └── rtl.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # so website files won't cause project language to be shown as HTML site/* linguist-documentation ================================================ FILE: .gitignore ================================================ /node_modules/ /dist/ /site/dist/ ================================================ FILE: .npmrc ================================================ package-lock=false ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [2.0.3] - 2021-12-02 ### Fixed - Deprecated slash as division when using Dart Sass (issue [#109]) ## [2.0.2] - 2021-09-01 ### Changed - Set package type to `module` to better support native ES module imports - Code cleanup and documentation improvements ## [2.0.1] - 2018-05-14 ### Added - Support for reassigning `$spinnerSize` Sass variable (PR [#81]) - Re-included Sass files in the npm package ## [2.0.0] - 2018-05-13 ### Added - Built-in TypeScript definitions - Support for the `style-src 'self';` Content Security Policy ### Changed - Rewritten as a native ES6 module using spin.js v4 ### Removed - Deprecated jQuery API - Deprecated `enable` and `disable` methods - Bower support - Support for Internet Explorer 9 (not supported by spin.js v4) ## [1.0.6] - 2018-02-04 ### Added - Support for forms with `novalidate` attribute (issue [#80]) ### Fixed - Protruding outline in Chrome on buttons with a progress bar ## [1.0.5] - 2017-09-24 ### Added - Validation for URL input fields in IE 9 ### Fixed - Detection of required HTML5 inputs in IE 9 - Bug where the `stop()` method would remove a button's disabled state even though Ladda was already stopped (issue [#68]) ## [1.0.4] - 2017-08-27 ### Fixed - Incorrect return value for jQuery `isLoading` argument (PR [#62], issue [#65]) Note that the Ladda jQuery API is deprecated - it is recommended to use the plain JavaScript API instead. ## [1.0.3] - 2017-08-27 ### Changed - Code cleanup and performance improvements ### Deprecated - Undocumented `enable` and `disable` instance methods ## [1.0.2] - 2017-08-27 ### Added - Support for right-to-left text direction (PR [#66]) ### Deprecated - jQuery API ## [1.0.1] - 2017-07-11 ### Fixed - Bug where focus outline protruded outside button in Chrome ### Removed - Unnecessary `toArray()` function ## [1.0.0] - 2016-03-08 ### Added - `data-spinner-lines` attribute for controlling the number of lines in the spinner (PR [#50]) ### Changed - The `ladda-button` class and `data-style` attribute are now automatically set if missing (PR [#52]) - `ladda-label` now wraps existing elements instead of reinserting them using `innerHTML` (PR [#55]) - `checkValidity` is now used to validate forms where supported (PR [#58]) ## [0.9.8] - 2015-04-05 ### Added - `main` field to package.json for easier CommonJS loading (PR [#47]) - Validity check for email inputs (PR [#48]) ### Changed - Updated spin.js to v2.0.2 (PR [#49]) ## [0.9.7] - 2015-01-17 ### Fixed - The Ladda theme is now baked into the output ladda.min.css file again. Was broken after upgrading grunt Sass in v0.9.5. ## [0.9.6] - 2015-01-16 ### Removed - Unintended references to non-existing sourcemap files that were introduced in v0.9.5 as part of the grunt Sass upgrade. ## [0.9.5] - 2015-01-16 ### Fixed - Validation of required select, radio, and checkbox inputs (PR [#43]) - The `ladda-spinner` element is no longer created if it already exists (PR [#44]) ## [0.9.4] - 2014-06-21 ### Added - `remove()` method to Ladda instances to stop memory leaks (PR [#36]) ### Changed - Updated from spin.js v1.3 to v2.0 (PR [#37]) ## [0.9.3] - 2014-04-16 ### Added - jQuery wrapper (PR [#33]) ### Fixed - Overlay effect in Safari - Missing CommonJS requirement ## [0.9.2] - 2013-12-03 ### Fixed - Spinner height is now calculated when spinning starts as opposed to when Ladda is initialized. This fixes spinner sizing issues with buttons that are initially hidden. ## [0.9.1] - 2013-11-27 ### Fixed - Issue where loading animations did not start for buttons outside of forms. This only affected use through the `Ladda.bind()` method. ## [0.9.0] - 2013-10-23 ### Changed - Ladda now confirms that all `required` fields in its parent form are filled out before starting the spinning animation. Note that this only applies to buttons bound using `Ladda.bind()`. ### Removed - jQuery dependencies ## [0.8.0] - 2013-09-05 ### Added - `data-spinner-color` attribute for configuring spinner color ### Changed - Disabled pointer events on spinner element ### Fixed - Array conversion bug which prevented binding Ladda to a selector - Default `z-index` value, was `initial` but should be `auto` ## [0.7.0] - 2013-07-19 ### Added - `startAfter(delay)` method ### Changed - Limit progress value to number between `0` and `1` ### Fixed - Issue that prevented forms from submitting - Error that caused IE 8 to blow up on page load (still not supported, though) ## [0.6.0] - 2013-07-07 ### Added - `data-spinner-size` attribute for setting explicit pixel size of spinner ### Changed - `ladda-label` wrapper is now automatically created if it doesn't exist ## [0.5.2] - 2013-06-27 ### Added - bower.json ### Fixed - Error when passing element to `Ladda.create()` ## [0.5.1] - 2013-06-19 ### Added - Bootstrap integration example ## [0.5.0] - 2013-06-19 ### Changed - Split visual and functional styles into separate stylesheets for framework compatibility (issue [#20]) - Spinner size is now automatically calculated using JavaScript ## [0.4.2] - 2013-06-14 ### Added - Size options (issue [#15]) - Mint color preset ### Changed - All settings are now applied via `data-*` attributes ## [0.4.1] - 2013-06-14 ### Added - `enable` and `disable` helper methods ## [0.4.0] - 2013-06-14 ### Added - Common.js / AMD module support - Grunt build file and minified files in /dist folder ### Changed - Migrated from plain CSS to Sass ## [0.3.0] - 2013-06-08 ### Added - Built-in progress bars which can be controlled using `setProgress` method ### Changed - Replaced spinner GIF with spin.js - `spinner` and `label` classes are now prefixed with `ladda-` ### Removed - Hardcoded timeout ## [0.2.0] - 2013-06-05 ### Added - Simple JavaScript API - Instructions in readme - MIT license ## [0.1.0] - 2013-06-05 - Initial release [Unreleased]: https://github.com/hakimel/Ladda/compare/2.0.3...HEAD [2.0.3]: https://github.com/hakimel/Ladda/compare/2.0.2...2.0.3 [2.0.2]: https://github.com/hakimel/Ladda/compare/2.0.1...2.0.2 [2.0.1]: https://github.com/hakimel/Ladda/compare/2.0.0...2.0.1 [2.0.0]: https://github.com/hakimel/Ladda/compare/1.0.6...2.0.0 [1.0.6]: https://github.com/hakimel/Ladda/compare/1.0.5...1.0.6 [1.0.5]: https://github.com/hakimel/Ladda/compare/1.0.4...1.0.5 [1.0.4]: https://github.com/hakimel/Ladda/compare/1.0.3...1.0.4 [1.0.3]: https://github.com/hakimel/Ladda/compare/1.0.2...1.0.3 [1.0.2]: https://github.com/hakimel/Ladda/compare/1.0.1...1.0.2 [1.0.1]: https://github.com/hakimel/Ladda/compare/1.0.0...1.0.1 [1.0.0]: https://github.com/hakimel/Ladda/compare/0.9.8...1.0.0 [0.9.8]: https://github.com/hakimel/Ladda/compare/0.9.7...0.9.8 [0.9.7]: https://github.com/hakimel/Ladda/compare/0.9.6...0.9.7 [0.9.6]: https://github.com/hakimel/Ladda/compare/0.9.5...0.9.6 [0.9.5]: https://github.com/hakimel/Ladda/compare/0.9.4...0.9.5 [0.9.4]: https://github.com/hakimel/Ladda/compare/0.9.3...0.9.4 [0.9.3]: https://github.com/hakimel/Ladda/compare/0.9.2...0.9.3 [0.9.2]: https://github.com/hakimel/Ladda/compare/0.9.1...0.9.2 [0.9.1]: https://github.com/hakimel/Ladda/compare/0.9.0...0.9.1 [0.9.0]: https://github.com/hakimel/Ladda/compare/0.8.0...0.9.0 [0.8.0]: https://github.com/hakimel/Ladda/compare/0.7.0...0.8.0 [0.7.0]: https://github.com/hakimel/Ladda/compare/0.6.0...0.7.0 [0.6.0]: https://github.com/hakimel/Ladda/compare/0.5.2...0.6.0 [0.5.2]: https://github.com/hakimel/Ladda/compare/0.5.1...0.5.2 [0.5.1]: https://github.com/hakimel/Ladda/compare/0.5.0...0.5.1 [0.5.0]: https://github.com/hakimel/Ladda/compare/0.4.2...0.5.0 [0.4.2]: https://github.com/hakimel/Ladda/compare/0.4.1...0.4.2 [0.4.1]: https://github.com/hakimel/Ladda/compare/0.4.0...0.4.1 [0.4.0]: https://github.com/hakimel/Ladda/compare/0.3.0...0.4.0 [0.3.0]: https://github.com/hakimel/Ladda/compare/0.2.0...0.3.0 [0.2.0]: https://github.com/hakimel/Ladda/compare/0.1.0...0.2.0 [0.1.0]: https://github.com/hakimel/Ladda/tree/0.1.0 [#109]: https://github.com/hakimel/Ladda/issues/109 [#81]: https://github.com/hakimel/Ladda/pull/81 [#80]: https://github.com/hakimel/Ladda/issues/80 [#68]: https://github.com/hakimel/Ladda/issues/68 [#66]: https://github.com/hakimel/Ladda/pull/66 [#65]: https://github.com/hakimel/Ladda/issues/65 [#62]: https://github.com/hakimel/Ladda/pull/62 [#58]: https://github.com/hakimel/Ladda/pull/58 [#55]: https://github.com/hakimel/Ladda/pull/55 [#52]: https://github.com/hakimel/Ladda/pull/52 [#50]: https://github.com/hakimel/Ladda/pull/50 [#49]: https://github.com/hakimel/Ladda/pull/49 [#48]: https://github.com/hakimel/Ladda/pull/48 [#47]: https://github.com/hakimel/Ladda/pull/47 [#44]: https://github.com/hakimel/Ladda/pull/44 [#43]: https://github.com/hakimel/Ladda/pull/43 [#37]: https://github.com/hakimel/Ladda/pull/37 [#36]: https://github.com/hakimel/Ladda/pull/36 [#33]: https://github.com/hakimel/Ladda/pull/33 [#20]: https://github.com/hakimel/Ladda/issues/20 [#15]: https://github.com/hakimel/Ladda/issues/15 ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing Clone the repository and run `npm install` to install development dependencies and compile the project. Then run `npm start` to watch files for changes and start a static file server for viewing the test site. Open http://localhost:8000/ in your browser to view the site. ================================================ FILE: Gruntfile.cjs ================================================ const sass = require('sass'); module.exports = function (grunt) { 'use strict'; // Project configuration grunt.initConfig({ sass: { options: { implementation: sass, outputStyle: 'compressed', }, dist: { files: { 'dist/ladda.min.css': 'css/ladda-themed.scss', 'dist/ladda-themeless.min.css': 'css/ladda.scss' } } }, jshint: { options: { // enforcing esversion: 6, curly: true, eqeqeq: true, freeze: true, futurehostile: true, globals: { module: false, console: false, define: false }, latedef: "nofunc", maxparams: 3, noarg: true, nocomma: true, nonbsp: true, nonew: true, undef: true, unused: true, // environments browser: true, node: true, }, files: [ 'Gruntfile.cjs', 'js/ladda.js' ] }, connect: { server: { options: { port: 8000, base: 'site', } } }, copy: { css: { files: [ { src: ['dist/ladda*.min.css'], dest: 'site/' } ] } }, watch: { main: { files: [ 'Gruntfile.js', 'js/ladda.js' ], tasks: 'js', options: { livereload: true, }, }, theme: { files: [ 'css/*.scss' ], tasks: 'css', options: { livereload: true, }, } } }); // Dependencies grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-sass'); grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-contrib-copy'); // Default task grunt.registerTask('default', ['jshint', 'css']); // Theme task grunt.registerTask('css', ['sass', 'copy']); // Serve presentation locally grunt.registerTask('serve', ['connect', 'watch']); }; ================================================ FILE: LICENSE ================================================ Copyright (C) 2017 Hakim El Hattab, http://hakim.se 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 ================================================ # Ladda Buttons with built-in loading indicators, effectively bridging the gap between action and feedback. [Check out the demo page](https://lab.hakim.se/ladda/). ## Installation `npm install ladda` ### Module bundling Ladda 2.x is distributed as a standard ES6 module. Since not all browsers/environments support native ES6 modules, it is recommended to use a bundler such as Rollup, Parcel, or Webpack to create a production-ready code bundle. ## Usage ### CSS You will need to include ONE of the two style sheets in the **/dist** directory. If you want the button styles used on the demo page, use the **ladda.min.css** file. If you want to have the functional buttons without the visual style (colors, padding, etc.), use the **ladda-themeless.min.css** file. ### HTML Below is an example of a button using the `expand-right` animation style. ```html ``` When the JS code runs to bind Ladda to the button, the `ladda-button` class will be automatically added if it doesn't already exist. Additionally, a span with class `ladda-label` will automatically wrap the button text, resulting in the following DOM structure: ```html ``` Buttons accept the following attributes: - **data-style**: one of the button styles *[required]* - expand-left, expand-right, expand-up, expand-down - contract, contract-overlay - zoom-in, zoom-out - slide-left, slide-right, slide-up, slide-down - **data-color**: green/red/blue/purple/mint - **data-size**: xs/s/l/xl, defaults to medium - **data-spinner-size**: pixel dimensions of spinner, defaults to dynamic size based on the button height - **data-spinner-color**: a hex code or any named CSS color, defaults to `#fff` - **data-spinner-lines**: the number of lines for the spinner, defaults to `12` ### JavaScript Start by importing the Ladda module: ```javascript import * as Ladda from 'ladda'; ``` The following approach is recommended for JavaScript control over your buttons: ```javascript // Create a new instance of ladda for the specified button var l = Ladda.create(document.querySelector('.my-button')); // Start loading l.start(); // Will display a progress bar for 50% of the button width l.setProgress(0.5); // Stop loading l.stop(); // Toggle between loading/not loading states l.toggle(); // Check the current state l.isLoading(); // Delete the button's ladda instance l.remove(); ``` To show the loading animation for a form that is submitted to the server (always resulting in a page reload) the `bind()` method can be used: ```javascript // Automatically trigger the loading animation on click Ladda.bind('button[type=submit]'); // Same as the above but automatically stops after two seconds Ladda.bind('button[type=submit]', {timeout: 2000}); ``` Note: when using the `bind()` method on buttons that are inside a form, loading indicators will not be shown until the form is valid. All loading animations on the page can be stopped by using: ```javascript Ladda.stopAll(); ``` ## Browser support Ladda has been tested in Firefox, Microsoft Edge, Chrome, and Internet Explorer 11. It also Should Work™ in Safari and Internet Explorer 10. ## Changelog ## License MIT ================================================ FILE: css/ladda-themed.scss ================================================ /*! * Ladda including the default theme. */ @import "ladda"; /************************************* * CONFIG */ $green: #2aca76; $blue: #53b5e6; $red: #ea8557; $purple: #9973C2; $mint: #16a085; /************************************* * BUTTON THEME */ .ladda-button { background: #666; border: 0; padding: 14px 18px; font-size: 18px; cursor: pointer; color: #fff; border-radius: 2px; border: 1px solid transparent; -webkit-appearance: none; -webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); &:hover { border-color: rgba( 0, 0, 0, 0.07 ); background-color: #888; } @include buttonColor( 'green', $green ); @include buttonColor( 'blue', $blue ); @include buttonColor( 'red', $red ); @include buttonColor( 'purple', $purple ); @include buttonColor( 'mint', $mint ); &[disabled], &[data-loading] { border-color: rgba( 0, 0, 0, 0.07 ); &, &:hover { cursor: default; background-color: #999; } } &[data-size=xs] { padding: 4px 8px; .ladda-label { font-size: 0.7em; } } &[data-size=s] { padding: 6px 10px; .ladda-label { font-size: 0.9em; } } &[data-size=l] .ladda-label { font-size: 1.2em; } &[data-size=xl] .ladda-label { font-size: 1.5em; } } ================================================ FILE: css/ladda.scss ================================================ /*! * Ladda * http://lab.hakim.se/ladda * MIT licensed * * Copyright (C) 2018 Hakim El Hattab, http://hakim.se */ /************************************* * CONFIG */ @use "sass:math"; $spinnerSize: 32px !default; /************************************* * MIXINS */ @mixin buttonColor( $name, $color ) { &[data-color=#{$name}] { background: $color; &:hover { background-color: lighten( $color, 5% ); } } } /************************************* * Opacity animation for spin.js */ @keyframes ladda-spinner-line-fade { 0%, 100% { opacity: 0.22; /* minimum opacity */ } 1% { opacity: 1; } } /************************************* * BUTTON BASE */ .ladda-button { position: relative; } /* Spinner animation */ .ladda-button .ladda-spinner { position: absolute; z-index: 2; display: inline-block; width: $spinnerSize; top: 50%; margin-top: 0; opacity: 0; pointer-events: none; } /* Button label */ .ladda-button .ladda-label { position: relative; z-index: 3; } /* Progress bar */ .ladda-button .ladda-progress { position: absolute; width: 0; height: 100%; left: 0; top: 0; background: rgba( 0, 0, 0, 0.2 ); display: none; transition: 0.1s linear all; } .ladda-button[data-loading] .ladda-progress { display: block; } /************************************* * EASING */ .ladda-button, .ladda-button .ladda-spinner, .ladda-button .ladda-label { transition: 0.3s cubic-bezier(0.175, 0.885, 0.320, 1.275) all; } .ladda-button[data-style=zoom-in], .ladda-button[data-style=zoom-in] .ladda-spinner, .ladda-button[data-style=zoom-in] .ladda-label, .ladda-button[data-style=zoom-out], .ladda-button[data-style=zoom-out] .ladda-spinner, .ladda-button[data-style=zoom-out] .ladda-label { transition: 0.3s ease all; } /************************************* * EXPAND RIGHT */ .ladda-button[data-style=expand-right] { .ladda-spinner { right: math.div($spinnerSize, -2) + 10; } &[data-size="s"] .ladda-spinner, &[data-size="xs"] .ladda-spinner { right: math.div($spinnerSize, -2) + 4; } &[data-loading] { padding-right: 56px; .ladda-spinner { opacity: 1; } &[data-size="s"], &[data-size="xs"] { padding-right: 40px; } } } /************************************* * EXPAND LEFT */ .ladda-button[data-style=expand-left] { .ladda-spinner { left: $spinnerSize * 0.5 + 10; } &[data-size="s"] .ladda-spinner, &[data-size="xs"] .ladda-spinner { left: 4px; } &[data-loading] { padding-left: 56px; .ladda-spinner { opacity: 1; } &[data-size="s"], &[data-size="xs"] { padding-left: 40px; } } } /************************************* * EXPAND UP */ .ladda-button[data-style=expand-up] { overflow: hidden; .ladda-spinner { top: -$spinnerSize; left: 50%; margin-left: 0; } &[data-loading] { padding-top: 54px; .ladda-spinner { opacity: 1; top: ($spinnerSize * 0.5) + 10; margin-top: 0; } &[data-size="s"], &[data-size="xs"] { padding-top: 32px; .ladda-spinner { top: 4px; } } } } /************************************* * EXPAND DOWN */ .ladda-button[data-style=expand-down] { overflow: hidden; .ladda-spinner { top: 62px; left: 50%; margin-left: 0; } &[data-size="s"] .ladda-spinner, &[data-size="xs"] .ladda-spinner { top: 40px; } &[data-loading] { padding-bottom: 54px; .ladda-spinner { opacity: 1; } &[data-size="s"], &[data-size="xs"] { padding-bottom: 32px; } } } /************************************* * SLIDE LEFT */ .ladda-button[data-style=slide-left] { overflow: hidden; .ladda-label { position: relative; } .ladda-spinner { left: 100%; margin-left: 0; } &[data-loading] { .ladda-label { opacity: 0; left: -100%; } .ladda-spinner { opacity: 1; left: 50%; } } } /************************************* * SLIDE RIGHT */ .ladda-button[data-style=slide-right] { overflow: hidden; .ladda-label { position: relative; } .ladda-spinner { right: 100%; margin-left: 0; left: $spinnerSize * 0.5; [dir="rtl"] & { right: auto; } } &[data-loading] { .ladda-label { opacity: 0; left: 100%; } .ladda-spinner { opacity: 1; left: 50%; } } } /************************************* * SLIDE UP */ .ladda-button[data-style=slide-up] { overflow: hidden; .ladda-label { position: relative; } .ladda-spinner { left: 50%; margin-left: 0; margin-top: 1em; } &[data-loading] { .ladda-label { opacity: 0; top: -1em; } .ladda-spinner { opacity: 1; margin-top: 0; } } } /************************************* * SLIDE DOWN */ .ladda-button[data-style=slide-down] { overflow: hidden; .ladda-label { position: relative; } .ladda-spinner { left: 50%; margin-left: 0; margin-top: -2em; } &[data-loading] { .ladda-label { opacity: 0; top: 1em; } .ladda-spinner { opacity: 1; margin-top: 0; } } } /************************************* * ZOOM-OUT */ .ladda-button[data-style=zoom-out] { overflow: hidden; .ladda-label { position: relative; display: inline-block; } .ladda-spinner { left: 50%; margin-left: $spinnerSize; transform: scale(2.5); } &[data-loading] { .ladda-label { opacity: 0; transform: scale(0.5); } .ladda-spinner { opacity: 1; margin-left: 0; transform: none; } } } /************************************* * ZOOM-IN */ .ladda-button[data-style=zoom-in] { overflow: hidden; .ladda-label { position: relative; display: inline-block; } .ladda-spinner { left: 50%; margin-left: math.div($spinnerSize, -2); transform: scale(0.2); } &[data-loading] { .ladda-label { opacity: 0; transform: scale(2.2); } .ladda-spinner { opacity: 1; margin-left: 0; transform: none; } } } /************************************* * CONTRACT */ .ladda-button[data-style=contract] { overflow: hidden; width: 100px; .ladda-spinner { left: 50%; margin-left: 0; } &[data-loading] { border-radius: 50%; width: 52px; .ladda-label { opacity: 0; } .ladda-spinner { opacity: 1; } } } /************************************* * OVERLAY */ .ladda-button[data-style=contract-overlay] { overflow: hidden; width: 100px; box-shadow: 0px 0px 0px 2000px rgba(0,0,0,0); .ladda-spinner { left: 50%; margin-left: 0; } &[data-loading] { border-radius: 50%; width: 52px; box-shadow: 0px 0px 0px 2000px rgba(0,0,0,0.8); .ladda-label { opacity: 0; } .ladda-spinner { opacity: 1; } } } [dir="rtl"] .ladda-spinner > div { left: 25% !important; } ================================================ FILE: js/ladda.d.ts ================================================ export interface LaddaButton { start(): LaddaButton, startAfter(delay: number): LaddaButton, stop(): LaddaButton, toggle(): LaddaButton, setProgress(progress: number): void, isLoading(): boolean, remove(): void, } export interface BindOptions { /** * Number of milliseconds to wait before automatically cancelling the animation. */ timeout?: number, /** * A function to be called with the Ladda instance when a target button is clicked. */ callback?: (instance: LaddaButton) => void, } /** * Creates a new instance of Ladda which wraps the target button element. */ export function create(button: HTMLButtonElement): LaddaButton; /** * Binds the target buttons to automatically enter the loading state when clicked. * @param target Either an HTML element or a CSS selector. */ export function bind(target: HTMLElement | string, options?: BindOptions): void; /** * Stops all current loading animations. */ export function stopAll(): void; ================================================ FILE: js/ladda.js ================================================ /*! * Ladda * http://lab.hakim.se/ladda * MIT licensed * * Copyright (C) 2018 Hakim El Hattab, http://hakim.se */ import {Spinner} from 'spin.js'; // All currently instantiated instances of Ladda var ALL_INSTANCES = []; /** * Creates a new instance of Ladda which wraps the * target button element. * * @return An API object that can be used to control * the loading animation state. */ export function create(button) { if (typeof button === 'undefined') { console.warn("Ladda button target must be defined."); return; } // The button must have the class "ladda-button" if (!button.classList.contains('ladda-button')) { button.classList.add('ladda-button'); } // Style is required, default to "expand-right" if (!button.hasAttribute('data-style')) { button.setAttribute('data-style', 'expand-right'); } // The text contents must be wrapped in a ladda-label // element, create one if it doesn't already exist if (!button.querySelector('.ladda-label')) { var laddaLabel = document.createElement('span'); laddaLabel.className = 'ladda-label'; wrapContent(button, laddaLabel); } // The spinner component var spinnerWrapper = button.querySelector('.ladda-spinner'); // Wrapper element for the spinner if (!spinnerWrapper) { spinnerWrapper = document.createElement('span'); spinnerWrapper.className = 'ladda-spinner'; } button.appendChild(spinnerWrapper); // Timer used to delay starting/stopping var timer; var spinner; var instance = { /** * Enter the loading state. */ start: function() { // Create the spinner if it doesn't already exist if (!spinner) { spinner = createSpinner(button); } button.disabled = true; button.setAttribute('data-loading', ''); clearTimeout(timer); spinner.spin(spinnerWrapper); this.setProgress(0); return this; // chain }, /** * Enter the loading state, after a delay. */ startAfter: function(delay) { clearTimeout(timer); timer = setTimeout(function() { instance.start(); }, delay); return this; // chain }, /** * Exit the loading state. */ stop: function() { if (instance.isLoading()) { button.disabled = false; button.removeAttribute('data-loading'); } // Kill the animation after a delay to make sure it // runs for the duration of the button transition clearTimeout(timer); if (spinner) { timer = setTimeout(function() { spinner.stop(); }, 1000); } return this; // chain }, /** * Toggle the loading state on/off. */ toggle: function() { return this.isLoading() ? this.stop() : this.start(); }, /** * Sets the width of the visual progress bar inside of * this Ladda button * * @param {number} progress in the range of 0-1 */ setProgress: function(progress) { // Cap it progress = Math.max(Math.min(progress, 1), 0); var progressElement = button.querySelector('.ladda-progress'); // Remove the progress bar if we're at 0 progress if (progress === 0 && progressElement && progressElement.parentNode) { progressElement.parentNode.removeChild(progressElement); } else { if (!progressElement) { progressElement = document.createElement('div'); progressElement.className = 'ladda-progress'; button.appendChild(progressElement); } progressElement.style.width = ((progress || 0) * button.offsetWidth) + 'px'; } }, isLoading: function() { return button.hasAttribute('data-loading'); }, remove: function() { clearTimeout(timer); button.disabled = false; button.removeAttribute('data-loading'); if (spinner) { spinner.stop(); spinner = null; } ALL_INSTANCES.splice(ALL_INSTANCES.indexOf(instance), 1); } }; ALL_INSTANCES.push(instance); return instance; } /** * Binds the target buttons to automatically enter the * loading state when clicked. * * @param target Either an HTML element or a CSS selector. * @param options * - timeout Number of milliseconds to wait before * automatically cancelling the animation. * - callback A function to be called with the Ladda * instance when a target button is clicked. */ export function bind(target, options) { var targets; if (typeof target === 'string') { targets = document.querySelectorAll(target); } else if (typeof target === 'object') { targets = [target]; } else { throw new Error('target must be string or object'); } options = options || {}; for (var i = 0; i < targets.length; i++) { bindElement(targets[i], options); } } /** * Stops ALL current loading animations. */ export function stopAll() { for (var i = 0, len = ALL_INSTANCES.length; i < len; i++) { ALL_INSTANCES[i].stop(); } } /** * Get the first ancestor node from an element, having a * certain type. * * @param elem An HTML element * @param type an HTML tag type (uppercased) * * @return An HTML element */ function getAncestorOfTagType(elem, type) { while (elem.parentNode && elem.tagName !== type) { elem = elem.parentNode; } return (type === elem.tagName) ? elem : undefined; } function createSpinner(button) { var height = button.offsetHeight, spinnerColor, spinnerLines; if (height === 0) { // We may have an element that is not visible so // we attempt to get the height in a different way height = parseFloat(window.getComputedStyle(button).height); } // If the button is tall we can afford some padding if (height > 32) { height *= 0.8; } // Prefer an explicit height if one is defined if (button.hasAttribute('data-spinner-size')) { height = parseInt(button.getAttribute('data-spinner-size'), 10); } // Allow buttons to specify the color of the spinner element if (button.hasAttribute('data-spinner-color')) { spinnerColor = button.getAttribute('data-spinner-color'); } // Allow buttons to specify the number of lines of the spinner if (button.hasAttribute('data-spinner-lines')) { spinnerLines = parseInt(button.getAttribute('data-spinner-lines'), 10); } var radius = height * 0.2, length = radius * 0.6, width = radius < 7 ? 2 : 3; return new Spinner({ color: spinnerColor || '#fff', lines: spinnerLines || 12, radius: radius, length: length, width: width, animation: 'ladda-spinner-line-fade', zIndex: 'auto', top: 'auto', left: 'auto', className: '' }); } function wrapContent(node, wrapper) { var r = document.createRange(); r.selectNodeContents(node); r.surroundContents(wrapper); node.appendChild(wrapper); } function bindElement(element, options) { if (typeof element.addEventListener !== 'function') { return; } var instance = create(element); var timeout = -1; element.addEventListener('click', function() { // If the button belongs to a form, make sure all the // fields in that form are filled out var valid = true; var form = getAncestorOfTagType(element, 'FORM'); if (typeof form !== 'undefined' && !form.hasAttribute('novalidate')) { // Modern form validation if (typeof form.checkValidity === 'function') { valid = form.checkValidity(); } } if (valid) { // This is asynchronous to avoid an issue where disabling // the button prevents forms from submitting instance.startAfter(1); // Set a loading timeout if one is specified if (typeof options.timeout === 'number') { clearTimeout(timeout); timeout = setTimeout(instance.stop, options.timeout); } // Invoke callbacks if (typeof options.callback === 'function') { options.callback.apply(null, [instance]); } } }, false); } ================================================ FILE: package.json ================================================ { "name": "ladda", "version": "2.0.3", "description": "Buttons with built-in loading indicators", "homepage": "https://lab.hakim.se/ladda/", "files": [ "css/*.scss", "dist/*.css", "js/ladda.d.ts", "js/ladda.js" ], "type": "module", "main": "./js/ladda.js", "types": "./js/ladda.d.ts", "scripts": { "test": "grunt jshint --gruntfile Gruntfile.cjs", "prepare": "grunt --gruntfile Gruntfile.cjs && rollup -c", "start": "grunt serve --gruntfile Gruntfile.cjs" }, "author": "Hakim El Hattab https://hakim.se", "contributors": [ "Theodore Brown https://theodorejb.me" ], "repository": { "type": "git", "url": "git://github.com/hakimel/Ladda.git" }, "devDependencies": { "grunt": "^1.4.1", "grunt-contrib-connect": "^3.0.0", "grunt-contrib-copy": "^1.0.0", "grunt-contrib-jshint": "^3.1.1", "grunt-contrib-watch": "^1.1.0", "grunt-sass": "^3.1.0", "rollup": "^2.60.2", "rollup-plugin-node-resolve": "^5.2.0", "sass": "^1.44.0" }, "license": "MIT", "dependencies": { "spin.js": "^4.1.1" } } ================================================ FILE: rollup.config.js ================================================ import resolve from 'rollup-plugin-node-resolve'; export default [ { input: 'site/index.js', output: { file: 'site/dist/index-bundle.js', format: 'iife' }, plugins: [resolve()] }, { input: 'site/form.js', output: { file: 'site/dist/form-bundle.js', format: 'iife' }, plugins: [resolve()] }, ]; ================================================ FILE: site/bootstrap.html ================================================ Ladda: Bootstrap compatibility test

Ladda Bootstrap 5 test

expand-left

expand-right

expand-up

expand-down

zoom-in

zoom-out

slide-left

slide-right

slide-up

slide-down

Built-in progress bar

expand-right

contract

Sizes

Small

Large

Buttons in forms containing required inputs

================================================ FILE: site/bootstrap3.html ================================================ Ladda: Bootstrap compatibility test

Ladda Bootstrap 3 test

expand-left

expand-right

expand-up

expand-down

zoom-in

zoom-out

slide-left

slide-right

slide-up

slide-down

Built-in progress bar

expand-right

contract

Sizes

Small

Large

Buttons in forms containing required inputs

================================================ FILE: site/bootstrap4.html ================================================ Ladda: Bootstrap compatibility test

Ladda Bootstrap 4 test

expand-left

expand-right

expand-up

expand-down

zoom-in

zoom-out

slide-left

slide-right

slide-up

slide-down

Built-in progress bar

expand-right

contract

Sizes

Small

Large

Buttons in forms containing required inputs

================================================ FILE: site/demo.css ================================================ /* Styles used specifically for the demo page */ body { background: #f5f5f5; font-family: monospace; font-size: 14px; color: #333333; } button { outline: 0; } .examples { max-width: 670px; margin: 2em auto; padding: 4em; background: #fff; text-align: center; } .examples .intro { margin-bottom: 3em; line-height: 1.4em; font-size: 16px; text-align: left; } .examples .intro h1 { margin-top: 0; font-size: 18px; font-weight: bold; } .examples h3 { font-size: 17px; } .examples .outro { display: block; text-align: right; margin-top: 3em; } .examples section { display: inline-block; width: 24%; min-width: 160px; margin-bottom: 2em; text-align: center; vertical-align: top; } .examples section h3 { color: #bbb; font-weight: normal; } .sharing { float: left; } .fork { position: absolute; top: 0; right: 0; border: none; } ================================================ FILE: site/form.html ================================================ Ladda: Form test

Ladda Form Validation

This page can be used to test that Ladda does NOT start spinning when submitting an invalid form.

================================================ FILE: site/form.js ================================================ import * as Ladda from '../js/ladda.js'; Ladda.bind('button[type=submit]'); ================================================ FILE: site/index.html ================================================ Ladda

Ladda

A UI concept which merges loading indicators into the action that invoked them. Primarily intended for use with forms where it gives users immediate feedback upon submit rather than leaving them wondering while the browser does its thing. For a real-world example, check out any of the forms on slides.com.

expand-left

expand-right

expand-up

expand-down

contract

contract-overlay

zoom-in

zoom-out

slide-left

slide-right

slide-up

slide-down

Built-in progress bar

expand-right

contract

Sizes

Extra Small

Small

Large

Extra Large

Fork me on GitHub ================================================ FILE: site/index.js ================================================ import * as Ladda from '../js/ladda.js'; // Bind normal buttons Ladda.bind('.button-demo button', { timeout: 2000 }); // Bind progress buttons and simulate loading progress Ladda.bind('.progress-demo button', { callback: function (instance) { var progress = 0; var interval = setInterval(function () { progress = Math.min(progress + Math.random() * 0.1, 1); instance.setProgress(progress); if (progress === 1) { instance.stop(); clearInterval(interval); } }, 200); } }); // You can control loading explicitly using the JavaScript API // as outlined below: // var l = Ladda.create( document.querySelector( 'button' ) ); // l.start(); // l.stop(); // l.toggle(); // l.isLoading(); // l.setProgress( 0-1 ); ================================================ FILE: site/rtl.html ================================================ Ladda

Ladda RTL support

expand-left

expand-right

expand-up

expand-down

contract

contract-overlay

zoom-in

zoom-out

slide-left

slide-right

slide-up

slide-down

Built-in progress bar

expand-right

contract

Sizes

Extra Small

Small

Large

Extra Large