diff --git a/assets/controllers/elements/attachment_autocomplete_controller.js b/assets/controllers/elements/attachment_autocomplete_controller.js
index fe44baee..56b57292 100644
--- a/assets/controllers/elements/attachment_autocomplete_controller.js
+++ b/assets/controllers/elements/attachment_autocomplete_controller.js
@@ -23,6 +23,12 @@ import "tom-select/dist/css/tom-select.bootstrap5.css";
import '../../css/components/tom-select_extensions.css';
import TomSelect from "tom-select";
+import TomSelect_click_to_edit from '../../tomselect/click_to_edit/click_to_edit'
+import TomSelect_autoselect_typed from '../../tomselect/autoselect_typed/autoselect_typed'
+
+TomSelect.define('click_to_edit', TomSelect_click_to_edit)
+TomSelect.define('autoselect_typed', TomSelect_autoselect_typed)
+
export default class extends Controller {
_tomSelect;
@@ -46,6 +52,11 @@ export default class extends Controller {
}
return '
' + escape(data.label) + '
';
}
+ },
+ plugins: {
+ 'autoselect_typed': {},
+ 'click_to_edit': {},
+ 'remove_button': {}
}
};
diff --git a/assets/tomselect/autoselect_typed/autoselect_typed.js b/assets/tomselect/autoselect_typed/autoselect_typed.js
new file mode 100644
index 00000000..3019d4c8
--- /dev/null
+++ b/assets/tomselect/autoselect_typed/autoselect_typed.js
@@ -0,0 +1,58 @@
+/**
+ * Autoselect Typed plugin for Tomselect
+ *
+ * This plugin allows automatically selecting an option matching the typed text when the Tomselect element goes out of
+ * focus (is blurred) and/or when the delimiter is typed.
+ *
+ * #select_on_blur option
+ * Tomselect natively supports the "createOnBlur" option. This option picks up any remaining text in the input field
+ * and uses it to create a new option and selects that option. It does behave a bit strangely though, in that it will
+ * not select an already existing option when the input is blurred, so if you typed something that matches an option in
+ * the list and then click outside the box (without pressing enter) the entered text is just removed (unless you have
+ * allow duplicates on in which case it will create a new option).
+ * This plugin fixes that, such that Tomselect will first try to select an option matching the remaining uncommitted
+ * text and only when no matching option is found tries to create a new one (if createOnBlur and create is on)
+ *
+ * #select_on_delimiter option
+ * Normally when typing the delimiter (space by default) Tomselect will try to create a new option (and select it) (if
+ * create is on), but if the typed text matches an option (and allow duplicates is off) it refuses to react at all until
+ * you press enter. With this option, the delimiter will also allow selecting an option, not just creating it.
+ */
+function select_current_input(self){
+ if(self.isLocked){
+ return
+ }
+
+ const val = self.inputValue()
+ if (self.options[val]) {
+ self.addItem(val)
+ self.setTextboxValue()
+ }
+}
+
+export default function(plugin_options_) {
+ const plugin_options = Object.assign({
+ //Autoselect the typed text when the input element goes out of focus
+ select_on_blur: true,
+ //Autoselect the typed text when the delimiter is typed
+ select_on_delimiter: true,
+ }, plugin_options_);
+
+ const self = this
+
+ if(plugin_options.select_on_blur) {
+ this.hook("before", "onBlur", function () {
+ select_current_input(self)
+ })
+ }
+
+ if(plugin_options.select_on_delimiter) {
+ this.hook("before", "onKeyPress", function (e) {
+ const character = String.fromCharCode(e.keyCode || e.which);
+ if (self.settings.mode === 'multi' && character === self.settings.delimiter) {
+ select_current_input(self)
+ }
+ })
+ }
+
+}
\ No newline at end of file
diff --git a/assets/tomselect/click_to_edit/click_to_edit.js b/assets/tomselect/click_to_edit/click_to_edit.js
new file mode 100644
index 00000000..b7dcab03
--- /dev/null
+++ b/assets/tomselect/click_to_edit/click_to_edit.js
@@ -0,0 +1,93 @@
+/**
+ * click_to_edit plugin for Tomselect
+ *
+ * This plugin allows editing (and selecting text in) any selected item by clicking it.
+ *
+ * Usually, when the user typed some text and created an item in Tomselect that item cannot be edited anymore. To make
+ * a change, the item has to be deleted and retyped completely. There is also generally no way to copy text out of a
+ * tomselect item. The "restore_on_backspace" plugin improves that somewhat, by allowing the user to edit an item after
+ * pressing backspace. However, it is somewhat confusing to first have to focus the field an then hit backspace in order
+ * to copy a piece of text. It may also not be immediately obvious for editing.
+ * This plugin transforms an item into editable text when it is clicked, e.g. when the user tries to place the caret
+ * within an item or when they try to drag across the text to highlight it.
+ * It also plays nice with the remove_button plugin which still removes (deselects) an option entirely.
+ *
+ * It is recommended to also enable the autoselect_typed plugin when using this plugin. Without it, the text in the
+ * input field (i.e. the item that was just clicked) is lost when the user clicks outside the field. Also, when the user
+ * clicks an option (making it text) and then tries to enter another one by entering the delimiter (e.g. space) nothing
+ * happens until enter is pressed or the text is changed from what it was.
+ */
+
+/**
+ * Return a dom element from either a dom query string, jQuery object, a dom element or html string
+ * https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518
+ *
+ * param query should be {}
+ */
+const getDom = query => {
+ if (query.jquery) {
+ return query[0];
+ }
+ if (query instanceof HTMLElement) {
+ return query;
+ }
+ if (isHtmlString(query)) {
+ var tpl = document.createElement('template');
+ tpl.innerHTML = query.trim(); // Never return a text node of whitespace as the result
+ return tpl.content.firstChild;
+ }
+ return document.querySelector(query);
+};
+const isHtmlString = arg => {
+ if (typeof arg === 'string' && arg.indexOf('<') > -1) {
+ return true;
+ }
+ return false;
+};
+
+function plugin(plugin_options_) {
+ const self = this
+
+ const plugin_options = Object.assign({
+ //If there is unsubmitted text in the input field, should that text be automatically used to select a matching
+ //element? If this is off, clicking on item1 and then clicking on item2 will result in item1 being deselected
+ auto_select_before_edit: true,
+ //If there is unsubmitted text in the input field, should that text be automatically used to create a matching
+ //element if no matching element was found or auto_select_before_edit is off?
+ auto_create_before_edit: true,
+ //customize this function to change which text the item is replaced with when clicking on it
+ text: option => {
+ return option[self.settings.labelField];
+ }
+ }, plugin_options_);
+
+
+ self.hook('after', 'setupTemplates', () => {
+ const orig_render_item = self.settings.render.item;
+ self.settings.render.item = (data, escape) => {
+ const item = getDom(orig_render_item.call(self, data, escape));
+
+ item.addEventListener('click', evt => {
+ if (self.isLocked) {
+ return;
+ }
+ const val = self.inputValue();
+
+ if (self.options[val]) {
+ self.addItem(val)
+ } else if (self.settings.create) {
+ self.createItem();
+ }
+ const option = self.options[item.dataset.value]
+ self.setTextboxValue(plugin_options.text.call(self, option));
+ self.focus();
+ self.removeItem(item);
+ }
+ );
+
+ return item;
+ }
+ });
+
+}
+export { plugin as default };
\ No newline at end of file