init commit

This commit is contained in:
2026-03-17 09:56:00 +08:00
commit e2c8ae752d
6827 changed files with 1211784 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
import { getRect, css, matrix, isRectEqual, indexOfObject } from './utils.js';
import Sortable from './Sortable.js';
export default function AnimationStateManager() {
let animationStates = [],
animationCallbackId;
return {
captureAnimationState() {
animationStates = [];
if (!this.options.animation) return;
let children = [].slice.call(this.el.children);
children.forEach(child => {
if (css(child, 'display') === 'none' || child === Sortable.ghost) return;
animationStates.push({
target: child,
rect: getRect(child)
});
let fromRect = { ...animationStates[animationStates.length - 1].rect };
// If animating: compensate for current animation
if (child.thisAnimationDuration) {
let childMatrix = matrix(child, true);
if (childMatrix) {
fromRect.top -= childMatrix.f;
fromRect.left -= childMatrix.e;
}
}
child.fromRect = fromRect;
});
},
addAnimationState(state) {
animationStates.push(state);
},
removeAnimationState(target) {
animationStates.splice(indexOfObject(animationStates, { target }), 1);
},
animateAll(callback) {
if (!this.options.animation) {
clearTimeout(animationCallbackId);
if (typeof(callback) === 'function') callback();
return;
}
let animating = false,
animationTime = 0;
animationStates.forEach((state) => {
let time = 0,
animatingThis = false,
target = state.target,
fromRect = target.fromRect,
toRect = getRect(target),
prevFromRect = target.prevFromRect,
prevToRect = target.prevToRect,
animatingRect = state.rect,
targetMatrix = matrix(target, true);
if (targetMatrix) {
// Compensate for current animation
toRect.top -= targetMatrix.f;
toRect.left -= targetMatrix.e;
}
target.toRect = toRect;
if (target.thisAnimationDuration) {
// Could also check if animatingRect is between fromRect and toRect
if (
isRectEqual(prevFromRect, toRect) &&
!isRectEqual(fromRect, toRect) &&
// Make sure animatingRect is on line between toRect & fromRect
(animatingRect.top - toRect.top) /
(animatingRect.left - toRect.left) ===
(fromRect.top - toRect.top) /
(fromRect.left - toRect.left)
) {
// If returning to same place as started from animation and on same axis
time = calculateRealTime(animatingRect, prevFromRect, prevToRect, this.options);
}
}
// if fromRect != toRect: animate
if (!isRectEqual(toRect, fromRect)) {
target.prevFromRect = fromRect;
target.prevToRect = toRect;
if (!time) {
time = this.options.animation;
}
this.animate(
target,
animatingRect,
toRect,
time
);
}
if (time) {
animating = true;
animationTime = Math.max(animationTime, time);
clearTimeout(target.animationResetTimer);
target.animationResetTimer = setTimeout(function() {
target.animationTime = 0;
target.prevFromRect = null;
target.fromRect = null;
target.prevToRect = null;
target.thisAnimationDuration = null;
}, time);
target.thisAnimationDuration = time;
}
});
clearTimeout(animationCallbackId);
if (!animating) {
if (typeof(callback) === 'function') callback();
} else {
animationCallbackId = setTimeout(function() {
if (typeof(callback) === 'function') callback();
}, animationTime);
}
animationStates = [];
},
animate(target, currentRect, toRect, duration) {
if (duration) {
css(target, 'transition', '');
css(target, 'transform', '');
let elMatrix = matrix(this.el),
scaleX = elMatrix && elMatrix.a,
scaleY = elMatrix && elMatrix.d,
translateX = (currentRect.left - toRect.left) / (scaleX || 1),
translateY = (currentRect.top - toRect.top) / (scaleY || 1);
target.animatingX = !!translateX;
target.animatingY = !!translateY;
css(target, 'transform', 'translate3d(' + translateX + 'px,' + translateY + 'px,0)');
repaint(target); // repaint
css(target, 'transition', 'transform ' + duration + 'ms' + (this.options.easing ? ' ' + this.options.easing : ''));
css(target, 'transform', 'translate3d(0,0,0)');
(typeof target.animated === 'number') && clearTimeout(target.animated);
target.animated = setTimeout(function () {
css(target, 'transition', '');
css(target, 'transform', '');
target.animated = false;
target.animatingX = false;
target.animatingY = false;
}, duration);
}
}
};
}
function repaint(target) {
return target.offsetWidth;
}
function calculateRealTime(animatingRect, fromRect, toRect, options) {
return (
Math.sqrt(Math.pow(fromRect.top - animatingRect.top, 2) + Math.pow(fromRect.left - animatingRect.left, 2)) /
Math.sqrt(Math.pow(fromRect.top - toRect.top, 2) + Math.pow(fromRect.left - toRect.left, 2))
) * options.animation;
}

