mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-22 10:09:34 +00:00
moved click_to_edit and autoselect_typed into separate plugins
This commit is contained in:
parent
87d29f50aa
commit
4820763e02
3 changed files with 189 additions and 79 deletions
|
|
@ -23,114 +23,73 @@ import "tom-select/dist/css/tom-select.bootstrap5.css";
|
||||||
import '../../css/components/tom-select_extensions.css';
|
import '../../css/components/tom-select_extensions.css';
|
||||||
import TomSelect from "tom-select";
|
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 {
|
export default class extends Controller {
|
||||||
_tomSelect;
|
_tomSelect;
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
const self = this;
|
|
||||||
let settings = {
|
let settings = {
|
||||||
persistent: false,
|
persistent: false,
|
||||||
create: true,
|
create: true,
|
||||||
maxItems: 5,
|
maxItems: 1,
|
||||||
createOnBlur: true,
|
createOnBlur: true,
|
||||||
selectOnTab: true,
|
selectOnTab: true,
|
||||||
//This a an ugly solution to disable the delimiter parsing of the TomSelect plugin
|
//This a an ugly solution to disable the delimiter parsing of the TomSelect plugin
|
||||||
delimiter: ' ',
|
delimiter: 'VERY_L0NG_D€LIMITER_WHICH_WILL_NEVER_BE_ENCOUNTERED_IN_A_STRING',
|
||||||
render: {
|
render: {
|
||||||
item: function item(data, escape) {
|
item: (data, escape) => {
|
||||||
var tpl = document.createElement('template');
|
return '<span>' + escape(data.label) + '</span>';
|
||||||
tpl.innerHTML = '<span>|' + escape(data.label) + '|</span>';
|
|
||||||
var thing = tpl.content.firstChild;
|
|
||||||
|
|
||||||
thing.addEventListener('click', evt => {
|
|
||||||
if (!self._tomSelect.isFocused) {
|
|
||||||
//return;
|
|
||||||
}
|
|
||||||
if (self._tomSelect.isLocked) return;
|
|
||||||
var val = self._tomSelect.inputValue()
|
|
||||||
if (self._tomSelect.options[val]) {
|
|
||||||
self._tomSelect.addItem(val)
|
|
||||||
} else if (self._tomSelect.settings.create) {
|
|
||||||
self._tomSelect.createItem();
|
|
||||||
}
|
|
||||||
self._tomSelect.setTextboxValue()
|
|
||||||
self._tomSelect.focus();
|
|
||||||
self._tomSelect.removeItem(thing);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return thing;
|
|
||||||
},
|
},
|
||||||
option: function option(data, escape) {
|
option: (data, escape) => {
|
||||||
if (data.image) {
|
if (data.image) {
|
||||||
return "<div class='row m-0'><div class='col-2 pl-0 pr-1'><img class='typeahead-image' src='" + data.image + "'/></div><div class='col-10'>" + data.label + "</div></div>";
|
return "<div class='row m-0'><div class='col-2 pl-0 pr-1'><img class='typeahead-image' src='" + data.image + "'/></div><div class='col-10'>" + data.label + "</div></div>"
|
||||||
}
|
}
|
||||||
return '<div>' + escape(data.label) + '</div>';
|
return '<div>' + escape(data.label) + '</div>';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onInitialize: function () {
|
plugins: {
|
||||||
},
|
'autoselect_typed': {},
|
||||||
onItemRemove: function (value) {
|
'click_to_edit': {},
|
||||||
console.log(value)
|
'remove_button': {}
|
||||||
console.log(self._tomSelect.options)
|
|
||||||
self._tomSelect.setTextboxValue(value);
|
|
||||||
if (self._tomSelect.control_input.value.trim() === '') {
|
|
||||||
var option = self.options[value];
|
|
||||||
if (option) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: {//'restore_on_backspace': {},
|
|
||||||
//'remove_button': {}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (this.element.dataset.autocomplete) {
|
|
||||||
var base_url = this.element.dataset.autocomplete;
|
if(this.element.dataset.autocomplete) {
|
||||||
|
const base_url = this.element.dataset.autocomplete;
|
||||||
settings.searchField = "label";
|
settings.searchField = "label";
|
||||||
settings.sortField = "label";
|
settings.sortField = "label";
|
||||||
settings.valueField = "label";
|
settings.valueField = "label";
|
||||||
settings.load = function (query, callback) {
|
settings.load = (query, callback) => {
|
||||||
if (query.length < 2) {
|
if(query.length < 2){
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var url = base_url.replace('__QUERY__', encodeURIComponent(query));
|
const url = base_url.replace('__QUERY__', encodeURIComponent(query));
|
||||||
fetch(url).then(function (response) {
|
|
||||||
return response.json();
|
fetch(url)
|
||||||
}).then(function (json) {
|
.then(response => response.json())
|
||||||
var data = json.map(function (x) {
|
.then(json => {
|
||||||
return {
|
const data = json.map(x => {
|
||||||
"label": x.name,
|
return {
|
||||||
"image": x.image
|
"label": x.name,
|
||||||
};
|
"image": x.image,
|
||||||
});
|
}
|
||||||
callback(data);
|
});
|
||||||
})["catch"](function () {
|
callback(data);
|
||||||
|
}).catch(()=>{
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
this._tomSelect = new TomSelect(this.element, settings);
|
||||||
this._tomSelect = new tom_select__WEBPACK_IMPORTED_MODULE_31__["default"](this.element, settings);
|
|
||||||
this._tomSelect.hook("before", "onBlur", function () {
|
|
||||||
var val = self._tomSelect.inputValue()
|
|
||||||
if (!self._tomSelect.isLocked && self._tomSelect.options[val]) {
|
|
||||||
self._tomSelect.addItem(val)
|
|
||||||
self._tomSelect.setTextboxValue()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._tomSelect.hook("before", "onKeyPress", function (e) {
|
|
||||||
var character = String.fromCharCode(e.keyCode || e.which);
|
|
||||||
if (!self._tomSelect.isLocked && self._tomSelect.settings.mode === 'multi' && character === self._tomSelect.settings.delimiter) {
|
|
||||||
var val = self._tomSelect.inputValue()
|
|
||||||
if (self._tomSelect.options[val]) {
|
|
||||||
self._tomSelect.addItem(val)
|
|
||||||
self._tomSelect.setTextboxValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
super.disconnect();
|
super.disconnect();
|
||||||
//Destroy the TomSelect instance
|
//Destroy the TomSelect instance
|
||||||
|
|
|
||||||
58
assets/tomselect/autoselect_typed/autoselect_typed.js
Normal file
58
assets/tomselect/autoselect_typed/autoselect_typed.js
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
93
assets/tomselect/click_to_edit/click_to_edit.js
Normal file
93
assets/tomselect/click_to_edit/click_to_edit.js
Normal file
|
|
@ -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 };
|
||||||
Loading…
Add table
Add a link
Reference in a new issue