@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
566 lines (565 loc) • 22 kB
JavaScript
import "./kendo.core-B45DfgdW.js";
import "./kendo.userevents.js";
//#region ../src/kendo.selectable.js
const __meta__ = {
id: "selectable",
name: "Selectable",
category: "framework",
depends: ["core", "userevents"],
advanced: true
};
(function($, undefined) {
let kendo = window.kendo, Widget = kendo.ui.Widget, support = kendo.support, abs = Math.abs, ARIASELECTED = "aria-selected", SELECTED = "k-selected", ACTIVE = "k-selecting", SELECTABLE = "k-selectable", CHANGE = "change", CHANGING = "changing", NS = ".kendoSelectable", UNSELECT = "unselect", UNSELECTING = "k-unselecting", HOVER = "k-hover", MID = "k-range-mid", SPLITEND = "k-range-split-end", SPLITSTART = "k-range-split-start", START = "start", END = "end", NONE = "none", MOUSEENTER = "mouseenter", MOUSELEAVE = "mouseleave", TOUCHSTART = "touchstart", TOUCHEND = "touchend", INPUTSELECTOR = `input,a,textarea,.k-multiselect-wrap,select,button,.k-icon,.k-svg-icon,.k-svg-icon *,.k-button>span,.k-button>span *,.k-button>img,label.k-checkbox-label.k-no-text,span.k-numeric-wrap,.k-focusable`, msie = kendo.support.browser.msie, supportEventDelegation = false, extend = $.extend;
(function($) {
(function() {
$("<div class=\"parent\"><span></span></div>").on("click", ">*", function() {
supportEventDelegation = true;
}).find("span").trigger("click").end().off();
})();
})($);
const Selectable = Widget.extend({
init: function(element, options) {
let that = this, multiple, dragToSelect, selectableClass;
Widget.fn.init.call(that, element, options);
selectableClass = that.selectableClass = that.options.selectableClass || SELECTABLE;
that._marquee = $("<div class='k-marquee'><div class='k-marquee-color'></div></div>");
that._lastActive = null;
that.element.addClass(selectableClass);
that.relatedTarget = that.options.relatedTarget;
multiple = that.options.multiple;
dragToSelect = that.options.dragToSelect;
that.userEvents = new kendo.UserEvents(that.element, {
global: true,
allowSelection: true,
filter: (!supportEventDelegation ? "." + selectableClass + " " : "") + that.options.filter,
tap: that._tap.bind(that),
touchAction: multiple ? "none" : "pan-x pan-y"
});
if (multiple) {
if (dragToSelect) that.userEvents.bind("hold", that._hold.bind(that)).bind("start", that._start.bind(that)).bind("move", that._move.bind(that)).bind("end", that._end.bind(that));
that.userEvents.bind("select", that._select.bind(that));
}
Widget.fn.endInit.call(that);
},
events: [
CHANGE,
CHANGING,
UNSELECT
],
options: {
name: "Selectable",
filter: ">*",
inputSelectors: INPUTSELECTOR,
multiple: false,
holdToDrag: false,
dragToSelect: true,
relatedTarget: $.noop,
ignoreOverlapped: false,
addIdToRanges: false,
toggleable: false,
selectableClass: ""
},
_isElement: function(target) {
var elements = this.element;
var idx, length = elements.length, result = false;
target = target[0];
for (idx = 0; idx < length; idx++) if (elements[idx] === target) {
result = true;
break;
}
return result;
},
_tap: function(e) {
let target = $(e.target), that = this, options = that.options, ctrlKey = e.event.ctrlKey || e.event.metaKey, multiple = that.options.multiple, shiftKey = multiple && e.event.shiftKey, selectedClass = that.options.selectedClass || SELECTED, selected, whichCode = e.event.which, buttonCode = e.event.button;
if (!that._isElement(target.closest("." + that.selectableClass)) || whichCode && whichCode == 3 || buttonCode && buttonCode == 2) return;
if (!this._allowSelection(e.event.target)) return;
if (that.trigger(CHANGING, {
target,
originalEvent: e.event
})) return;
selected = target.hasClass(selectedClass);
target = target.add(that.relatedTarget(target));
if (!multiple) {
if (selected && ctrlKey) {
that._unselect(target);
that._notify(CHANGE, e);
} else if (!selected) {
that.clear();
that.value(target, e);
that._notify(CHANGE, e);
}
} else if (shiftKey) {
if (!that._lastRange || !compareElements(that._lastRange, target)) {
that.selectRange(that._firstSelectee(), target, e);
that._notify(CHANGE, e);
}
that._lastRange = target;
} else {
that._lastRange = null;
if (selected && (ctrlKey || options.toggleable)) {
that._unselect(target);
that._notify(CHANGE, e);
} else if (ctrlKey || options.toggleable) {
that.value(target, e);
that._notify(CHANGE, e);
} else if (!selected || that.value().length > 1) {
that.clear();
that.value(target, e);
that._notify(CHANGE, e);
}
that._lastActive = that._downTarget = target;
}
},
_hold: function(e) {
if (this.options.holdToDrag) this._tap(e);
this._activated = true;
},
_isActivated: function() {
return this.options.holdToDrag ? this._activated : true;
},
_start: function(e) {
let that = this, target = $(e.target), selectedClass = that.options.selectedClass || SELECTED, selected = target.hasClass(selectedClass), currentElement, ctrlKey = e.event.ctrlKey || e.event.metaKey;
if (!that._isActivated() || !this._allowSelection(e.event.target)) return;
if (that.trigger(CHANGING, {
target,
originalEvent: e.event
})) {
that.userEvents.cancel();
return;
}
that._downTarget = target;
if (!that._isElement(target.closest("." + that.selectableClass))) {
that.userEvents.cancel();
return;
}
if (that.options.useAllItems) that._items = that.element.find(that.options.filter);
else {
currentElement = target.closest(that.element);
that._items = currentElement.find(that.options.filter);
}
e.sender.capture();
that._marquee.appendTo(document.body).css({
left: e.x.client + 1,
top: e.y.client + 1,
width: 0,
height: 0
});
if (!ctrlKey) that.clear();
target = target.add(that.relatedTarget(target));
if (selected) {
that._selectElement(target, true);
if (ctrlKey) target.addClass(UNSELECTING);
}
},
_move: function(e) {
var that = this, position = {
left: e.x.startLocation > e.x.location ? e.x.location : e.x.startLocation,
top: e.y.startLocation > e.y.location ? e.y.location : e.y.startLocation,
width: abs(e.x.initialDelta),
height: abs(e.y.initialDelta)
};
if (!that._isActivated()) return;
that._marquee.css(position);
that._invalidateSelectables(position, e.event.ctrlKey || e.event.metaKey);
e.preventDefault();
},
_end: function(e) {
var that = this, rangeSelectedAttr = kendo.attr("range-selected"), uid = kendo.guid();
if (!that._isActivated()) return;
that._activated = false;
that._marquee.remove();
that._unselect(that.element.find(that.options.filter + ".k-unselecting")).removeClass(UNSELECTING);
var target = that.element.find(that.options.filter + ".k-selecting");
target = target.add(that.relatedTarget(target));
if (that.options.addIdToRanges) for (var i = 0; i < that._currentlyActive.length; i++) $(that._currentlyActive[i]).attr(rangeSelectedAttr, uid);
if (!that._lastRange || !compareElements(that._lastRange, target)) {
that.value(target, e);
that._notify(CHANGE, e);
}
that._lastRange = target;
that._lastActive = that._downTarget;
that._items = null;
},
_invalidateSelectables: function(position, ctrlKey) {
var idx, length, target = this._downTarget[0], items = this._items, selectedClass = this.options.selectedClass || SELECTED, related, toSelect;
this._currentlyActive = [];
for (idx = 0, length = items.length; idx < length; idx++) {
toSelect = items.eq(idx);
related = toSelect.add(this.relatedTarget(toSelect));
if (collision(toSelect, position)) {
if (toSelect.hasClass(selectedClass)) {
if (ctrlKey && target !== toSelect[0]) related.removeClass(selectedClass).addClass(UNSELECTING);
} else if (!toSelect.hasClass(ACTIVE) && !toSelect.hasClass(UNSELECTING) && !this._collidesWithActiveElement(related, position)) related.addClass(ACTIVE);
this._currentlyActive.push(related[0]);
} else if (toSelect.hasClass(ACTIVE)) related.removeClass(ACTIVE);
else if (ctrlKey && toSelect.hasClass(UNSELECTING)) related.removeClass(UNSELECTING).addClass(selectedClass);
}
},
_collidesWithActiveElement: function(element, marqueeRect) {
if (!this.options.ignoreOverlapped) return false;
var activeElements = this._currentlyActive;
var elemRect = element[0].getBoundingClientRect();
var activeElementRect;
var collision = false;
var leftRight = kendo.support.isRtl(element) ? "right" : "left";
var tempRect = {};
marqueeRect.right = marqueeRect.left + marqueeRect.width;
marqueeRect.bottom = marqueeRect.top + marqueeRect.height;
for (var i = 0; i < activeElements.length; i++) {
activeElementRect = activeElements[i].getBoundingClientRect();
if (overlaps(elemRect, activeElementRect)) {
tempRect[leftRight] = leftRight === "left" ? activeElementRect.right : activeElementRect.left;
elemRect = extend({}, elemRect, tempRect);
if (elemRect.left > elemRect.right) return true;
collision = !overlaps(elemRect, marqueeRect);
}
}
return collision;
},
value: function(val) {
var that = this, selectElement = that._selectElement.bind(that);
if (val) {
val.each(function() {
selectElement(this);
});
return;
}
return that.element.find(that.options.filter + "." + (that.options.selectedClass || SELECTED));
},
selectedRanges: function() {
var that = this;
var rangeSelectedAttr = kendo.attr("range-selected");
var map = {};
that.element.find("[" + rangeSelectedAttr + "]").each(function(_, elem) {
var rangeId = $(elem).attr(rangeSelectedAttr);
var mapLocation = map[rangeId];
if (!mapLocation) mapLocation = map[rangeId] = [];
mapLocation.push($(elem));
});
return map;
},
selectedSingleItems: function() {
var that = this;
var rangeSelectedAttr = kendo.attr("range-selected");
return that.element.find(that.options.filter + "." + (that.options.selectedClass || SELECTED) + ":not([" + rangeSelectedAttr + "])").toArray().map(function(elem) {
return $(elem);
});
},
_firstSelectee: function() {
var that = this, selected;
if (that._lastActive !== null) return that._lastActive;
selected = that.value();
return selected.length > 0 ? selected[0] : that.element.find(that.options.filter)[0];
},
_selectElement: function(element, preventNotify) {
var toSelect = $(element), selectedClass = this.options.selectedClass || SELECTED, isPrevented = !preventNotify && this._notify("select", { element });
toSelect.removeClass(ACTIVE);
if (!isPrevented) {
toSelect.addClass(selectedClass);
if (this.options.aria) toSelect.not("[role=\"none\"]").attr(ARIASELECTED, true);
}
},
_notify: function(name, args) {
args = args || {};
return this.trigger(name, args);
},
_unselect: function(element) {
if (this.trigger(UNSELECT, { element })) return;
var rangeSelectedAttr = kendo.attr("range-selected");
element.removeClass(this.options.selectedClass || SELECTED).removeAttr(rangeSelectedAttr);
if (this.options.aria) element.not("[role=\"none\"]").attr(ARIASELECTED, false);
return element;
},
_select: function(e) {
if (this._allowSelection(e.event.target)) {
if (!msie || msie && !$(kendo._activeElement()).is(this.options.inputSelectors)) {
if (this._allowPreventDefault(e.event.target)) e.preventDefault();
}
}
},
_allowPreventDefault: function(target) {
return !$(target).is(".k-table-td") || !this.options.allowPaste;
},
_allowSelection: function(target) {
if ($(target).is(this.options.inputSelectors)) {
this.userEvents.cancel();
this._downTarget = null;
return false;
}
return true;
},
resetTouchEvents: function() {
this.userEvents.cancel();
},
clear: function() {
var items = this.element.find(this.options.filter + "." + (this.options.selectedClass || SELECTED));
this._unselect(items);
},
selectRange: function(start, end) {
var that = this, idx, tmp, items;
that.clear();
if (that.element.length > 1) items = that.options.continuousItems();
if (!items || !items.length) items = that.element.find(that.options.filter);
start = $.inArray($(start)[0], items);
end = $.inArray($(end)[0], items);
if (start > end) {
tmp = start;
start = end;
end = tmp;
}
if (!that.options.useAllItems) end += that.element.length - 1;
for (idx = start; idx <= end; idx++) that._selectElement(items[idx], true);
},
destroy: function() {
var that = this;
Widget.fn.destroy.call(that);
that.element.off(NS);
that.userEvents.destroy();
that._marquee = that._lastActive = that.element = that.userEvents = null;
}
});
const RangeSelectable = Widget.extend({
init: function(element, options) {
let that = this, ns, cellSelectorValid;
Widget.fn.init.call(that, element, options);
that.widget = options.widget;
ns = options.ns;
cellSelectorValid = options.filter;
that.userEvents = new kendo.UserEvents(that.element, {
global: true,
allowSelection: true,
filter: that.options.filter,
tap: that._tap.bind(that),
touchAction: NONE
});
if (support.touch) element.on(TOUCHSTART + ns, cellSelectorValid, that._mouseEnter.bind(that)).on(TOUCHEND + ns + " touchmove" + ns, cellSelectorValid, that._mouseLeave.bind(that));
else element.on(MOUSEENTER + ns, cellSelectorValid, that._mouseEnter.bind(that)).on(MOUSELEAVE + ns, cellSelectorValid, that._mouseLeave.bind(that));
Widget.fn.endInit.call(this);
},
events: [CHANGE],
options: {
name: "RangeSelectable",
filter: ">*",
inputSelectors: INPUTSELECTOR,
resetOnStart: false,
multiple: false,
dragToSelect: true,
cellSelector: "*",
ns: "",
reverse: false,
relatedTarget: $.noop
},
destroy: function() {
let that = this;
Widget.fn.destroy.call(that);
that.userEvents.destroy();
that.widget = null;
that._lastActive = that.element = that.userEvents = that._start = that._end = null;
},
_allowSelection: function(target) {
if ($(target).is(this.options.inputSelectors)) {
this.userEvents.cancel();
return false;
}
return true;
},
_mouseEnter: function(e) {
let that = this, cell = $(e.currentTarget), range;
cell.addClass(HOVER);
range = that.widget?.selectRange();
if (!range) return;
if (that.options.resetOnStart && range.end) return;
if (range.target === START && that._end) that.range(cell, that._end, true, that.options.reverse);
if (range.target === END) that.range(that._start, cell, true, that.options.reverse);
},
_mouseLeave: function(e) {
$(e.currentTarget).removeClass(HOVER);
},
start: function(element, preventFlagUpdate) {
if (element === undefined || element === null) return this._start;
element.addClass("k-selected k-range-start");
element.not("[role=\"none\"]").attr(ARIASELECTED, true);
if (!preventFlagUpdate) this._start = element;
},
end: function(element, preventFlagUpdate) {
if (element === undefined || element === null) return this._start;
element.addClass("k-selected k-range-end");
element.not("[role=\"none\"]").attr(ARIASELECTED, true);
if (!preventFlagUpdate) this._end = element;
},
mid: function(elements) {
let tables = this.element.find("table"), options = this.options;
elements.addClass(MID);
elements.not("[role=\"none\"]").attr(ARIASELECTED, true);
tables.each(function() {
let that = $(this);
let lastCell = that.find(options.cellSelectorValid).last();
let firstCell = that.find(options.cellSelectorValid).first();
if (lastCell.hasClass(MID)) lastCell.addClass(SPLITEND);
if (firstCell.hasClass(MID)) firstCell.addClass(SPLITSTART);
});
},
clear: function(clearVariables) {
let options = this.options;
this.element.find(options.cellSelector).removeClass("k-range-mid k-range-split-end k-range-split-start");
this.clearStartEnd();
if (clearVariables) this._start = this._end = null;
},
clearStartEnd: function() {
let that = this, options = that.options;
that.element.find(options.cellSelector).removeClass("k-range-start k-selected k-range-end").removeAttr(ARIASELECTED);
},
selectFrom: function(start) {
let that = this, options = this.options, items, startIdx;
items = that.element.find(options.cellSelector);
startIdx = $.inArray($(start)[0], items);
that.clear();
that.start(start);
items = items.filter(function(index) {
return index > startIdx;
});
that.mid(items);
},
selectTo: function(end) {
let that = this, options = this.options, items, endIdx;
items = that.element.find(options.cellSelector);
endIdx = $.inArray($(end)[0], items);
that.clear();
items = items.filter(function(index) {
return index < endIdx;
});
that.mid(items);
that.end($(end));
},
range: function(start, end, preventFlagUpdate, invert) {
let that = this, options = this.options, items, startIdx, endIdx;
if (start === undefined) return {
start: that._start,
end: that._end
};
that._clearFlags();
items = that.element.find(options.cellSelector);
startIdx = $.inArray($(start)[0], items);
endIdx = $.inArray($(end)[0], items);
if (!start || start && !start.length) that._useStart = true;
that.clear();
if (start) that.start($(start), preventFlagUpdate);
items = items.filter(function(index) {
if (endIdx < 0 || !start && startIdx < 0) return;
return index > startIdx && index < endIdx || options.reverse && index < startIdx && index > endIdx;
});
that.mid(items);
if (end) that.end($(end), preventFlagUpdate);
else that._useEnd = true;
if (startIdx > endIdx && invert) {
that.clearStartEnd();
that.start($(end), true);
that.end($(start), true);
}
},
change: function() {
this.trigger(CHANGE);
},
_clearFlags: function() {
this._useStart = this._useEnd = false;
},
_tap: function(e) {
let target = $(e.target), that = this, range = that.widget.selectRange() || {}, start = range.start, end = range.end, currentDate = kendo.calendar.toDateObject($(target).find("span")), options = that.options, resetOnStart = options.resetOnStart;
that._lastActive = target;
if (!start && !end) {
that.clear(true);
if (range.target === START) that.start(target);
if (range.target === END) that.end(target);
that._clearFlags();
that.trigger(CHANGE);
return;
}
if (!start && end) {
if (range.target === END) that.end(target);
else if (+currentDate > +range.end && !options.reverse) {
that.clear(true);
that.start(target);
} else that.range(target, that._end, false, true);
that.trigger(CHANGE);
that._clearFlags();
return;
}
if (start && !end) {
if (range.target === END && !options.reverse && +start > +currentDate) {
that.clear(true);
if (resetOnStart) that.start(target);
else that.end(target);
} else if (range.target === START) that.start(target);
else that.range(that._start, target, false, true);
that.trigger(CHANGE);
that._clearFlags();
return;
}
if (start && end) {
if (!options.reverse) {
if (+start > +currentDate && range.target === END) {
that.clear(true);
if (resetOnStart) {
that.start(target);
that.end(null);
range.target = START;
} else {
that.start(null);
that.end(target);
}
that.trigger(CHANGE);
return;
}
if (+start < +currentDate && range.target === START) {
if (+currentDate > +range.end) {
that.clear(true);
that.start(target);
that.end(null);
} else that.range(target, that._end);
that.trigger(CHANGE);
return;
}
if (range.target === START) that.range(target, that._end);
if (range.target === END) that.range(that._start, target);
that.trigger(CHANGE);
return;
}
if (resetOnStart) if (range.target === START) that.range(target, that._end, false, true);
else that.range(that._start, target, false, true);
else if (range.target === START) that.start(target);
else that.end(target);
that.trigger(CHANGE);
}
}
});
Selectable.parseOptions = function(selectable) {
var selectableMode = selectable.mode || selectable;
var asLowerString = typeof selectableMode === "string" && selectableMode.toLowerCase();
return {
multiple: asLowerString && asLowerString.indexOf("multiple") > -1,
cell: asLowerString && asLowerString.indexOf("cell") > -1,
range: asLowerString && asLowerString.indexOf("range") > -1,
single: asLowerString && asLowerString.indexOf("single") > -1
};
};
function compareElements(element, toCompare) {
if (element.length !== toCompare.length) return false;
for (var i = 0; i < element.length; i++) if (element[i] !== toCompare[i]) return false;
return true;
}
function collision(element, position) {
if (!element.is(":visible")) return false;
var elementPosition = kendo.getOffset(element), right = position.left + position.width, bottom = position.top + position.height;
elementPosition.right = elementPosition.left + kendo._outerWidth(element);
elementPosition.bottom = elementPosition.top + kendo._outerHeight(element);
return !(elementPosition.left > right || elementPosition.right < position.left || elementPosition.top > bottom || elementPosition.bottom < position.top);
}
function overlaps(firstRect, secondRect) {
return !(firstRect.right <= secondRect.left || firstRect.left >= secondRect.right || firstRect.bottom <= secondRect.top || firstRect.top >= secondRect.bottom);
}
kendo.ui.plugin(Selectable);
kendo.ui.plugin(RangeSelectable);
})(window.kendo.jQuery);
var kendo_selectable_default = kendo;
//#endregion
export { __meta__, kendo_selectable_default as default };