This commit is contained in:
David Štaleker
2024-02-23 12:56:54 +01:00
parent 18bac3de1c
commit 28d1630749
1388 changed files with 558637 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
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.

View File

@@ -0,0 +1,122 @@
# 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 <a href="https://rollupjs.org/">Rollup</a>,
<a href="https://parceljs.org/">Parcel</a>, or <a href="https://webpack.js.org/">Webpack</a> 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
<button class="ladda-button" data-style="expand-right">Submit</button>
```
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
<button class="ladda-button" data-style="expand-right">
<span class="ladda-label">Submit</span>
</button>
```
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
<https://github.com/hakimel/Ladda/blob/master/CHANGELOG.md>
## License
MIT

View File

@@ -0,0 +1,81 @@
/*!
* 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;
}
}

View File

@@ -0,0 +1,486 @@
/*!
* 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 diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,37 @@
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;

View File

@@ -0,0 +1,317 @@
/*!
* 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);
}

View File

@@ -0,0 +1,43 @@
{
"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 <hakim.elhattab@gmail.com> https://hakim.se",
"contributors": [
"Theodore Brown <theodorejb@outlook.com> 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"
}
}