handleDragStart: function(ev) {
if (!scope.dragState || !scope.dragState.node.classList.contains('drag-handle')) {
scope.dragState = null;
- ev.preventDefault();
return false;
}
/** @private */
handleDragOver: function(ev) {
+ if (scope.dragState === null ) return;
var n = scope.dragState.targetNode,
r = scope.dragState.rect,
t = r.top + r.height / 2;
/** @private */
handleDragEnter: function(ev) {
+ if (scope.dragState === null ) return;
scope.dragState.rect = ev.currentTarget.getBoundingClientRect();
scope.dragState.targetNode = ev.currentTarget;
},
/** @private */
handleDrop: function(ev) {
+ if (scope.dragState === null ) return;
var s = scope.dragState;
if (s.node && s.targetNode) {
this.addItem(dl, this.values[i], label);
}
+ this.initDragAndDrop(dl);
+
return this.bind(dl);
},
+ /** @private */
+ initDragAndDrop: function(dl) {
+ let draggedItem = null;
+ let placeholder = null;
+
+ dl.addEventListener('dragstart', (e) => {
+ if (e.target.classList.contains('item')) {
+ draggedItem = e.target;
+ e.target.classList.add('dragging');
+ }
+ });
+
+ dl.addEventListener('dragend', (e) => e.target.classList.remove('dragging'));
+
+ dl.addEventListener('dragover', (e) => e.preventDefault());
+
+ dl.addEventListener('dragenter', (e) => e.target.classList.add('drag-over'));
+
+ dl.addEventListener('dragleave', (e) => e.target.classList.remove('drag-over'));
+
+ dl.addEventListener('drop', (e) => {
+ e.preventDefault();
+ e.target.classList.remove('drag-over');
+ const target = e.target.classList.contains('item') ? e.target : dl.querySelector('.add-item');
+ dl.insertBefore(draggedItem, target);
+ });
+
+ dl.addEventListener('click', (e) => {
+ if (e.target.closest('.item')) {
+ const span = e.target.closest('.item').querySelector('SPAN');
+ if (span) {
+ const range = document.createRange();
+ range.selectNodeContents(span);
+ const selection = window.getSelection();
+ if (selection.rangeCount === 0 || selection.toString().length === 0) {
+ selection.removeAllRanges();
+ selection.addRange(range);
+ } else selection.removeAllRanges();
+ }
+ }
+ });
+
+ dl.addEventListener('touchstart', (e) => {
+ const touch = e.touches[0];
+ const target = e.target.closest('.item');
+ if (target) {
+ draggedItem = target;
+
+ placeholder = draggedItem.cloneNode(true);
+ placeholder.className = 'placeholder';
+ placeholder.style.height = `${draggedItem.offsetHeight}px`;
+ draggedItem.parentNode.insertBefore(placeholder, draggedItem.nextSibling);
+ draggedItem.classList.add('dragging')
+ }
+ });
+
+ dl.addEventListener('touchmove', (e) => {
+ if (draggedItem) {
+ const touch = e.touches[0];
+ const currentY = touch.clientY;
+
+ const items = Array.from(dl.querySelectorAll('.item'));
+ const target = items.find(item => {
+ const rect = item.getBoundingClientRect();
+ return currentY > rect.top && currentY < rect.bottom;
+ });
+
+ if (target && target !== draggedItem) {
+ const insertBefore = currentY < target.getBoundingClientRect().top + target.offsetHeight / 2;
+ dl.insertBefore(placeholder, insertBefore ? target : target.nextSibling);
+ }
+
+ e.preventDefault();
+ }
+ });
+
+ dl.addEventListener('touchend', (e) => {
+ if (draggedItem && placeholder) {
+ dl.insertBefore(draggedItem, placeholder);
+ draggedItem.classList.remove('dragging')
+ placeholder.parentNode.removeChild(placeholder);
+ placeholder = null;
+ draggedItem = null;
+ }
+ });
+ },
+
/** @private */
bind: function(dl) {
dl.addEventListener('click', L.bind(this.handleClick, this));
/** @private */
addItem: function(dl, value, text, flash) {
var exists = false,
- new_item = E('div', { 'class': flash ? 'item flash' : 'item', 'tabindex': 0 }, [
+ new_item = E('div', { 'class': flash ? 'item flash' : 'item', 'tabindex': 0, 'draggable': true }, [
E('span', {}, [ text || value ]),
E('input', {
'type': 'hidden',
return;
if (item) {
- this.removeItem(dl, item);
+ // Get bounding rectangle of the item
+ var rect = item.getBoundingClientRect();
+
+ // Get computed styles for the ::after pseudo-element
+ var afterStyles = window.getComputedStyle(item, '::after');
+ var afterWidth = parseFloat(afterStyles.width) || 0;
+
+ // Check if the click is within the ::after region
+ if (rect.right - ev.clientX <= afterWidth) {
+ this.removeItem(dl, item);
+ }
}
else if (matchesElem(ev.target, '.cbi-button-add')) {
var input = ev.target.previousElementSibling;
border-radius: 3px;
color: var(--text-color-high);
position: relative;
- pointer-events: none;
+ pointer-events: auto; /* needed for drag-and-drop in UIDynamicList */
overflow: hidden;
word-break: break-all;
+ cursor: move; /* drag-and-drop */
+ user-select: text; /* text selection in drag-and-drop */
}
.cbi-dynlist > .item::after {
pointer-events: auto;
}
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .item.drag-over {
+ border-top: 1px solid var(--text-color-highest);
+}
+
+/* Make item being dragged in UIDynamicList partially transparent*/
+.cbi-dynlist > .item.dragging {
+ opacity: 0.5;
+}
+
+/* prevent pointer changing when over the span element in UIDynamicList */
+.cbi-dynlist > .item > span {
+ pointer-events: none;
+}
+
.cbi-dynlist > .add-item {
display: flex;
}
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .add-item > .cbi-input-text.drag-over {
+ border-top: 1px solid var(--text-color-highest);
+}
+
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .add-item > .cbi-button-add.drag-over {
+ border-top: 1px solid var(--text-color-highest);
+}
+
.cbi-dynlist > .add-item > input,
.cbi-dynlist > .add-item > button {
flex: 1 1 auto;
max-width: 25rem;
margin-right: 2em;
padding: .5em .25em .25em 0;
- pointer-events: none;
+ pointer-events: auto; /* needed for drag-and-drop in UIDynamicList */
color: #666;
border-bottom: 2px solid rgba(0, 0, 0, .26);
outline: 0;
+ cursor: move; /* drag-and-drop */
+ user-select: text; /* text selection in drag-and-drop */
}
.cbi-dynlist[name="sshkeys"] > .item {
background-color: var(--red-color-high);
}
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .item.drag-over {
+ border-top: 1px solid black;
+}
+
+/* Make item being dragged in UIDynamicList partially transparent*/
+.cbi-dynlist > .item.dragging {
+ opacity: 0.5;
+}
+
+/* prevent pointer changing when over the span element in UIDynamicList */
.cbi-dynlist > .item > span {
white-space: normal;
word-break: break-word;
+ pointer-events: none;
}
.cbi-dynlist > .add-item {
min-width: 16rem;
}
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .add-item > .cbi-input-text.drag-over {
+ border-top: 1px solid black;
+}
+
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .add-item > .cbi-button-add.drag-over {
+ border-top: 1px solid black;
+}
+
.cbi-dynlist > .add-item:not([ondrop]) > input {
overflow: hidden;
width: 100%;
position: relative;
overflow: hidden;
transition: box-shadow .25s ease-in-out;
- pointer-events: none;
+ pointer-events: auto; /* needed for drag-and-drop in UIDynamicList */
flex: 1 1 100%;
word-break: break-all;
+ cursor: move; /* drag-and-drop */
+ user-select: text; /* text selection in drag-and-drop */
}
.cbi-dynlist > .item::after {
pointer-events: all;
}
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .item.drag-over {
+ border-top: 1px solid black;
+}
+
+/* Make item being dragged in UIDynamicList partially transparent*/
+.cbi-dynlist > .item.dragging {
+ opacity: 0.5;
+}
+/* prevent pointer changing when over the span element in UIDynamicList */
+.cbi-dynlist > .item > span {
+ pointer-events: none;
+}
+
.cbi-dynlist[disabled] > .item::after {
pointer-events: none;
}
display: flex;
}
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .add-item > .cbi-input-text.drag-over {
+ border-top: 1px solid black;
+}
+
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .add-item > .cbi-button-add.drag-over {
+ border-top: 1px solid black;
+}
+
.cbi-dynlist > .add-item > input {
flex: 1;
min-width: 18.5rem;
border: 1px outset #000;
border-radius: 3px;
position: relative;
- pointer-events: none;
+ pointer-events: auto; /* needed for drag-and-drop in UIDynamicList */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+ cursor: move; /* drag-and-drop */
+ user-select: text; /* text selection in drag-and-drop */
}
.cbi-dynlist > .item::after {
height: auto;
}
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .item.drag-over {
+ border-top: 1px solid red;
+}
+
+/* Make item being dragged in UIDynamicList partially transparent*/
+.cbi-dynlist > .item.dragging {
+ opacity: 0.5;
+}
+
+/* prevent pointer changing when over the span element in UIDynamicList */
+.cbi-dynlist > .item > span {
+ pointer-events: none;
+}
+
.cbi-dynlist > .add-item {
display: flex;
}
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .add-item > .cbi-input-text.drag-over {
+ border-top: 1px solid re;
+}
+
+/* indication line for drag-and-drop in UIDynamicList*/
+.cbi-dynlist > .add-item > .cbi-button-add.drag-over {
+ border-top: 1px solid red;
+}
+
.cbi-dynlist > .add-item > input {
flex: 1 1 auto;
}