From b54cb642f4f0618a16aadaa412da8c242c4aacc8 Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Mon, 14 Apr 2025 12:50:24 +0800 Subject: [PATCH 01/12] feat(dropdown): allow editing of added items --- src/definitions/elements/label.less | 6 ++++++ src/definitions/modules/dropdown.js | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/definitions/elements/label.less b/src/definitions/elements/label.less index c28909a370..2fd34618e4 100755 --- a/src/definitions/elements/label.less +++ b/src/definitions/elements/label.less @@ -118,6 +118,12 @@ a.ui.label { margin: 0 0 0 @deleteMargin; } +/* Edited label close/delete icon positioning */ +.ui.label.editing:not(.icon) > .close.icon, +.ui.label.editing:not(.icon) > .delete.icon { + margin: .3em 0 0 @deleteMargin; +} + /* Label for only an icon */ .ui.icon.label > .icon { margin: 0 auto; diff --git a/src/definitions/modules/dropdown.js b/src/definitions/modules/dropdown.js index f4bcdd1d75..6b1f5052d6 100644 --- a/src/definitions/modules/dropdown.js +++ b/src/definitions/modules/dropdown.js @@ -2905,6 +2905,33 @@ .attr('data-' + metadata.value, escapedValue) .html(templates.label(escapedValue, text, settings.preserveHTML, settings.className)) ; + $label.on ("click", function() { + if ($(this).hasClass('editing')) { + return; + } + $(this).addClass('editing'); + $(this).html($(this).children()) + let inputDiv = $('') + inputDiv.on('keydown', function(e) { + // Prevent parent from acting on Backspace/Delete + if (e.key === 'Backspace' || e.key === 'Delete') { + e.stopPropagation(); + } + if (e.key === 'Enter') { + $(this).trigger('blur'); + } + }); + inputDiv.on('blur', function() { + let input = $(this); + let container = input.parent() + container.attr('data-value', input.val()); + container.prepend(input.val()) + container.removeClass('editing'); + input.remove() + }) + $(this).prepend(inputDiv) + inputDiv.trigger("focus") + }) $label = settings.onLabelCreate.call($label, escapedValue, text); if (module.has.label(value)) { From 2b58ddb97842f623d02e9d3cd62304401b2c3983 Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Mon, 14 Apr 2025 13:27:02 +0800 Subject: [PATCH 02/12] Remove item if string is empty --- src/definitions/modules/dropdown.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/definitions/modules/dropdown.js b/src/definitions/modules/dropdown.js index 6b1f5052d6..182d43954d 100644 --- a/src/definitions/modules/dropdown.js +++ b/src/definitions/modules/dropdown.js @@ -2924,6 +2924,9 @@ inputDiv.on('blur', function() { let input = $(this); let container = input.parent() + if (!(input.val()).trim()) { + container.remove(); + } container.attr('data-value', input.val()); container.prepend(input.val()) container.removeClass('editing'); From aeaa5b5b9c00aa7baa5b33cb8033f060329bfe9d Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Mon, 14 Apr 2025 13:27:19 +0800 Subject: [PATCH 03/12] Move style updates to dropdown.less --- src/definitions/elements/label.less | 6 ------ src/definitions/modules/dropdown.less | 11 +++++++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/definitions/elements/label.less b/src/definitions/elements/label.less index 2fd34618e4..c28909a370 100755 --- a/src/definitions/elements/label.less +++ b/src/definitions/elements/label.less @@ -118,12 +118,6 @@ a.ui.label { margin: 0 0 0 @deleteMargin; } -/* Edited label close/delete icon positioning */ -.ui.label.editing:not(.icon) > .close.icon, -.ui.label.editing:not(.icon) > .delete.icon { - margin: .3em 0 0 @deleteMargin; -} - /* Label for only an icon */ .ui.icon.label > .icon { margin: 0 auto; diff --git a/src/definitions/modules/dropdown.less b/src/definitions/modules/dropdown.less index d4d279faf4..0992bf043c 100755 --- a/src/definitions/modules/dropdown.less +++ b/src/definitions/modules/dropdown.less @@ -981,6 +981,17 @@ select.ui.dropdown { .ui.multiple.search.dropdown@{notSelection} > input.search:first-child { min-width: @multipleSearchMinWidth; } + + /* Edited label close/delete icon positioning */ + .ui.multiple.search.dropdown > .ui.label.editing > .close.icon, + .ui.multiple.search.dropdown > .ui.label.editing > .delete.icon { + margin: .3em 0 0 @deleteMargin; + } + + .ui.multiple.search.dropdown > .ui.label > input[type=text] { + padding: 1px; + width: 90%; + } } } From 11f7fddcb24c61a4e9d97e8b3c260c1addd9aad4 Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Mon, 14 Apr 2025 13:47:54 +0800 Subject: [PATCH 04/12] remove margin variable --- src/definitions/modules/dropdown.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/definitions/modules/dropdown.less b/src/definitions/modules/dropdown.less index 0992bf043c..77fd831bf1 100755 --- a/src/definitions/modules/dropdown.less +++ b/src/definitions/modules/dropdown.less @@ -985,7 +985,7 @@ select.ui.dropdown { /* Edited label close/delete icon positioning */ .ui.multiple.search.dropdown > .ui.label.editing > .close.icon, .ui.multiple.search.dropdown > .ui.label.editing > .delete.icon { - margin: .3em 0 0 @deleteMargin; + margin: .3em 0 0 .5em; } .ui.multiple.search.dropdown > .ui.label > input[type=text] { From 2053cfccd37d9cabcef7cc24bff6fc66d79359fb Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Thu, 17 Apr 2025 20:26:42 +0800 Subject: [PATCH 05/12] Limit editable labels to allowAdditions === true and improve editing behavior and css --- src/definitions/modules/dropdown.js | 69 ++++++++++++++++----------- src/definitions/modules/dropdown.less | 2 +- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/definitions/modules/dropdown.js b/src/definitions/modules/dropdown.js index 182d43954d..d75c11b411 100644 --- a/src/definitions/modules/dropdown.js +++ b/src/definitions/modules/dropdown.js @@ -2905,36 +2905,49 @@ .attr('data-' + metadata.value, escapedValue) .html(templates.label(escapedValue, text, settings.preserveHTML, settings.className)) ; - $label.on ("click", function() { - if ($(this).hasClass('editing')) { - return; - } - $(this).addClass('editing'); - $(this).html($(this).children()) - let inputDiv = $('') - inputDiv.on('keydown', function(e) { - // Prevent parent from acting on Backspace/Delete - if (e.key === 'Backspace' || e.key === 'Delete') { - e.stopPropagation(); - } - if (e.key === 'Enter') { - $(this).trigger('blur'); - } - }); - inputDiv.on('blur', function() { - let input = $(this); - let container = input.parent() - if (!(input.val()).trim()) { - container.remove(); + if (settings.allowAdditions) { + $label.on ("click", function(e) { + if (e.target !== this || $(this).hasClass('editing')) { + return; } - container.attr('data-value', input.val()); - container.prepend(input.val()) - container.removeClass('editing'); - input.remove() + $(this).addClass('editing'); + $(this).html($(this).children()) + $(this).children().hide(); + let inputDiv = $('') + inputDiv.on('keydown', function(e) { + // Prevent parent from acting on Backspace/Delete while typing + if (e.key === 'Backspace' || e.key === 'Delete') { + e.stopPropagation(); + } + if (e.key === 'Enter') { + $(this).trigger('blur'); + } + }); + inputDiv.on('blur', function() { + let input = $(this); + let container = input.parent() + + let oldVal = container.attr('data-value'); + let newVal = input.val(); + if (!(input.val()).trim()) { + module.remove.activeLabels(container); + return + } + let userVal = module.is.userValue() + if (oldVal !== newVal && !userVal) { + module.remove.activeLabels(container); + module.add.label(newVal, newVal, true); + } else { + container.prepend(input.val()) + container.removeClass('editing'); + input.remove() + container.children('.close.icon, .delete.icon').show(); + } + }) + $(this).prepend(inputDiv) + inputDiv.trigger("focus") }) - $(this).prepend(inputDiv) - inputDiv.trigger("focus") - }) + } $label = settings.onLabelCreate.call($label, escapedValue, text); if (module.has.label(value)) { diff --git a/src/definitions/modules/dropdown.less b/src/definitions/modules/dropdown.less index 77fd831bf1..d24cf53198 100755 --- a/src/definitions/modules/dropdown.less +++ b/src/definitions/modules/dropdown.less @@ -990,7 +990,7 @@ select.ui.dropdown { .ui.multiple.search.dropdown > .ui.label > input[type=text] { padding: 1px; - width: 90%; + vertical-align: baseline; } } } From adb36f014f838cb674d25fcbef5c5f09d44cade0 Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Thu, 17 Apr 2025 21:39:48 +0800 Subject: [PATCH 06/12] Properly update value of dropdown's hidden menu --- src/definitions/modules/dropdown.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/definitions/modules/dropdown.js b/src/definitions/modules/dropdown.js index d75c11b411..3b85cb3811 100644 --- a/src/definitions/modules/dropdown.js +++ b/src/definitions/modules/dropdown.js @@ -2935,8 +2935,10 @@ } let userVal = module.is.userValue() if (oldVal !== newVal && !userVal) { + let $activeItem = $menu.children('.' + className.active).eq(0) module.remove.activeLabels(container); module.add.label(newVal, newVal, true); + module.add.value(newVal, newVal, $activeItem, false); } else { container.prepend(input.val()) container.removeClass('editing'); From 7f4cb4ac0f3cd3b9a948661b0d3cd9c80f0715b6 Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Thu, 17 Apr 2025 21:55:15 +0800 Subject: [PATCH 07/12] Prevent adding new item when editing an existing one --- src/definitions/modules/dropdown.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/definitions/modules/dropdown.js b/src/definitions/modules/dropdown.js index 3b85cb3811..462cd915af 100644 --- a/src/definitions/modules/dropdown.js +++ b/src/definitions/modules/dropdown.js @@ -2920,6 +2920,7 @@ e.stopPropagation(); } if (e.key === 'Enter') { + e.stopPropagation(); $(this).trigger('blur'); } }); From 9c302521dddcfe1357d18dac4b2cc4656f4d1635 Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Thu, 17 Apr 2025 23:20:50 +0800 Subject: [PATCH 08/12] Add checking if edited value matches with existing item in menu --- src/definitions/modules/dropdown.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/definitions/modules/dropdown.js b/src/definitions/modules/dropdown.js index ad19a22d2c..b1fdff93e3 100644 --- a/src/definitions/modules/dropdown.js +++ b/src/definitions/modules/dropdown.js @@ -2728,6 +2728,16 @@ } let userVal = module.is.userValue() if (oldVal !== newVal && !userVal) { + + // check if new value matches with a non-user value + let nonAddedValues = $menu.children().map(function () { + return $(this).data('value'); + }).get(); + if (nonAddedValues.includes(newVal)) { + let $matchedMenuItem = $menu.children('[data-value="' + newVal + '"]') + $matchedMenuItem.addClass(className.filtered) + } + let $activeItem = $menu.children('.' + className.active).eq(0) module.remove.activeLabels(container); module.add.label(newVal, newVal, true); From 79c41da81981539d683657dcc0296f98981fed84 Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Thu, 17 Apr 2025 23:34:30 +0800 Subject: [PATCH 09/12] Also add "active" class to the menu item added by editing --- src/definitions/modules/dropdown.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/definitions/modules/dropdown.js b/src/definitions/modules/dropdown.js index b1fdff93e3..796f411fc7 100644 --- a/src/definitions/modules/dropdown.js +++ b/src/definitions/modules/dropdown.js @@ -2735,6 +2735,7 @@ }).get(); if (nonAddedValues.includes(newVal)) { let $matchedMenuItem = $menu.children('[data-value="' + newVal + '"]') + $matchedMenuItem.addClass(className.active) $matchedMenuItem.addClass(className.filtered) } From 42910b3f98c26f0b923133c4e92830dad3daa7f3 Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Thu, 17 Apr 2025 23:56:36 +0800 Subject: [PATCH 10/12] Fix issues spotted by CodeQL --- src/definitions/modules/dropdown.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/definitions/modules/dropdown.js b/src/definitions/modules/dropdown.js index 796f411fc7..2e35627352 100644 --- a/src/definitions/modules/dropdown.js +++ b/src/definitions/modules/dropdown.js @@ -2705,7 +2705,8 @@ $(this).addClass('editing'); $(this).html($(this).children()) $(this).children().hide(); - let inputDiv = $('') + let value = $(this).attr('data-value'); + let inputDiv = $('').val(value); inputDiv.on('keydown', function(e) { // Prevent parent from acting on Backspace/Delete while typing if (e.key === 'Backspace' || e.key === 'Delete') { @@ -2744,7 +2745,7 @@ module.add.label(newVal, newVal, true); module.add.value(newVal, newVal, $activeItem, false); } else { - container.prepend(input.val()) + container.prepend(document.createTextNode(input.val())); container.removeClass('editing'); input.remove() container.children('.close.icon, .delete.icon').show(); From c901c18ad94f12cdeff060d4c32cc0c3319d5360 Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Fri, 18 Apr 2025 00:39:56 +0800 Subject: [PATCH 11/12] Fix linting issues detected by eslint --- src/definitions/modules/dropdown.js | 36 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/definitions/modules/dropdown.js b/src/definitions/modules/dropdown.js index 2e35627352..dc5f165842 100644 --- a/src/definitions/modules/dropdown.js +++ b/src/definitions/modules/dropdown.js @@ -2698,16 +2698,16 @@ .attr('data-' + metadata.value, value) .html(templates.label(value, text, settings)); if (settings.allowAdditions) { - $label.on ("click", function(e) { + $label.on('click', function (e) { if (e.target !== this || $(this).hasClass('editing')) { return; } $(this).addClass('editing'); - $(this).html($(this).children()) + $(this).html($(this).children()); $(this).children().hide(); let value = $(this).attr('data-value'); let inputDiv = $('').val(value); - inputDiv.on('keydown', function(e) { + inputDiv.on('keydown', function (e) { // Prevent parent from acting on Backspace/Delete while typing if (e.key === 'Backspace' || e.key === 'Delete') { e.stopPropagation(); @@ -2717,43 +2717,43 @@ $(this).trigger('blur'); } }); - inputDiv.on('blur', function() { + inputDiv.on('blur', function () { let input = $(this); - let container = input.parent() + let container = input.parent(); let oldVal = container.attr('data-value'); let newVal = input.val(); - if (!(input.val()).trim()) { + if (!input.val().trim()) { module.remove.activeLabels(container); - return + + return; } - let userVal = module.is.userValue() + let userVal = module.is.userValue(); if (oldVal !== newVal && !userVal) { - // check if new value matches with a non-user value let nonAddedValues = $menu.children().map(function () { return $(this).data('value'); }).get(); if (nonAddedValues.includes(newVal)) { - let $matchedMenuItem = $menu.children('[data-value="' + newVal + '"]') - $matchedMenuItem.addClass(className.active) - $matchedMenuItem.addClass(className.filtered) + let $matchedMenuItem = $menu.children('[data-value="' + newVal + '"]'); + $matchedMenuItem.addClass(className.active); + $matchedMenuItem.addClass(className.filtered); } - let $activeItem = $menu.children('.' + className.active).eq(0) + let $activeItem = $menu.children('.' + className.active).eq(0); module.remove.activeLabels(container); module.add.label(newVal, newVal, true); module.add.value(newVal, newVal, $activeItem, false); } else { container.prepend(document.createTextNode(input.val())); container.removeClass('editing'); - input.remove() + input.remove(); container.children('.close.icon, .delete.icon').show(); } - }) - $(this).prepend(inputDiv) - inputDiv.trigger("focus") - }) + }); + $(this).prepend(inputDiv); + inputDiv.trigger('focus'); + }); } $label = settings.onLabelCreate.call($label, value, text); From d1f7f1ae3e6c3f9117523ed8d6ee4fa10c980745 Mon Sep 17 00:00:00 2001 From: Brian Javier Date: Fri, 18 Apr 2025 00:46:07 +0800 Subject: [PATCH 12/12] Fix lint issues in less file --- src/definitions/modules/dropdown.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/definitions/modules/dropdown.less b/src/definitions/modules/dropdown.less index 6e3d9093fc..c61b206db8 100755 --- a/src/definitions/modules/dropdown.less +++ b/src/definitions/modules/dropdown.less @@ -993,10 +993,10 @@ select.ui.dropdown { /* Edited label close/delete icon positioning */ .ui.multiple.search.dropdown > .ui.label.editing > .close.icon, .ui.multiple.search.dropdown > .ui.label.editing > .delete.icon { - margin: .3em 0 0 .5em; + margin: 0.3em 0 0 0.5em; } - .ui.multiple.search.dropdown > .ui.label > input[type=text] { + .ui.multiple.search.dropdown > .ui.label > input[type="text"] { padding: 1px; vertical-align: baseline; }