Clean select code.

This commit is contained in:
Lars Jung 2016-06-29 01:55:28 +02:00
parent d0b6197aec
commit 749c8b29c9
3 changed files with 110 additions and 97 deletions

View file

@ -1,5 +1,4 @@
#selection-rect { #selection-rect {
display: none;
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;

View file

@ -1,5 +1,5 @@
const {each, map} = require('../util'); const {each, dom} = require('../util');
const {win, jq} = require('../globals'); const {win} = require('../globals');
const event = require('../core/event'); const event = require('../core/event');
const resource = require('../core/resource'); const resource = require('../core/resource');
const allsettings = require('../core/settings'); const allsettings = require('../core/settings');
@ -15,39 +15,37 @@ const selectorTpl =
`<span class="selector"> `<span class="selector">
<img src="${resource.image('selected')}" alt="selected"/> <img src="${resource.image('selected')}" alt="selected"/>
</span>`; </span>`;
let x = 0; const $document = dom(doc);
let y = 0; const $html = dom('html');
let l = 0; const $selectionRect = dom('<div id="selection-rect"></div>');
let t = 0;
let w = 0; let dragStartX = 0;
let h = 0; let dragStartY = 0;
let isDragSelect; let isDragSelect = false;
let isCtrlPressed; let isCtrlPressed = false;
const shrink = 1 / 3;
const $document = jq(doc);
const $html = jq('html');
const $selectionRect = jq('<div id="selection-rect"/>');
function publish() { const publish = () => {
const items = map(jq('#items .item.selected'), el => el._item); const items = dom('#items .item.selected').map(el => el._item);
event.pub('selection', items); event.pub('selection', items);
} };
function elementRect($element) { const elementRect = $el => {
if (!$element.is(':visible')) { if ($el.isHidden()) {
return null; return null;
} }
const offset = $element.offset(); const rect = $el[0].getBoundingClientRect();
const elL = offset.left; // const rect = {left: 0, top: 0, right: 10, bottom: 10};
const elT = offset.top; return {
const elW = $element.outerWidth(); l: rect.left,
const elH = $element.outerHeight(); t: rect.top,
return {l: elL, t: elT, w: elW, h: elH, r: elL + elW, b: elT + elH}; r: rect.right,
} b: rect.bottom
};
};
function doOverlap(rect1, rect2) { const doOverlap = (rect1, rect2) => {
if (!rect1 || !rect2) { if (!rect1 || !rect2) {
return false; return false;
} }
@ -60,104 +58,106 @@ function doOverlap(rect1, rect2) {
const height = bottom - top; const height = bottom - top;
return width >= 0 && height >= 0; return width >= 0 && height >= 0;
} };
function selectionUpdate(ev) { const selectionUpdate = ev => {
l = Math.min(x, ev.pageX); const left = Math.min(dragStartX, ev.pageX);
t = Math.min(y, ev.pageY); const top = Math.min(dragStartY, ev.pageY);
w = Math.abs(x - ev.pageX); const width = Math.abs(dragStartX - ev.pageX);
h = Math.abs(y - ev.pageY); const height = Math.abs(dragStartY - ev.pageY);
if (!isDragSelect && w < 4 && h < 4) { if (!isDragSelect && width < 4 && height < 4) {
return; return;
} }
if (!isDragSelect && !isCtrlPressed) { if (!isDragSelect && !isCtrlPressed) {
jq('#items .item').removeClass('selected'); dom('#items .item').rmCls('selected');
publish(); publish();
} }
isDragSelect = true; isDragSelect = true;
$html.addClass('drag-select'); $html.addCls('drag-select');
ev.preventDefault(); $selectionRect.show();
$selectionRect const style = $selectionRect[0].style;
.stop(true, true) style.left = left + 'px';
.css({left: l, top: t, width: w, height: h, opacity: 1}) style.top = top + 'px';
.show(); style.width = width + 'px';
style.height = height + 'px';
const selRect = elementRect($selectionRect); const selRect = elementRect($selectionRect);
jq('#items .item').removeClass('selecting').each((idx, el) => { dom('#items .item').rmCls('selecting').each(el => {
const $item = jq(el); const $item = dom(el);
const inter = doOverlap(selRect, elementRect($item.find('a'))); const inter = doOverlap(selRect, elementRect($item.find('a')));
if (inter && !$item.hasClass('folder-parent')) { if (inter && !$item.hasCls('folder-parent')) {
$item.addClass('selecting'); $item.addCls('selecting');
} }
}); });
}
function selectionEnd(ev) { ev.preventDefault();
$document.off('mousemove', selectionUpdate); };
const selectionEnd = ev => {
$document
.off('mousemove', selectionUpdate)
.off('mouseup', selectionEnd);
if (!isDragSelect) { if (!isDragSelect) {
return; return;
} }
ev.preventDefault(); dom('#items .item.selecting.selected').rmCls('selecting').rmCls('selected');
jq('#items .item.selecting.selected').removeClass('selecting').removeClass('selected'); dom('#items .item.selecting').rmCls('selecting').addCls('selected');
jq('#items .item.selecting').removeClass('selecting').addClass('selected');
publish(); publish();
$html.removeClass('drag-select'); $html.rmCls('drag-select');
$selectionRect $selectionRect.hide();
.stop(true, true)
.animate({
left: l + w * 0.5 * shrink,
top: t + h * 0.5 * shrink,
width: w * (1 - shrink),
height: h * (1 - shrink),
opacity: 0
},
300,
() => {
$selectionRect.hide();
});
}
function selectionStart(ev) { ev.preventDefault();
// only on left button and don't block scrollbar };
if (ev.button !== 0 || ev.offsetX >= jq('#content').width() - 14) {
const selectionStart = ev => {
// only start on left button, don't block scrollbar
if (ev.button !== 0 || ev.offsetX >= dom('#content')[0].offsetWidth - 14) {
return; return;
} }
isDragSelect = false; isDragSelect = false;
isCtrlPressed = ev.ctrlKey || ev.metaKey; isCtrlPressed = ev.ctrlKey || ev.metaKey;
x = ev.pageX; dragStartX = ev.pageX;
y = ev.pageY; dragStartY = ev.pageY;
$document $document
.on('mousemove', selectionUpdate) .on('mousemove', selectionUpdate)
.one('mouseup', selectionEnd); .on('mouseup', selectionEnd);
}
function onSelectorClick(ev) {
ev.stopImmediatePropagation();
ev.preventDefault(); ev.preventDefault();
};
jq(ev.target).closest('.item').toggleClass('selected'); const closestItem = el => {
publish(); while (!el._item && el.parentNode) {
} el = el.parentNode;
function addCheckbox(item) {
if (item.$view && !item.isCurrentParentFolder()) {
jq(selectorTpl)
.on('click', onSelectorClick)
.appendTo(item.$view.find('a'));
} }
} return el._item;
};
function onViewChanged(added, removed) { const onSelectorClick = ev => {
closestItem(ev.target).$view.tglCls('selected');
publish();
ev.preventDefault();
ev.stopPropagation();
};
const addCheckbox = item => {
if (item.$view && !item.isCurrentParentFolder()) {
dom(selectorTpl)
.on('click', onSelectorClick)
.appTo(item.$view.find('a'));
}
};
const onViewChanged = (added, removed) => {
if (settings.checkboxes) { if (settings.checkboxes) {
each(added, addCheckbox); each(added, addCheckbox);
} }
@ -169,9 +169,9 @@ function onViewChanged(added, removed) {
}); });
publish(); publish();
} };
function init() { const init = () => {
if (!settings.enabled || !settings.clickndrag && !settings.checkboxes) { if (!settings.enabled || !settings.clickndrag && !settings.checkboxes) {
return; return;
} }
@ -179,20 +179,18 @@ function init() {
event.sub('view.changed', onViewChanged); event.sub('view.changed', onViewChanged);
if (settings.clickndrag) { if (settings.clickndrag) {
$selectionRect.hide().appendTo('body'); $selectionRect.hide().appTo('body');
jq('#content') dom('#content')
.on('mousedown', selectionStart) .on('mousedown', selectionStart)
.on('drag dragstart', ev => { .on('drag', ev => ev.preventDefault())
ev.stopImmediatePropagation(); .on('dragstart', ev => ev.preventDefault())
ev.preventDefault();
})
.on('click', () => { .on('click', () => {
jq('#items .item').removeClass('selected'); dom('#items .item').rmCls('selected');
publish(); publish();
}); });
} }
} };
init(); init();

View file

@ -243,6 +243,18 @@ dom.prototype = {
}); });
}, },
tglCls(...names) {
return this.each(el => {
for (const name of names) {
if (el.classList.contains(name)) {
el.classList.remove(name);
} else {
el.classList.add(name);
}
}
});
},
parent() { parent() {
return dom(this.map(el => el.parentNode)); return dom(this.map(el => el.parentNode));
}, },
@ -257,6 +269,10 @@ dom.prototype = {
show() { show() {
return this.rmCls('hidden'); return this.rmCls('hidden');
},
isHidden() {
return this.hasCls('hidden');
} }
}; };