View File

@@ -0,0 +1,12 @@
function userAgent(pattern) {
if (typeof window !== 'undefined' && window.navigator) {
return !!/*@__PURE__*/navigator.userAgent.match(pattern);
}
}
export const IE11OrLess = userAgent(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i);
export const Edge = userAgent(/Edge/i);
export const FireFox = userAgent(/firefox/i);
export const Safari = userAgent(/safari/i) && !userAgent(/chrome/i) && !userAgent(/android/i);
export const IOS = userAgent(/iP(ad|od|hone)/i);
export const ChromeForAndroid = userAgent(/chrome/i) && userAgent(/android/i);

View File

@@ -0,0 +1,57 @@
import { IE11OrLess, Edge } from './BrowserInfo.js';
import { expando } from './utils.js';
import PluginManager from './PluginManager.js';
export default function dispatchEvent(
{
sortable, rootEl, name,
targetEl, cloneEl, toEl, fromEl,
oldIndex, newIndex,
oldDraggableIndex, newDraggableIndex,
originalEvent, putSortable, extraEventProperties
}
) {
sortable = (sortable || (rootEl && rootEl[expando]));
if (!sortable) return;
let evt,
options = sortable.options,
onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);
// Support for new CustomEvent feature
if (window.CustomEvent && !IE11OrLess && !Edge) {
evt = new CustomEvent(name, {
bubbles: true,
cancelable: true
});
} else {
evt = document.createEvent('Event');
evt.initEvent(name, true, true);
}
evt.to = toEl || rootEl;
evt.from = fromEl || rootEl;
evt.item = targetEl || rootEl;
evt.clone = cloneEl;
evt.oldIndex = oldIndex;
evt.newIndex = newIndex;
evt.oldDraggableIndex = oldDraggableIndex;
evt.newDraggableIndex = newDraggableIndex;
evt.originalEvent = originalEvent;
evt.pullMode = putSortable ? putSortable.lastPutMode : undefined;
let allEventProperties = { ...extraEventProperties, ...PluginManager.getEventProperties(name, sortable) };
for (let option in allEventProperties) {
evt[option] = allEventProperties[option];
}
if (rootEl) {
rootEl.dispatchEvent(evt);
}
if (options[onName]) {
options[onName].call(sortable, evt);
}
}

View File

