From 20fd66630bf452356eb53eadf1312c2b325da487 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 9 Jan 2020 14:43:18 -0500 Subject: [PATCH] Update semicombo reordering (re: #7024): Don't try reordering multiCombo fields Fix offset behavior for full-width semicombos Use grab/grabbing cursor style Keep dragged chip above others --- css/80_app.css | 18 ++- modules/ui/fields/combo.js | 244 +++++++++++++++++++------------------ 2 files changed, 138 insertions(+), 124 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 09b854e97..1b47274d2 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1557,20 +1557,28 @@ a.hide-toggle { margin-left: 6px; } -.form-field-input-multicombo li.chips { +.form-field-input-multicombo li.chip { background-color: #eff2f7; border: 1px solid #ccd5e3; line-height: 25px; max-width: 100%; } -[dir='ltr'] .form-field-input-multicombo li.chips { +[dir='ltr'] .form-field-input-multicombo li.chip { padding: 2px 0px 2px 5px; } -[dir='rtl'] .form-field-input-multicombo li.chips { +[dir='rtl'] .form-field-input-multicombo li.chip { padding: 2px 5px 2px 0px; } +.form-field-input-multicombo li.chip.draggable { + cursor: grab; +} +.form-field-input-multicombo li.chip.dragging { + opacity: 0.75; + z-index: 3000; + cursor: grabbing; +} -.form-field-input-multicombo li.chips span { +.form-field-input-multicombo li.chip span { display: block; flex: 1 1 auto; overflow: hidden; @@ -1604,7 +1612,7 @@ a.hide-toggle { border-radius: 4px !important; } -.form-field-input-multicombo .full-line-chips li.chips { +.form-field-input-multicombo .full-line-chips li.chip { width: 100%; } .form-field-input-multicombo .full-line-chips .input-wrap { diff --git a/modules/ui/fields/combo.js b/modules/ui/fields/combo.js index 3e7573d76..c9222d210 100644 --- a/modules/ui/fields/combo.js +++ b/modules/ui/fields/combo.js @@ -431,7 +431,7 @@ export function uiFieldCombo(field, context) { // Render chips - var chips = container.selectAll('.chips') + var chips = container.selectAll('.chip') .data(_multiData); chips.exit() @@ -439,125 +439,12 @@ export function uiFieldCombo(field, context) { var enter = chips.enter() .insert('li', '.input-wrap') - .attr('class', 'chips'); - - enter.style('cursor', 'move'); + .attr('class', 'chip') + .classed('draggable', isSemi); - // allow drag and drop re-ordering of chips - var dragOrigin, targetIndex; - enter.call(d3_drag() - .on('start', function() { - dragOrigin = { - x: d3_event.x, - y: d3_event.y - }; - targetIndex = null; - }) - .on('drag', function(d, index) { - var x = d3_event.x - dragOrigin.x, - y = d3_event.y - dragOrigin.y; - - if (!d3_select(this).classed('dragging') && - // don't display drag until dragging beyond a distance threshold - Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return; - - d3_select(this) - .classed('dragging', true); - - targetIndex = null; - var targetIndexOffsetTop = null; - var draggedTagWidth = d3_select(this).node().offsetWidth; - - if (field.id === 'destination_oneway') { // meaning tags are full width - container.selectAll('.chips') - .style('transform', function(d2, index2) { - var node = d3_select(this).node(); - - if (index === index2) { - return 'translate(' + x + 'px, ' + y + 'px)'; - // move the dragged tag up the order - } else if (index2 > index && d3_event.y > node.offsetTop - node.offsetHeight) { - if (targetIndex === null || index2 > targetIndex) { - targetIndex = index2; - } - return 'translateY(-100%)'; - // move the dragged tag down the order - } else if (index2 < index && d3_event.y < node.offsetTop) { - if (targetIndex === null || index2 < targetIndex) { - targetIndex = index2; - } - return 'translateY(100%)'; - } - return null; - }); - } else { - container.selectAll('.chips') - .each(function(d2, index2) { - var node = d3_select(this).node(); - - // check the cursor is in the bounding box - if ( - index !== index2 && - d3_event.x < node.offsetLeft + (node.offsetWidth / 2) && - d3_event.x > node.offsetLeft - (node.offsetWidth / 2) && - d3_event.y < node.offsetTop + node.offsetHeight && - d3_event.y > node.offsetTop - ) { - targetIndex = index2; - targetIndexOffsetTop = node.offsetTop; - } - }); - - container.selectAll('.chips') - .style('transform', function(d2, index2) { - var node = d3_select(this).node(); - - if (index === index2) { - return 'translate(' + x + 'px, ' + y + 'px)'; - } - - // only translate tags in the same row - if (node.offsetTop === targetIndexOffsetTop) { - if (index2 < index && index2 >= targetIndex) { - return 'translateX(' + draggedTagWidth + 'px)'; - } else if (index2 > index && index2 <= targetIndex) { - return 'translateX(-' + draggedTagWidth + 'px)'; - } - } - return null; - }); - } - }) - .on('end', function(d, index) { - if (!d3_select(this).classed('dragging')) { - return; - } - - d3_select(this) - .classed('dragging', false); - - container.selectAll('.chips') - .style('transform', null); - - if (targetIndex !== null) { - var element = _multiData[index]; - _multiData.splice(index, 1); - _multiData.splice(targetIndex, 0, element); - - var t = {}; - - if (_multiData.length) { - t[field.key] = _multiData.map(function(element) { - return element.key; - }).join(';'); - } else { - t[field.key] = undefined; - } - - dispatch.call('change', this, t); - } - }) - ); + if (isSemi) { // only semiCombo values are ordered + registerDragAndDrop(enter); + } enter.append('span'); enter.append('a'); @@ -577,6 +464,125 @@ export function uiFieldCombo(field, context) { } }; + function registerDragAndDrop(selection) { + + // allow drag and drop re-ordering of chips + var dragOrigin, targetIndex; + selection.call(d3_drag() + .on('start', function() { + dragOrigin = { + x: d3_event.x, + y: d3_event.y + }; + targetIndex = null; + }) + .on('drag', function(d, index) { + var x = d3_event.x - dragOrigin.x, + y = d3_event.y - dragOrigin.y; + + if (!d3_select(this).classed('dragging') && + // don't display drag until dragging beyond a distance threshold + Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return; + + d3_select(this) + .classed('dragging', true); + + targetIndex = null; + var targetIndexOffsetTop = null; + var draggedTagWidth = d3_select(this).node().offsetWidth; + + if (field.id === 'destination_oneway') { // meaning tags are full width + container.selectAll('.chip') + .style('transform', function(d2, index2) { + var node = d3_select(this).node(); + + if (index === index2) { + return 'translate(' + x + 'px, ' + y + 'px)'; + // move the dragged tag up the order + } else if (index2 > index && d3_event.y > node.offsetTop) { + if (targetIndex === null || index2 > targetIndex) { + targetIndex = index2; + } + return 'translateY(-100%)'; + // move the dragged tag down the order + } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) { + if (targetIndex === null || index2 < targetIndex) { + targetIndex = index2; + } + return 'translateY(100%)'; + } + return null; + }); + } else { + container.selectAll('.chip') + .each(function(d2, index2) { + var node = d3_select(this).node(); + + // check the cursor is in the bounding box + if ( + index !== index2 && + d3_event.x < node.offsetLeft + (node.offsetWidth / 2) && + d3_event.x > node.offsetLeft - (node.offsetWidth / 2) && + d3_event.y < node.offsetTop + node.offsetHeight && + d3_event.y > node.offsetTop + ) { + targetIndex = index2; + targetIndexOffsetTop = node.offsetTop; + } + }) + .style('transform', function(d2, index2) { + var node = d3_select(this).node(); + + if (index === index2) { + return 'translate(' + x + 'px, ' + y + 'px)'; + } + + // only translate tags in the same row + if (node.offsetTop === targetIndexOffsetTop) { + if (index2 < index && index2 >= targetIndex) { + return 'translateX(' + draggedTagWidth + 'px)'; + } else if (index2 > index && index2 <= targetIndex) { + return 'translateX(-' + draggedTagWidth + 'px)'; + } + } + return null; + }); + } + }) + .on('end', function(d, index) { + if (!d3_select(this).classed('dragging')) { + return; + } + + d3_select(this) + .classed('dragging', false); + + container.selectAll('.chip') + .style('transform', null); + + if (typeof targetIndex === 'number') { + var element = _multiData[index]; + _multiData.splice(index, 1); + _multiData.splice(targetIndex, 0, element); + + var t = {}; + + if (_multiData.length) { + t[field.key] = _multiData.map(function(element) { + return element.key; + }).join(';'); + } else { + t[field.key] = undefined; + } + + dispatch.call('change', this, t); + } + dragOrigin = undefined; + targetIndex = undefined; + }) + ); + } + combo.focus = function() { input.node().focus();