diff --git a/public/index.html b/public/index.html index aa04bad43..06d781af6 100644 --- a/public/index.html +++ b/public/index.html @@ -5332,7 +5332,7 @@
Inclusion Group - + diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 44b13bfcf..e48dedaa2 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -1620,7 +1620,7 @@ function getWorldEntry(name, data, entry) { saveWorldInfo(name, data); }); groupInput.val(entry.group ?? '').trigger('input'); - setTimeout(() => createEntryInputAutocomplete(groupInput, getInclusionGroupCallback(data)), 1); + setTimeout(() => createEntryInputAutocomplete(groupInput, getInclusionGroupCallback(data), { allowMultiple: true }), 1); // inclusion priority const groupOverrideInput = template.find('input[name="groupOverride"]'); @@ -2042,11 +2042,15 @@ function getWorldEntry(name, data, entry) { * @returns {(input: any, output: any) => any} Callback function for the autocomplete */ function getInclusionGroupCallback(data) { - return function (input, output) { + return function (control, input, output) { + const uid = $(control).data("uid"); + const thisGroups = String($(control).val()).split(/,\s*/).filter(x => x).map(x => x.toLowerCase()); const groups = new Set(); for (const entry of Object.values(data.entries)) { + // Skip the groups of this entry, because auto-complete should only suggest the ones that are already available on other entries + if (entry.uid == uid) continue; if (entry.group) { - groups.add(String(entry.group)); + entry.group.split(/,\s*/).filter(x => x).forEach(x => groups.add(x)); } } @@ -2054,20 +2058,19 @@ function getInclusionGroupCallback(data) { haystack.sort((a, b) => a.localeCompare(b)); const needle = input.term.toLowerCase(); const hasExactMatch = haystack.findIndex(x => x.toLowerCase() == needle) !== -1; - const result = haystack.filter(x => x.toLowerCase().includes(needle)); - - if (input.term && !hasExactMatch) { - result.unshift(input.term); - } + const result = haystack.filter(x => x.toLowerCase().includes(needle) && (!thisGroups.includes(x) || hasExactMatch && thisGroups.filter(g => g == x).length == 1)); output(result); }; } function getAutomationIdCallback(data) { - return function (input, output) { + return function (control, input, output) { + const uid = $(control).data("uid"); const ids = new Set(); for (const entry of Object.values(data.entries)) { + // Skip automation id of this entry, because auto-complete should only suggest the ones that are already available on other entries + if (entry.uid == uid) continue; if (entry.automationId) { ids.add(String(entry.automationId)); } @@ -2083,36 +2086,53 @@ function getAutomationIdCallback(data) { const haystack = Array.from(ids); haystack.sort((a, b) => a.localeCompare(b)); const needle = input.term.toLowerCase(); - const hasExactMatch = haystack.findIndex(x => x.toLowerCase() == needle) !== -1; const result = haystack.filter(x => x.toLowerCase().includes(needle)); - if (input.term && !hasExactMatch) { - result.unshift(input.term); - } - output(result); }; } /** * Create an autocomplete for the inclusion group. - * @param {JQuery} input Input element to attach the autocomplete to - * @param {(input: any, output: any) => any} callback Source data callbacks + * @param {JQuery} input - Input element to attach the autocomplete to + * @param {(control: JQuery, input: any, output: any) => any} callback - Source data callbacks + * @param {object} [options={}] - Optional arguments + * @param {boolean} [options.allowMultiple=false] - Whether to allow multiple comma-separated values */ -function createEntryInputAutocomplete(input, callback) { +function createEntryInputAutocomplete(input, callback, { allowMultiple = false } = {}) { + const handleSelect = (event, ui) => { + // Prevent default autocomplete select, so we can manually set the value + event.preventDefault(); + if (!allowMultiple) { + $(input).val(ui.item.value).trigger('input').trigger('blur'); + } else { + var terms = String($(input).val()).split(/,\s*/); + terms.pop(); // remove the current input + terms.push(ui.item.value); // add the selected item + $(input).val(terms.filter(x => x).join(", ")).trigger('input').trigger('blur'); + } + }; + $(input).autocomplete({ minLength: 0, - source: callback, - select: function (event, ui) { - $(input).val(ui.item.value).trigger('input').trigger('blur'); + source: function (request, response) { + if (!allowMultiple) { + callback(input, request, response); + } else { + const term = request.term.split(/,\s*/).pop(); + request.term = term; + callback(input, request, response); + } }, + select: handleSelect, }); $(input).on('focus click', function () { - $(input).autocomplete('search', String($(input).val())); + $(input).autocomplete('search', allowMultiple ? String($(input).val()).split(/,\s*/).pop() : $(input).val()); }); } + /** * Duplicated a WI entry by copying all of its properties and assigning a new uid * @param {*} data - The data of the book @@ -2807,10 +2827,12 @@ function filterGroupsByScoring(groups, buffer, removeEntry) { function filterByInclusionGroups(newEntries, allActivatedEntries, buffer) { console.debug('-- INCLUSION GROUP CHECKS BEGIN --'); const grouped = newEntries.filter(x => x.group).reduce((acc, item) => { - if (!acc[item.group]) { - acc[item.group] = []; - } - acc[item.group].push(item); + item.group.split(/,\s*/).filter(x => x).forEach(group => { + if (!acc[group]) { + acc[group] = []; + } + acc[group].push(item); + }); return acc; }, {});