@@ -0,0 +1,87 @@
let plugins = [];
const defaults = {
initializeByDefault: true
};
export default {
mount(plugin) {
// Set default static properties
for (let option in defaults) {
if (defaults.hasOwnProperty(option) && !(option in plugin)) {
plugin[option] = defaults[option];
}
}
plugins.push(plugin);
},
pluginEvent(eventName, sortable, evt) {
this.eventCanceled = false;
evt.cancel = () => {
this.eventCanceled = true;
};
const eventNameGlobal = eventName + 'Global';
plugins.forEach(plugin => {
if (!sortable[plugin.pluginName]) return;
// Fire global events if it exists in this sortable
if (
sortable[plugin.pluginName][eventNameGlobal]
) {
sortable[plugin.pluginName][eventNameGlobal]({ sortable, ...evt });
}
// Only fire plugin event if plugin is enabled in this sortable,
// and plugin has event defined
if (
sortable.options[plugin.pluginName] &&
sortable[plugin.pluginName][eventName]
) {
sortable[plugin.pluginName][eventName]({ sortable, ...evt });
}
});
},
initializePlugins(sortable, el, defaults, options) {
plugins.forEach(plugin => {
const pluginName = plugin.pluginName;
if (!sortable.options[pluginName] && !plugin.initializeByDefault) return;
let initialized = new plugin(sortable, el, sortable.options);
initialized.sortable = sortable;
initialized.options = sortable.options;
sortable[pluginName] = initialized;
// Add default options from plugin
Object.assign(defaults, initialized.defaults);
});
for (let option in sortable.options) {
if (!sortable.options.hasOwnProperty(option)) continue;
let modified = this.modifyOption(sortable, option, sortable.options[option]);
if (typeof(modified) !== 'undefined') {
sortable.options[option] = modified;
}
}
},
getEventProperties(name, sortable) {
let eventProperties = {};
plugins.forEach(plugin => {
if (typeof(plugin.eventProperties) !== 'function') return;
Object.assign(eventProperties, plugin.eventProperties.call(sortable[plugin.pluginName], name));
});
return eventProperties;
},
modifyOption(sortable, name, value) {
let modifiedValue;
plugins.forEach(plugin => {
// Plugin must exist on the Sortable
if (!sortable[plugin.pluginName]) return;
// If static option listener exists for this option, call in the context of the Sortable's instance of this plugin
if (plugin.optionListeners && typeof(plugin.optionListeners[name]) === 'function') {
modifiedValue = plugin.optionListeners[name].call(sortable[plugin.pluginName], value);
}
});
return modifiedValue;
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,556 @@
import { IE11OrLess } from './BrowserInfo.js';
import Sortable from './Sortable.js';
const captureMode = {
capture: false,
passive: false
};
function on(el, event, fn) {
el.addEventListener(event, fn, !IE11OrLess && captureMode);
}
function off(el, event, fn) {
el.removeEventListener(event, fn, !IE11OrLess && captureMode);
}
function matches(/**HTMLElement*/el, /**String*/selector) {
if (!selector) return;
selector[0] === '>' && (selector = selector.substring(1));
if (el) {
try {
if (el.matches) {
return el.matches(selector);
} else if (el.msMatchesSelector) {
return el.msMatchesSelector(selector);
} else if (el.webkitMatchesSelector) {
return el.webkitMatchesSelector(selector);
}
} catch(_) {
return false;
}
}
return false;
}
function getParentOrHost(el) {
return (el.host && el !== document && el.host.nodeType)
? el.host
: el.parentNode;
}
function closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx, includeCTX) {
if (el) {
ctx = ctx || document;
do {
if (
selector != null &&
(
selector[0] === '>' ?
el.parentNode === ctx && matches(el, selector) :
matches(el, selector)
) ||
includeCTX && el === ctx
) {
return el;
}
if (el === ctx) break;
/* jshint boss:true */
} while (el = getParentOrHost(el));
}
return null;
}
const R_SPACE = /\s+/g;
function toggleClass(el, name, state) {
if (el && name) {
if (el.classList) {
el.classList[state ? 'add' : 'remove'](name);
}
else {
let className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' ');
el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' ');
}
}
}
function css(el, prop, val) {
let style = el && el.style;
if (style) {
if (val === void 0) {
if (document.defaultView && document.defaultView.getComputedStyle) {
val = document.defaultView.getComputedStyle(el, '');
}
else if (el.currentStyle) {
val = el.currentStyle;
}
return prop === void 0 ? val : val[prop];
}
else {
if (!(prop in style) && prop.indexOf('webkit') === -1) {
prop = '-webkit-' + prop;
}
style[prop] = val + (typeof val === 'string' ? '' : 'px');
}
}
}
function matrix(el, selfOnly) {
let appliedTransforms = '';
if (typeof(el) === 'string') {
appliedTransforms = el;
} else {
do {
let transform = css(el, 'transform');
if (transform && transform !== 'none') {
appliedTransforms = transform + ' ' + appliedTransforms;
}
/* jshint boss:true */
} while (!selfOnly && (el = el.parentNode));
}
const matrixFn = window.DOMMatrix || window.WebKitCSSMatrix || window.CSSMatrix || window.MSCSSMatrix;
/*jshint -W056 */
return matrixFn && (new matrixFn(appliedTransforms));
}
function find(ctx, tagName, iterator) {
if (ctx) {
let list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
if (iterator) {
for (; i < n; i++) {
iterator(list[i], i);
}
}
return list;
}
return [];
}
function getWindowScrollingElement() {
let scrollingElement = document.scrollingElement;
if (scrollingElement) {
return scrollingElement
} else {
return document.documentElement
}
}
/**
* Returns the "bounding client rect" of given element
* @param {HTMLElement} el The element whose boundingClientRect is wanted
* @param {[Boolean]} relativeToContainingBlock Whether the rect should be relative to the containing block of (including) the container
* @param {[Boolean]} relativeToNonStaticParent Whether the rect should be relative to the relative parent of (including) the contaienr
* @param {[Boolean]} undoScale Whether the container's scale() should be undone
* @param {[HTMLElement]} container The parent the element will be placed in
* @return {Object} The boundingClientRect of el, with specified adjustments
*/
function getRect(el, relativeToContainingBlock, relativeToNonStaticParent, undoScale, container) {
if (!el.getBoundingClientRect && el !== window) return;
let elRect,
top,
left,
bottom,
right,
height,
width;
if (el !== window && el !== getWindowScrollingElement()) {
elRect = el.getBoundingClientRect();
top = elRect.top;
left = elRect.left;
bottom = elRect.bottom;
right = elRect.right;
height = elRect.height;
width = elRect.width;
} else {
top = 0;
left = 0;
bottom = window.innerHeight;
right = window.innerWidth;
height = window.innerHeight;
width = window.innerWidth;
}
if ((relativeToContainingBlock || relativeToNonStaticParent) && el !== window) {
// Adjust for translate()
container = container || el.parentNode;
// solves #1123 (see: https://stackoverflow.com/a/37953806/6088312)
// Not needed on <= IE11
if (!IE11OrLess) {
do {
if (
container &&
container.getBoundingClientRect &&
(
css(container, 'transform') !== 'none' ||
relativeToNonStaticParent &&
css(container, 'position') !== 'static'
)
) {
let containerRect = container.getBoundingClientRect();
// Set relative to edges of padding box of container
top -= containerRect.top + parseInt(css(container, 'border-top-width'));
left -= containerRect.left + parseInt(css(container, 'border-left-width'));
bottom = top + elRect.height;
right = left + elRect.width;
break;
}
/* jshint boss:true */
} while (container = container.parentNode);
}
}
if (undoScale && el !== window) {
// Adjust for scale()
let elMatrix = matrix(container || el),
scaleX = elMatrix && elMatrix.a,
scaleY = elMatrix && elMatrix.d;
if (elMatrix) {
top /= scaleY;
left /= scaleX;
width /= scaleX;
height /= scaleY;
bottom = top + height;
right = left + width;
}
}
return {
top: top,
left: left,
bottom: bottom,
right: right,
width: width,
height: height
};
}
/**
* Checks if a side of an element is scrolled past a side of its parents
* @param {HTMLElement} el The element who's side being scrolled out of view is in question
* @param {String} elSide Side of the element in question ('top', 'left', 'right', 'bottom')
* @param {String} parentSide Side of the parent in question ('top', 'left', 'right', 'bottom')
* @return {HTMLElement} The parent scroll element that the el's side is scrolled past, or null if there is no such element
*/
function isScrolledPast(el, elSide, parentSide) {
let parent = getParentAutoScrollElement(el, true),
elSideVal = getRect(el)[elSide];
/* jshint boss:true */
while (parent) {
let parentSideVal = getRect(parent)[parentSide],
visible;
if (parentSide === 'top' || parentSide === 'left') {
visible = elSideVal >= parentSideVal;
} else {
visible = elSideVal <= parentSideVal;
}
if (!visible) return parent;
if (parent === getWindowScrollingElement()) break;
parent = getParentAutoScrollElement(parent, false);
}
return false;
}
/**
* Gets nth child of el, ignoring hidden children, sortable's elements (does not ignore clone if it's visible)
* and non-draggable elements
* @param {HTMLElement} el The parent element
* @param {Number} childNum The index of the child
* @param {Object} options Parent Sortable's options
* @return {HTMLElement} The child at index childNum, or null if not found
*/
function getChild(el, childNum, options) {
let currentChild = 0,
i = 0,
children = el.children;
while (i < children.length) {
if (
children[i].style.display !== 'none' &&
children[i] !== Sortable.ghost &&
children[i] !== Sortable.dragged &&
closest(children[i], options.draggable, el, false)
) {
if (currentChild === childNum) {
return children[i];
}
currentChild++;
}
i++;
}
return null;
}
/**
* Gets the last child in the el, ignoring ghostEl or invisible elements (clones)
* @param {HTMLElement} el Parent element
* @param {selector} selector Any other elements that should be ignored
* @return {HTMLElement} The last child, ignoring ghostEl
*/
function lastChild(el, selector) {
let last = el.lastElementChild;
while (
last &&
(
last === Sortable.ghost ||
css(last, 'display') === 'none' ||
selector && !matches(last, selector)
)
) {
last = last.previousElementSibling;
}
return last || null;
}
/**
* Returns the index of an element within its parent for a selected set of
* elements
* @param {HTMLElement} el
* @param {selector} selector
* @return {number}
*/
function index(el, selector) {
let index = 0;
if (!el || !el.parentNode) {
return -1;
}
/* jshint boss:true */
while (el = el.previousElementSibling) {
if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && el !== Sortable.clone && (!selector || matches(el, selector))) {
index++;
}
}
return index;
}
/**
* Returns the scroll offset of the given element, added with all the scroll offsets of parent elements.
* The value is returned in real pixels.
* @param {HTMLElement} el
* @return {Array} Offsets in the format of [left, top]
*/
function getRelativeScrollOffset(el) {
let offsetLeft = 0,
offsetTop = 0,
winScroller = getWindowScrollingElement();
if (el) {
do {
let elMatrix = matrix(el),
scaleX = elMatrix.a,
scaleY = elMatrix.d;
offsetLeft += el.scrollLeft * scaleX;
offsetTop += el.scrollTop * scaleY;
} while (el !== winScroller && (el = el.parentNode));
}
return [offsetLeft, offsetTop];
}
/**
* Returns the index of the object within the given array
* @param {Array} arr Array that may or may not hold the object
* @param {Object} obj An object that has a key-value pair unique to and identical to a key-value pair in the object you want to find
* @return {Number} The index of the object in the array, or -1
*/
function indexOfObject(arr, obj) {
for (let i in arr) {
if (!arr.hasOwnProperty(i)) continue;
for (let key in obj) {
if (obj.hasOwnProperty(key) && obj[key] === arr[i][key]) return Number(i);
}
}
return -1;
}
function getParentAutoScrollElement(el, includeSelf) {
// skip to window
if (!el || !el.getBoundingClientRect) return getWindowScrollingElement();
let elem = el;
let gotSelf = false;
do {
// we don't need to get elem css if it isn't even overflowing in the first place (performance)
if (elem.clientWidth < elem.scrollWidth || elem.clientHeight < elem.scrollHeight) {
let elemCSS = css(elem);
if (
elem.clientWidth < elem.scrollWidth && (elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll') ||
elem.clientHeight < elem.scrollHeight && (elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll')
) {
if (!elem.getBoundingClientRect || elem === document.body) return getWindowScrollingElement();
if (gotSelf || includeSelf) return elem;
gotSelf = true;
}
}
/* jshint boss:true */
} while (elem = elem.parentNode);
return getWindowScrollingElement();
}
function extend(dst, src) {
if (dst && src) {
for (let key in src) {
if (src.hasOwnProperty(key)) {
dst[key] = src[key];
}
}
}
return dst;
}
function isRectEqual(rect1, rect2) {
return Math.round(rect1.top) === Math.round(rect2.top) &&
Math.round(rect1.left) === Math.round(rect2.left) &&
Math.round(rect1.height) === Math.round(rect2.height) &&
Math.round(rect1.width) === Math.round(rect2.width);
}
let _throttleTimeout;
function throttle(callback, ms) {
return function () {
if (!_throttleTimeout) {
let args = arguments,
_this = this;
if (args.length === 1) {
callback.call(_this, args[0]);
} else {
callback.apply(_this, args);
}
_throttleTimeout = setTimeout(function () {
_throttleTimeout = void 0;
}, ms);
}
};
}
function cancelThrottle() {
clearTimeout(_throttleTimeout);
_throttleTimeout = void 0;
}
function scrollBy(el, x, y) {
el.scrollLeft += x;
el.scrollTop += y;
}
function clone(el) {
let Polymer = window.Polymer;
let $ = window.jQuery || window.Zepto;
if (Polymer && Polymer.dom) {
return Polymer.dom(el).cloneNode(true);
}
else if ($) {
return $(el).clone(true)[0];
}
else {
return el.cloneNode(true);
}
}
function setRect(el, rect) {
css(el, 'position', 'absolute');
css(el, 'top', rect.top);
css(el, 'left', rect.left);
css(el, 'width', rect.width);
css(el, 'height', rect.height);
}
function unsetRect(el) {
css(el, 'position', '');
css(el, 'top', '');
css(el, 'left', '');
css(el, 'width', '');
css(el, 'height', '');
}
const expando = 'Sortable' + (new Date).getTime();
export {
on,
off,
matches,
getParentOrHost,
closest,
toggleClass,
css,
matrix,
find,
getWindowScrollingElement,
getRect,
isScrolledPast,
getChild,
lastChild,
index,
getRelativeScrollOffset,
indexOfObject,
getParentAutoScrollElement,
extend,
isRectEqual,
throttle,
cancelThrottle,
scrollBy,
clone,
setRect,
unsetRect,
expando
};