未验证 提交 ef63dfd7 编写于 作者: O owen-m1 提交者: GitHub

Next version (#1524)

上级 b321482d
......@@ -4,4 +4,3 @@ mock.png
.build*
jquery.fn.*
.idea/
package-lock.json
{
"strict": true,
"strict": false,
"newcap": false,
"node": true,
"expr": true,
"supernew": true,
"laxbreak": true,
"esversion": 9,
"white": true,
"globals": {
"define": true,
......
# Sortable
# Sortable   [![DeepScan grade](https://deepscan.io/api/teams/3901/projects/5666/branches/43977/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=3901&pid=5666&bid=43977) [![](https://data.jsdelivr.com/v1/package/npm/sortablejs/badge)](https://www.jsdelivr.com/package/npm/sortablejs) [![npm](https://img.shields.io/npm/v/sortablejs.svg)](https://www.npmjs.com/package/sortablejs)
Sortable is a JavaScript library for reorderable drag-and-drop lists.
Demo: http://sortablejs.github.io/Sortable/
......@@ -13,6 +14,8 @@ Supported by [<img width="100px" src="https://user-images.githubusercontent.com/
* Supports drag handles *and selectable text* (better than voidberg's html5sortable)
* Smart auto-scrolling
* Advanced swap detection
* Smooth animations
* [Multi-drag](https://github.com/SortableJS/Sortable/wiki/Dragging-Multiple-Items-in-Sortable) support
* Built using native HTML5 drag and drop API
* Supports
* [Meteor](https://github.com/SortableJS/meteor-sortablejs)
......@@ -37,27 +40,54 @@ Supported by [<img width="100px" src="https://user-images.githubusercontent.com/
### Articles
* [Dragging Multiple Items in Sortable](https://github.com/SortableJS/Sortable/wiki/Dragging-Multiple-Items-in-Sortable) (April 26, 2019)
* [Swap Thresholds and Direction](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction) (December 2, 2018)
* [Sortable v1.0 — New capabilities](https://github.com/SortableJS/Sortable/wiki/Sortable-v1.0-—-New-capabilities/) (December 22, 2014)
* [Sorting with the help of HTML5 Drag'n'Drop API](https://github.com/SortableJS/Sortable/wiki/Sorting-with-the-help-of-HTML5-Drag'n'Drop-API/) (December 23, 2013)
<br/>
### Install
Via npm
### Getting Started
Install with NPM:
```bash
$ npm install sortablejs --save
```
Via bower:
Install with Bower:
```bash
$ bower install --save sortablejs
```
<br/>
Import into your project:
```js
// Default SortableJS
import Sortable from 'sortablejs';
// Core SortableJS (without default plugins)
import Sortable from 'sortablejs/modular/sortable.core.esm.js';
// Complete SortableJS (with all plugins)
import Sortable from 'sortablejs/modular/sortable.complete.esm.js';
```
Cherrypick plugins:
```js
// Cherrypick extra plugins
import Sortable, { MultiDrag, Swap } from 'sortablejs';
Sortable.mount(MultiDrag, Swap);
// Cherrypick default plugins
import Sortable, { AutoScroll } from 'sortablejs/modular/sortable.core.esm.js';
Sortable.mount(AutoScroll);
```
---
### Usage
```html
......@@ -95,10 +125,12 @@ var sortable = new Sortable(el, {
filter: ".ignore-elements", // Selectors that do not lead to dragging (String or Function)
preventOnFilter: true, // Call `event.preventDefault()` when triggered `filter`
draggable: ".item", // Specifies which items inside the element should be draggable
dataIdAttr: 'data-id',
ghostClass: "sortable-ghost", // Class name for the drop placeholder
chosenClass: "sortable-chosen", // Class name for the chosen item
dragClass: "sortable-drag", // Class name for the dragging item
dataIdAttr: 'data-id',
swapThreshold: 1, // Threshold of the swap zone
invertSwap: false, // Will always use inverted swap zone if set to true
......@@ -111,12 +143,6 @@ var sortable = new Sortable(el, {
fallbackOnBody: false, // Appends the cloned DOM Element into the Document's Body
fallbackTolerance: 0, // Specify in pixels how far the mouse should move before it's considered as a drag.
scroll: true, // or HTMLElement
scrollFn: function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { ... }, // if you have custom scrollbar scrollFn may be used for autoscrolling
scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling.
scrollSpeed: 10, // px
bubbleScroll: true, // apply autoscroll to all parent elements, allowing for easier movement
dragoverBubble: false,
removeCloneOnHide: true, // Remove the clone element when it is not showing, rather than just hiding it
emptyInsertThreshold: 5, // px, distance mouse must be from empty sortable to insert drag element into it
......@@ -231,7 +257,7 @@ Demo:
#### `sort` option
Sorting inside list.
Allow sorting inside list.
Demo: https://jsbin.com/jayedig/edit?js,output
......@@ -257,9 +283,9 @@ Whether or not the delay should be applied only if the user is using touch (eg.
#### `swapThreshold` option
Percentage of the target that the swap zone will take up, as a float between `0` and `1`.
Percentage of the target that the swap zone will take up, as a float between `0` and `1`. This option has nothing to do with the `swap` option.
Read more: https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#swap-threshold
[Read more](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#swap-threshold)
Demo: http://sortablejs.github.io/Sortable#thresholds
......@@ -268,9 +294,9 @@ Demo: http://sortablejs.github.io/Sortable#thresholds
#### `invertSwap` option
Set to `true` to set the swap zone to the sides of the target, for the effect of sorting "in between" items.
Set to `true` to set the swap zone to the sides of the target, for the effect of sorting "in between" items. This option has nothing to do with the `swap` option.
Read more: https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#forcing-inverted-swap-zone
[Read more](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#forcing-inverted-swap-zone)
Demo: http://sortablejs.github.io/Sortable#thresholds
......@@ -279,9 +305,9 @@ Demo: http://sortablejs.github.io/Sortable#thresholds
#### `invertedSwapThreshold` option
Percentage of the target that the inverted swap zone will take up, as a float between `0` and `1`. If not given, will default to `swapThreshold`.
Percentage of the target that the inverted swap zone will take up, as a float between `0` and `1`. If not given, will default to `swapThreshold`. This option has nothing to do with the `swap` option.
Read more: https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#dealing-with-swap-glitching
[Read more](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#dealing-with-swap-glitching)
---
......@@ -290,7 +316,7 @@ Read more: https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direc
#### `direction` option
Direction that the Sortable should sort in. Can be set to `'vertical'`, `'horizontal'`, or a function, which will be called whenever a target is dragged over. Must return `'vertical'` or `'horizontal'`.
Read more: https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#direction
[Read more](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#direction)
Example of direction detection for vertical list that includes full column and half column elements:
......@@ -467,48 +493,6 @@ Dragging only starts if you move the pointer past a certain tolerance, so that y
---
#### `scroll` option
If set to `true`, the page (or sortable-area) scrolls when coming to an edge.
Demo:
- `window`: https://jsbin.com/dosilir/edit?js,output
- `overflow: hidden`: https://jsbin.com/xecihez/edit?html,js,output
---
#### `scrollFn` option
Defines function that will be used for autoscrolling. el.scrollTop/el.scrollLeft is used by default.
Useful when you have custom scrollbar with dedicated scroll function.
---
#### `scrollSensitivity` option
Defines how near the mouse must be to an edge to start scrolling.
---
#### `scrollSpeed` option
The speed at which the window should scroll once the mouse pointer gets within the `scrollSensitivity` distance.
---
#### `bubbleScroll` option
If set to `true`, the normal `autoscroll` function will also be applied to all parent elements of the element the user is dragging over.
Demo: https://jsbin.com/kesewor/edit?html,js,output
---
#### `dragoverBubble` option
If set to `true`, the dragover event will bubble to parent sortables. Works on both fallback and native dragover event.
By default, it is false, but Sortable will only stop bubbling the event once the element has been inserted into a parent Sortable, or *can* be inserted into a parent Sortable, but isn't at that specific time (due to animation, etc).
......@@ -682,7 +666,35 @@ Create new instance.
##### Sortable.active:`Sortable`
Link to the active instance.
The active Sortable instance.
---
##### Sortable.dragged:`HTMLElement`
The element being dragged.
---
##### Sortable.ghost:`HTMLElement`
The ghost element.
---
##### Sortable.clone:`HTMLElement`
The clone element.
---
##### Sortable.mount(plugin:`...SortablePlugin|...SortablePlugin[]`)
Mounts a plugin to Sortable.
---
......
此差异已折叠。
此差异已折叠。
module.exports = function(api) {
api.cache(true);
let presets;
if (process.env.NODE_ENV === 'es') {
presets = [
[
"@babel/preset-env",
{
"modules": false
}
]
];
} else if (process.env.NODE_ENV === 'umd') {
presets = [
[
"@babel/preset-env"
]
];
}
return {
plugins: ['@babel/plugin-transform-object-assign'],
presets
};
};
import { version } from '../package.json';
export default `/**!
* Sortable ${ version }
* @author RubaXa <trash@rubaxa.org>
* @author owenm <owen23355@gmail.com>
* @license MIT
*/`;
import babel from 'rollup-plugin-babel';
import json from 'rollup-plugin-json';
import resolve from 'rollup-plugin-node-resolve';
import banner from './banner.js';
export default {
output: {
banner,
name: 'Sortable'
},
plugins: [
json(),
babel(),
resolve()
]
};
import build from './build.js';
export default ([
{
input: 'entry/entry-core.js',
output: Object.assign({}, build.output, {
file: 'modular/sortable.core.esm.js',
format: 'esm'
})
},
{
input: 'entry/entry-defaults.js',
output: Object.assign({}, build.output, {
file: 'modular/sortable.esm.js',
format: 'esm'
})
},
{
input: 'entry/entry-complete.js',
output: Object.assign({}, build.output, {
file: 'modular/sortable.complete.esm.js',
format: 'esm'
})
}
]).map(config => {
let buildCopy = { ...build };
return Object.assign(buildCopy, config);
});
const UglifyJS = require('uglify-js'),
fs = require('fs'),
package = require('../package.json');
const banner = `/*! Sortable ${ package.version } - ${ package.license } | ${ package.repository.url } */\n`;
fs.writeFileSync(
`./Sortable.min.js`,
banner + UglifyJS.minify(fs.readFileSync(`./Sortable.js`, 'utf8')).code,
'utf8'
);
import build from './build.js';
export default ([
{
input: 'entry/entry-complete.js',
output: Object.assign({}, build.output, {
file: './Sortable.js',
format: 'umd'
})
}
]).map(config => {
let buildCopy = { ...build };
return Object.assign(buildCopy, config);
});
import Sortable from './entry-defaults.js';
import Swap from '../plugins/Swap';
import MultiDrag from '../plugins/MultiDrag';
Sortable.mount(new Swap());
Sortable.mount(new MultiDrag());
export default Sortable;
import Sortable from '../src/Sortable.js';
import AutoScroll from '../plugins/AutoScroll';
import OnSpill from '../plugins/OnSpill';
import Swap from '../plugins/Swap';
import MultiDrag from '../plugins/MultiDrag';
export default Sortable;
export {
Sortable,
// Default
AutoScroll,
OnSpill,
// Extra
Swap,
MultiDrag
};
import Sortable from '../src/Sortable.js';
import AutoScroll from '../plugins/AutoScroll';
import { RemoveOnSpill, RevertOnSpill } from '../plugins/OnSpill';
// Extra
import Swap from '../plugins/Swap';
import MultiDrag from '../plugins/MultiDrag';
Sortable.mount(new AutoScroll());
Sortable.mount(RemoveOnSpill, RevertOnSpill);
export default Sortable;
export {
Sortable,
// Extra
Swap,
MultiDrag
};
/* jshint ignore:start */
const gulp = require('gulp'),
uglify = require('gulp-uglify'),
rename = require('gulp-rename'),
each = require('gulp-each'),
path = require('path'),
pump = require('pump')
;
const package = require('./package.json'),
name = package.exportName,
versionExp = /(?<=\"version\":\s{0,}\")(.{0,})(?=\")/i;
gulp.task('version', function() {
let version = package.version;
return pump([
gulp.src('./*.json'),
each(function(content, file, callback){
let prevVersion = content.match(versionExp);
prevVersion && (prevVersion = prevVersion[0]);
content = content.replace(versionExp, version);
prevVersion && prevVersion !== version && console.info(`Updated version in ${ path.basename(file.history[0]) }:\t${ prevVersion } -> ${ version }`);
callback(null, content);
}),
gulp.dest('./')
]);
});
gulp.task('minify', function() {
return pump([
gulp.src(`./${ name }.js`),
uglify({
output: {
preamble: `/*! ${ name } ${ package.version } - ${ package.license } | ${ package.repository.url } */\n`
}
}),
rename({
suffix: '.min'
}),
gulp.dest(`./`)
]);
});
gulp.task('build', gulp.series('version', 'minify'));
/* jshint ignore:end */
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -3,18 +3,24 @@
"exportName": "Sortable",
"version": "1.9.0",
"devDependencies": {
"gulp": "^4.0.0",
"gulp-each": "^0.5.0",
"gulp-rename": "^1.4.0",
"gulp-uglify": "^3.0.1",
"http-server": "^0.9.0",
"pump": "^3.0.0"
"@babel/core": "^7.4.4",
"@babel/plugin-transform-object-assign": "^7.2.0",
"@babel/preset-env": "^7.4.4",
"rollup": "^1.11.3",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^5.0.0",
"uglify-js": "^3.5.12"
},
"description": "JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices. No jQuery required. Supports Meteor, AngularJS, React, Polymer, Vue, Knockout and any CSS library, e.g. Bootstrap.",
"main": "Sortable.js",
"main": "./Sortable.js",
"module": "modular/sortable.esm.js",
"scripts": {
"http-server": "http-server -s ./",
"prepublish": "gulp build"
"build:umd": "set NODE_ENV=umd&& rollup -c build/umd-build.js",
"build:umd:watch": "set NODE_ENV=umd&& rollup -w -c build/umd-build.js",
"build:es": "set NODE_ENV=es&& rollup -c build/esm-build.js",
"minify": "node build/minify.js",
"build": "npm run build:es && npm run build:umd && npm run minify"
},
"maintainers": [
"Konstantin Lebedev <ibnRubaXa@gmail.com>",
......@@ -39,12 +45,5 @@
"vue",
"mixin"
],
"license": "MIT",
"spm": {
"main": "Sortable.js",
"ignore": [
"meteor",
"st"
]
}
"license": "MIT"
}
import {
on,
off,
css,
throttle,
cancelThrottle,
scrollBy,
getParentAutoScrollElement,
expando,
getRect,
getWindowScrollingElement
} from '../../src/utils.js';
import { Edge, IE11OrLess, Safari } from '../../src/BrowserInfo.js';
let autoScrolls = [],
scrollEl,
scrollRootEl,
scrolling = false,
lastAutoScrollX,
lastAutoScrollY,
touchEvt,
pointerElemChangedInterval;
function AutoScrollPlugin() {
function AutoScroll() {
this.options = {
scroll: true,
scrollSensitivity: 30,
scrollSpeed: 10,
bubbleScroll: true
};
// Bind all private methods
for (let fn in this) {
if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
this[fn] = this[fn].bind(this);
}
}
}
AutoScroll.prototype = {
dragStarted({ originalEvent }) {
if (this.sortable.nativeDraggable) {
on(document, 'dragover', this._handleAutoScroll);
} else {
if (this.sortable.options.supportPointer) {
on(document, 'pointermove', this._handleFallbackAutoScroll);
} else if (originalEvent.touches) {
on(document, 'touchmove', this._handleFallbackAutoScroll);
} else {
on(document, 'mousemove', this._handleFallbackAutoScroll);
}
}
},
dragOverCompleted({ originalEvent }) {
// For when bubbling is canceled and using fallback (fallback 'touchmove' always reached)
if (!this.sortable.options.dragOverBubble && !originalEvent.rootEl) {
this._handleAutoScroll(originalEvent);
}
},
drop() {
if (this.sortable.nativeDraggable) {
off(document, 'dragover', this._handleAutoScroll);
} else {
off(document, 'pointermove', this._handleFallbackAutoScroll);
off(document, 'touchmove', this._handleFallbackAutoScroll);
off(document, 'mousemove', this._handleFallbackAutoScroll);
}
clearPointerElemChangedInterval();
clearAutoScrolls();
cancelThrottle();
},
nulling() {
touchEvt =
scrollRootEl =
scrollEl =
scrolling =
pointerElemChangedInterval =
lastAutoScrollX =
lastAutoScrollY = null;
autoScrolls.length = 0;
},
_handleFallbackAutoScroll(evt) {
this._handleAutoScroll(evt, true);
},
_handleAutoScroll(evt, fallback) {
const x = evt.clientX,
y = evt.clientY,
elem = document.elementFromPoint(x, y);
touchEvt = evt;
// IE does not seem to have native autoscroll,
// Edge's autoscroll seems too conditional,
// MACOS Safari does not have autoscroll,
// Firefox and Chrome are good
if (fallback || Edge || IE11OrLess || Safari) {
autoScroll(evt, this.options, elem, fallback);
// Listener for pointer element change
let ogElemScroller = getParentAutoScrollElement(elem, true);
if (
scrolling &&
(
!pointerElemChangedInterval ||
x !== lastAutoScrollX ||
y !== lastAutoScrollY
)
) {
pointerElemChangedInterval && clearPointerElemChangedInterval();
// Detect for pointer elem change, emulating native DnD behaviour
pointerElemChangedInterval = setInterval(() => {
let newElem = getParentAutoScrollElement(document.elementFromPoint(x, y), true);
if (newElem !== ogElemScroller) {
ogElemScroller = newElem;
clearAutoScrolls();
}
autoScroll(evt, this.options, newElem, fallback);
}, 10);
lastAutoScrollX = x;
lastAutoScrollY = y;
}
} else {
// if DnD is enabled (and browser has good autoscrolling), first autoscroll will already scroll, so get parent autoscroll of first autoscroll
if (!this.sortable.options.bubbleScroll || getParentAutoScrollElement(elem, true) === getWindowScrollingElement()) {
clearAutoScrolls();
return;
}
autoScroll(evt, this.options, getParentAutoScrollElement(elem, false), false);
}
}
};
return Object.assign(AutoScroll, {
pluginName: 'scroll',
initializeByDefault: true
});
}
function clearAutoScrolls() {
autoScrolls.forEach(function(autoScroll) {
clearInterval(autoScroll.pid);
});
autoScrolls = [];
}
function clearPointerElemChangedInterval() {
clearInterval(pointerElemChangedInterval);
}
const autoScroll = throttle(function(evt, options, rootEl, isFallback) {
// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
if (!options.scroll) return;
const sens = options.scrollSensitivity,
speed = options.scrollSpeed,
winScroller = getWindowScrollingElement();
let scrollThisInstance = false,
scrollCustomFn;
// New scroll root, set scrollEl
if (scrollRootEl !== rootEl) {
scrollRootEl = rootEl;
clearAutoScrolls();
scrollEl = options.scroll;
scrollCustomFn = options.scrollFn;
if (scrollEl === true) {
scrollEl = getParentAutoScrollElement(rootEl, true);
}
}
let layersOut = 0;
let currentParent = scrollEl;
do {
let el = currentParent,
rect = getRect(el),
top = rect.top,
bottom = rect.bottom,
left = rect.left,
right = rect.right,
width = rect.width,
height = rect.height,
canScrollX,
canScrollY,
scrollWidth = el.scrollWidth,
scrollHeight = el.scrollHeight,
elCSS = css(el),
scrollPosX = el.scrollLeft,
scrollPosY = el.scrollTop;
if (el === winScroller) {
canScrollX = width < scrollWidth && (elCSS.overflowX === 'auto' || elCSS.overflowX === 'scroll' || elCSS.overflowX === 'visible');
canScrollY = height < scrollHeight && (elCSS.overflowY === 'auto' || elCSS.overflowY === 'scroll' || elCSS.overflowY === 'visible');
} else {
canScrollX = width < scrollWidth && (elCSS.overflowX === 'auto' || elCSS.overflowX === 'scroll');
canScrollY = height < scrollHeight && (elCSS.overflowY === 'auto' || elCSS.overflowY === 'scroll');
}
let vx = canScrollX && (Math.abs(right - evt.clientX) <= sens && (scrollPosX + width) < scrollWidth) - (Math.abs(left - evt.clientX) <= sens && !!scrollPosX);
let vy = canScrollY && (Math.abs(bottom - evt.clientY) <= sens && (scrollPosY + height) < scrollHeight) - (Math.abs(top - evt.clientY) <= sens && !!scrollPosY);
if (!autoScrolls[layersOut]) {
for (let i = 0; i <= layersOut; i++) {
if (!autoScrolls[i]) {
autoScrolls[i] = {};
}
}
}
if (autoScrolls[layersOut].vx != vx || autoScrolls[layersOut].vy != vy || autoScrolls[layersOut].el !== el) {
autoScrolls[layersOut].el = el;
autoScrolls[layersOut].vx = vx;
autoScrolls[layersOut].vy = vy;
clearInterval(autoScrolls[layersOut].pid);
if (vx != 0 || vy != 0) {
scrollThisInstance = true;
/* jshint loopfunc:true */
autoScrolls[layersOut].pid = setInterval((function () {
// emulate drag over during autoscroll (fallback), emulating native DnD behaviour
if (isFallback && this.layer === 0) {
Sortable.active._onTouchMove(touchEvt); // To move ghost if it is positioned absolutely
}
let scrollOffsetY = autoScrolls[this.layer].vy ? autoScrolls[this.layer].vy * speed : 0;
let scrollOffsetX = autoScrolls[this.layer].vx ? autoScrolls[this.layer].vx * speed : 0;
if (typeof(scrollCustomFn) === 'function') {
if (scrollCustomFn.call(Sortable.dragged.parentNode[expando], scrollOffsetX, scrollOffsetY, evt, touchEvt, autoScrolls[this.layer].el) !== 'continue') {
return;
}
}
scrollBy(autoScrolls[this.layer].el, scrollOffsetX, scrollOffsetY);
}).bind({layer: layersOut}), 24);
}
}
layersOut++;
} while (options.bubbleScroll && currentParent !== winScroller && (currentParent = getParentAutoScrollElement(currentParent, false)));
scrolling = scrollThisInstance; // in case another function catches scrolling as false in between when it is not
}, 30);
export default AutoScrollPlugin;
## AutoScroll
This plugin allows for the page to automatically scroll during dragging near a scrollable element's edge on mobile devices and IE9 (or whenever fallback is enabled), and also enhances most browser's native drag-and-drop autoscrolling.
Demo:
- `window`: https://jsbin.com/dosilir/edit?js,output
- `overflow: hidden`: https://jsbin.com/xecihez/edit?html,js,output
**This plugin is a default plugin, and is included in the default UMD and ESM builds of Sortable**
---
### Options
```js
new Sortable(el, {
scroll: true, // Enable the plugin. Can be HTMLElement.
scrollFn: function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { ... }, // if you have custom scrollbar scrollFn may be used for autoscrolling
scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling.
scrollSpeed: 10, // px, speed of the scrolling
bubbleScroll: true // apply autoscroll to all parent elements, allowing for easier movement
});
```
---
#### `scroll` option
Enables the plugin. Defaults to `true`. May also be set to an HTMLElement which will be where autoscrolling is rooted.
Demo:
- `window`: https://jsbin.com/dosilir/edit?js,output
- `overflow: hidden`: https://jsbin.com/xecihez/edit?html,js,output
---
#### `scrollFn` option
Defines function that will be used for autoscrolling. el.scrollTop/el.scrollLeft is used by default.
Useful when you have custom scrollbar with dedicated scroll function.
This function should return `'continue'` if it wishes to allow Sortable's native autoscrolling.
---
#### `scrollSensitivity` option
Defines how near the mouse must be to an edge to start scrolling.
---
#### `scrollSpeed` option
The speed at which the window should scroll once the mouse pointer gets within the `scrollSensitivity` distance.
---
#### `bubbleScroll` option
If set to `true`, the normal `autoscroll` function will also be applied to all parent elements of the element the user is dragging over.
Demo: https://jsbin.com/kesewor/edit?html,js,output
---
export { default } from './AutoScroll.js';
此差异已折叠。
## MultiDrag Plugin
This plugin allows users to select multiple items within a sortable at once, and drag them as one item.
Once placed, the items will unfold into their original order, but all beside eachother at the new position.
[Read More](https://github.com/SortableJS/Sortable/wiki/Dragging-Multiple-Items-in-Sortable)
Demo: https://jsbin.com/wopavom/edit?js,output
---
### Options
```js
new Sortable(el, {
multiDrag: false, // Enable the plugin
selectedClass: "sortable-selected", // Class name for selected item
multiDragKey: null, // Key that must be down for items to be selected
// Called when an item is selected
onSelect: function(/**Event*/evt) {
evt.item // The selected item
},
// Called when an item is deselected
onDeselect: function(/**Event*/evt) {
evt.item // The deselected item
}
});
```
---
#### `multiDragKey` option
The key that must be down for multiple items to be selected. The default is `null`, meaning no key must be down.
For special keys, such as the <kbd>CTRL</kbd> key, simply specify the option as `'CTRL'` (casing does not matter).
---
#### `selectedClass` option
Class name for the selected item(s) if multiDrag is enabled. Defaults to `sortable-selected`.
```css
.selected {
background-color: #f9c7c8;
border: solid red 1px;
}
```
```js
Sortable.create(list, {
multiDrag: true,
selectedClass: "selected"
});
```
---
### Event Properties
- items:`HTMLElement[]` - Array of selected items, or empty
- clones:`HTMLElement[]` - Array of clones, or empty
---
### Sortable.utils
* select(el`:HTMLElement`) — select the given multi-drag item
* deselect(el`:HTMLElement`) — deselect the given multi-drag item
export { default } from './MultiDrag.js';
import { getChild } from '../../src/utils.js';
const drop = function({
originalEvent,
putSortable,
dragEl,
activeSortable,
dispatchSortableEvent,
hideGhostForTarget,
unhideGhostForTarget
}) {
let toSortable = putSortable || activeSortable;
hideGhostForTarget();
let target = document.elementFromPoint(originalEvent.clientX, originalEvent.clientY);
unhideGhostForTarget();
if (toSortable && !toSortable.el.contains(target)) {
dispatchSortableEvent('spill');
this.onSpill(dragEl);
}
};
function Revert() {}
Revert.prototype = {
startIndex: null,
dragStart({ oldDraggableIndex }) {
this.startIndex = oldDraggableIndex;
},
onSpill(dragEl) {
this.sortable.captureAnimationState();
let nextSibling = getChild(this.sortable.el, this.startIndex, this.sortable.options);
if (nextSibling) {
this.sortable.el.insertBefore(dragEl, nextSibling);
} else {
this.sortable.el.appendChild(dragEl);
}
this.sortable.animateAll();
},
drop
};
Object.assign(Revert, {
pluginName: 'revertOnSpill'
});
function Remove() {}
Remove.prototype = {
onSpill(dragEl) {
this.sortable.captureAnimationState();
dragEl.parentNode && dragEl.parentNode.removeChild(dragEl);
this.sortable.animateAll();
},
drop
};
Object.assign(Remove, {
pluginName: 'removeOnSpill'
});
export default [Remove, Revert];
export {
Remove as RemoveOnSpill,
Revert as RevertOnSpill
};
# OnSpill Plugins
This file contains two seperate plugins, RemoveOnSpill and RevertOnSpill. They can be imported individually, or the default export (an array of both plugins) can be passed to `Sortable.mount` as well.
**These plugins are default plugins, and are included in the default UMD and ESM builds of Sortable**
---
## RevertOnSpill Plugin
This plugin, when enabled, will cause the dragged item to be reverted to it's original position if it is spilled (ie. it is dropped outside of a valid Sortable drop target)
---
### Options
```js
new Sortable(el, {
revertOnSpill: false // Enable plugin
});
```
---
## RemoveOnSpill Plugin
This plugin, when enabled, will cause the dragged item to be removed from the DOM if it is spilled (ie. it is dropped outside of a valid Sortable drop target)
---
### Options
```js
new Sortable(el, {
removeOnSpill: false // Enable plugin
});
```
export { default, RemoveOnSpill, RevertOnSpill } from './OnSpill.js';
# Creating Sortable Plugins
Sortable plugins are plugins that can be directly mounted to the Sortable class. They are a powerful way of modifying the default behaviour of Sortable beyond what simply using events alone allows. To mount your plugin to Sortable, it must pass a constructor function to the `Sortable.mount` function. This constructor function will be called (with the `new` keyword in front of it) whenever a Sortable instance with your plugin enabled is initialized. The constructor function will be called with the parameters `sortable` and `el`, which is the HTMLElement that the Sortable is being initialized on. This means that there will be a new instance of your plugin each time it is enabled in a Sortable.
## Constructor Parameters
`sortable: Sortable` — The sortable that the plugin is being initialized on
`el: HTMLElement` — The element that the sortable is being initialized on
## Static Properties
The constructor function passed to `Sortable.mount` may contain several static properties and methods. The following static properties may be defined:
`pluginName: String` (Required)
The name of the option that the user will use in their sortable's options to enable the plugin. Should start with a lower case and be camel-cased. For example: `'multiDrag'`. This is also the property name that the plugin's instance will be under in a sortable instance (ex. `sortableInstance.multiDrag`).
`utils: Object`
Object containing functions that will be added to the `Sortable.utils` default object on the Sortable class.
`eventOptions(eventName: String, sortable: Sortable): Function`
A function that is called whenever Sortable fires an event. This function should return an object to be combined with the event object that Sortable will emit.
`initializeByDefault: Boolean`
Determines whether or not the plugin will always be initialized on every new Sortable instance. If this option is enabled, it does not mean that by default the plugin will be enabled on the Sortable - this must still be done in the options via the plugin's `pluginName`, or it can be enabled by default if your plugin specifies it's pluginName as a default option that is truthy. Since the plugin will already be initialized on every Sortable instance, it can also be enabled dynamically via `sortableInstance.option('pluginName', true)`.
It is a good idea to have this option set to `false` if the plugin modifies the behaviour of Sortable in such a way that enabling or disabling the plugin dynamically could cause it to break. Likewise, this option should be disabled if the plugin should only be instantiated on Sortables in which that plugin is enabled.
This option defaults to `true`.
`optionListeners: Object`
An object that may contain event listeners that are fired when a specific option is updated.
These listeners are useful because the user's provided options are not necessarily unchanging once the plugin is initialized, and could be changed dynamically via the `option()` method.
The listener will be fired in the context of the instance of the plugin that it is being changed in (ie. the `this` keyword will be the instance of your plugin).
The name of the method should match the name of the option it listens for. The new value of the option will be passed in as an argument, and any returned value will be what the option is stored as. If no value is returned, the option will be stored as the value the user provided.
Example:
```js
Plugin.name = 'generateTitle';
Plugin.optionModifiers = {
// Listen for option 'generateTitle'
generateTitle: function(title) {
// Store the option in all caps
return title.toUpperCase();
// OR save it to this instance of your plugin as a private field.
// This way it can be accessed in events, but will not modify the user's options.
this.titleAllCaps = title.toUpperCase();
}
};
```
## Plugin Options
Plugins may have custom options or override the defaults of certain options. In order to do this, there must be an `options` object on the initialized plugin. This can be set in the plugin's prototype, or during the initialization of the plugin (when the `el` is available). For example:
```js
function myPlugin(el) {
this.options = {
color: el.style.backgroundColor
};
}
Sortable.mount(myPlugin);
```
## Plugin Events
### Context
The events will be fired in the context of their own parent object, however the plugin instance's Sortable instance is available under `this.sortable`.
### Event List
The following table contains details on the events that a plugin may handle in the prototype of the plugin's constructor function.
| Event Name | Description | Cancelable? | Cancel Behaviour | Event Type | Custom Event Object Properties |
|---------------------------|------------------------------------------------------------------------------------------------------------------|-------------|----------------------------------------------------|------------|-------------------------------------------------------------------------|
| filter | Fired when the element is filtered, and dragging is therefore canceled | No | - | Normal | None |
| delayStart | Fired when the delay starts, even if there is no delay | Yes | Cancels sorting | Normal | None |
| delayEnded | Fired when the delay ends, even if there is no delay | Yes | Cancels sorting | Normal | None |
| setupClone | Fired when Sortable clones the dragged element | Yes | Cancels normal clone setup | Normal | None |
| dragStart | Fired when the dragging is first started | Yes | Cancels sorting | Normal | None |
| clone | Fired when the clone is inserted into the DOM (if `removeCloneOnHide: false`). Tick after dragStart. | Yes | Cancels normal clone insertion & hiding | Normal | None |
| dragStarted | Fired tick after dragStart | No | - | Normal | None |
| dragOver | Fired when the user drags over a sortable | Yes | Cancels normal dragover behaviour | DragOver | None |
| dragOverValid | Fired when the user drags over a sortable that the dragged item can be inserted into | Yes | Cancels normal valid dragover behaviour | DragOver | None |
| revert | Fired when the dragged item is reverted to it's original position when entering it's `sort:false` root | Yes | Cancels normal reverting, but is still completed() | DragOver | None |
| dragOverCompleted | Fired when dragOver is completed (ie. bubbling is disabled). To check if inserted, use `inserted` even property. | No | - | DragOver | `insertion: Boolean` — Whether or not the dragged element was inserted |
| dragOverAnimationCapture | Fired right before the animation state is captured in dragOver | No | - | DragOver | None |
| dragOverAnimationComplete | Fired after the animation is completed after a dragOver insertion | No | - | DragOver | None |
| drop | Fired on drop | Yes | Cancels normal drop behavior | Normal | None |
| nulling | Fired when the plugin should preform cleanups, once all drop events have fired | No | - | Normal | None |
| destroy | Fired when Sortable is destroyed | No | - | Normal | None |
### Global Events
Normally, an event will only be fired in a plugin if the plugin is enabled on the Sortable from which the event is being fired. However, it sometimes may be desirable for a plugin to listen in on an event from Sortables in which it is not enabled on. This is possible with global events. For an event to be global, simply add the suffix 'Global' to the event's name (casing matters) (eg. `dragStartGlobal`).
Please note that your plugin must be initialized on any Sortable from which it expects to recieve events, and that includes global events. In other words, you will want to keep the `initializeByDefault` option as it's default `true` value if your plugin needs to recieve events from Sortables it is not enabled on.
Please also note that if both normal and global event handlers are set, the global event handler will always be fired before the regular one.
### Event Object
An object with the following properties is passed as an argument to each plugin event when it is fired.
#### Properties:
`dragEl: HTMLElement` — The element being dragged
`parentEl: HTMLElement` — The element that the dragged element is currently in
`ghostEl: HTMLElement|undefined` — If using fallback, the element dragged under the cursor (undefined until after `dragStarted` plugin event)
`rootEl: HTMLElement` — The element that the dragged element originated from
`nextEl: HTMLElement` — The original next sibling of dragEl
`cloneEl: HTMLElement|undefined` — The clone element (undefined until after `setupClone` plugin event)
`cloneHidden: Boolean` — Whether or not the clone is hidden
`dragStarted: Boolean` — Boolean indicating whether or not the dragStart event has fired
`putSortable: Sortable|undefined` — The element that dragEl is dragged into from it's root, otherwise undefined
`activeSortable: Sortable` — The active Sortable instance
`originalEvent: Event` — The original HTML event corresponding to the Sortable event
`oldIndex: Number` — The old index of dragEl
`oldDraggableIndex: Number` — The old index of dragEl, only counting draggable elements
`newIndex: Number` — The new index of dragEl
`newDraggableIndex: Number` — The new index of dragEl, only counting draggable elements
#### Methods:
`cloneNowHidden()` — Function to be called if the plugin has hidden the clone
`cloneNowShown()` — Function to be called if the plugin has shown the clone
`hideGhostForTarget()` — Hides the fallback ghost element if CSS pointer-events are not available. Call this before using document.elementFromPoint at the mouse position.
`unhideGhostForTarget()` — Unhides the ghost element. To be called after `hideGhostForTarget()`.
`dispatchSortableEvent(eventName: String)` — Function that can be used to emit an event on the current sortable while sorting, with all usual event properties set (eg. indexes, rootEl, cloneEl, originalEvent, etc.).
### DragOverEvent Object
This event is passed to dragover events, and extends the normal event object.
#### Properties:
`isOwner: Boolean` — Whether or not the dragged over sortable currently contains the dragged element
`axis: String` — Direction of the dragged over sortable, `'vertical'` or `'horizontal'`
`revert: Boolean` — Whether or not the dragged element is being reverted to it's original position from another position
`dragRect: DOMRect` — DOMRect of the dragged element
`targetRect: DOMRect` — DOMRect of the target element
`canSort: Boolean` — Whether or not sorting is enabled in the dragged over sortable
`fromSortable: Sortable` — The sortable that the dragged element is coming from
`target: HTMLElement` — The sortable item that is being dragged over
#### Methods:
`onMove(target: HTMLElement, after: Boolean): Boolean|Number` — Calls the `onMove` function the user specified in the options
`changed()` — Fires the `onChange` event with event properties preconfigured
`completed(insertion: Boolean)` — Should be called when dragover has "completed", meaning bubbling should be stopped. If `insertion` is `true`, Sortable will treat it as if the dragged element was inserted into the sortable, and hide/show clone, set ghost class, animate, etc.
## Swap Plugin
This plugin modifies the behaviour of Sortable to allow for items to be swapped with eachother rather than sorted. Once dragging starts, the user can drag over other items and there will be no change in the elements. However, the item that the user drops on will be swapped with the originally dragged item.
Demo: https://jsbin.com/yejehog/edit?html,js,output
---
### Options
```js
new Sortable(el, {
swap: false, // Enable swap mode
swapClass: "sortable-swap-highlight" // Class name for swap item (if swap mode is enabled)
});
```
---
#### `swapClass` option
Class name for the item to be swapped with, if swap mode is enabled. Defaults to `sortable-swap-highlight`.
```css
.highlighted {
background-color: #9AB6F1;
}
```
```js
Sortable.create(list, {
swap: true,
swapClass: "highlighted"
});
```
---
### Event Properties
- swapItem:`HTMLElement|undefined` - The element that the dragged element was swapped with
import {
toggleClass,
index
} from '../../src/utils.js';
let lastSwapEl;
function SwapPlugin() {
function Swap() {
this.options = {
swapClass: 'sortable-swap-highlight'
};
}
Swap.prototype = {
dragStart({ dragEl }) {
lastSwapEl = dragEl;
},
dragOverValid({ completed, target, onMove, activeSortable, changed }) {
if (!activeSortable.options.swap) return;
let el = this.sortable.el,
options = this.sortable.options;
if (target && target !== el) {
let prevSwapEl = lastSwapEl;
if (onMove(target) !== false) {
toggleClass(target, options.swapClass, true);
lastSwapEl = target;
} else {
lastSwapEl = null;
}
if (prevSwapEl && prevSwapEl !== lastSwapEl) {
toggleClass(prevSwapEl, options.swapClass, false);
}
}
changed();
return completed(true);
},
drop({ activeSortable, putSortable, dragEl }) {
let toSortable = (putSortable || this.sortable);
let options = this.sortable.options;
lastSwapEl && toggleClass(lastSwapEl, options.swapClass, false);
if (lastSwapEl && (options.swap || putSortable && putSortable.options.swap)) {
if (dragEl !== lastSwapEl) {
toSortable.captureAnimationState();
if (toSortable !== activeSortable) activeSortable.captureAnimationState();
swapNodes(dragEl, lastSwapEl);
toSortable.animateAll();
if (toSortable !== activeSortable) activeSortable.animateAll();
}
}
},
nulling() {
lastSwapEl = null;
}
};
return Object.assign(Swap, {
pluginName: 'swap',
eventOptions() {
return {
swapItem: lastSwapEl
};
}
});
}
function swapNodes(n1, n2) {
let p1 = n1.parentNode,
p2 = n2.parentNode,
i1, i2;
if (!p1 || !p2 || p1.isEqualNode(n2) || p2.isEqualNode(n1)) return;
i1 = index(n1);
i2 = index(n2);
if (p1.isEqualNode(p2) && i1 < i2) {
i2++;
}
p1.insertBefore(n2, p1.children[i1]);
p2.insertBefore(n1, p2.children[i2]);
}
export default SwapPlugin;
export { default } from './Swap.js';
此差异已折叠。
function userAgent(pattern) {
return !!navigator.userAgent.match(pattern);
}
const IE11OrLess = /*@__PURE__*/userAgent(/(?:Trident.*rv[ :]?11\.|msie|iemobile)/i);
const Edge = /*@__PURE__*/userAgent(/Edge/i);
const FireFox = /*@__PURE__*/userAgent(/firefox/i);
const Safari = /*@__PURE__*/userAgent(/safari/i) && !userAgent(/chrome/i) && !userAgent(/android/i);
const IOS = /*@__PURE__*/userAgent(/iP(ad|od|hone)/i);
export {
IE11OrLess,
Edge,
FireFox,
Safari,
IOS
};
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册