Fix tag selector dropdown style

This commit is contained in:
rubenwardy
2021-02-05 16:19:34 +00:00
parent 0ff4f40652
commit 3839dfbf90
6 changed files with 68 additions and 241 deletions

View File

@@ -1,6 +1,6 @@
/*! /*!
* Tag Selector plugin for jQuery: Facilitates selecting multiple tags by extending jQuery UI Autocomplete. * Tag Selector plugin for jQuery: Facilitates selecting multiple tags by extending jQuery UI Autocomplete.
* You may use Tag Selector under the terms of either the MIT License or the GNU General Public License (GPL) Version 2. * License: MIT
* https://petprojects.googlecode.com/svn/trunk/MIT-LICENSE.txt * https://petprojects.googlecode.com/svn/trunk/MIT-LICENSE.txt
*/ */
(function($) { (function($) {
@@ -20,7 +20,6 @@
function show_error(input, msg) { function show_error(input, msg) {
const err = input.parent().parent().find(".invalid-remaining"); const err = input.parent().parent().find(".invalid-remaining");
console.log(err.length);
err.text(msg); err.text(msg);
err.show(); err.show();
} }
@@ -30,14 +29,22 @@
const selector = $(this), const selector = $(this),
input = $('input[type=text]', this); input = $('input[type=text]', this);
selector.click(function() { input.focus(); }) const lookup = {};
for (let i = 0; i < source.length; i++) {
lookup[source[i].id] = source[i];
}
selector.click(() => input.focus())
.delegate('.badge a', 'click', function() { .delegate('.badge a', 'click', function() {
const id = $(this).parent().data("id"); const id = $(this).parent().data("id");
select.find("option[value=" + id + "]").attr("selected", false) select.find("option[value=" + id + "]").attr("selected", false)
recreate(); recreate();
}); });
function addTag(id, text) { function addTag(item) {
const id = item.id;
let text = item.text;
const idx = text.indexOf(':'); const idx = text.indexOf(':');
if (idx > 0) { if (idx > 0) {
text = text.substr(0, idx); text = text.substr(0, idx);
@@ -57,7 +64,7 @@
selector.find("span").remove(); selector.find("span").remove();
select.find("option").each(function() { select.find("option").each(function() {
if (this.hasAttribute("selected")) { if (this.hasAttribute("selected")) {
addTag(this.getAttribute("value"), this.innerText); addTag(lookup[this.getAttribute("value")]);
} }
}); });
} }
@@ -65,185 +72,44 @@
input.focusout(function() { input.focusout(function() {
const value = input.val().trim(); const value = input.val().trim();
if (value != "") { if (value !== "") {
show_error(input, "Please select an existing tag, it;s not possible to add custom ones."); show_error(input, "Please select an existing tag, it's not possible to add custom ones.");
} }
}) })
input.keydown(function(e) { input.keydown(function(e) {
if (e.keyCode === $.ui.keyCode.TAB && $(this).data('ui-autocomplete').menu.active) if (e.keyCode === $.ui.keyCode.TAB && $(this).data('ui-autocomplete').menu.active) {
e.preventDefault(); e.preventDefault();
}) }
.autocomplete({ }).autocomplete({
minLength: 0, minLength: 0,
source: source, source: source,
select: function(event, ui) { select: function(event, ui) {
addTag(ui.item.id, ui.item.toString()); addTag(ui.item);
input.val(""); input.val("");
return false; return false;
} }
}).focus(function() { }).focus(function() {
// The following works only once. $(this).data("ui-autocomplete").search($(this).val());
// $(this).trigger('keydown.autocomplete'); });
// As suggested by digitalPBK, works multiple times
// $(this).data("autocomplete").search($(this).val());
// As noted by Jonny in his answer, with newer versions use uiAutocomplete
$(this).data("ui-autocomplete").search($(this).val());
});
input.data('ui-autocomplete')._renderItem = function(ul, item) { input.data('ui-autocomplete')._renderItem = function(ul, item) {
return $('<li/>') return $('<li/>')
.data('item.autocomplete', item) .data('item.autocomplete', item)
.append($('<a/>').html(item.toString())) .append($('<a/>').html(item.toString()))
.appendTo(ul); .appendTo(ul);
}; };
input.data('ui-autocomplete')._resizeMenu = function(ul, item) { input.data('ui-autocomplete')._resizeMenu = function() {
var ul = this.menu.element; const ul = this.menu.element;
ul.outerWidth(Math.max( ul.outerWidth(Math.max(
ul.width('').outerWidth(), ul.width('').outerWidth(),
selector.outerWidth() selector.outerWidth()
)); ));
}; };
}); });
} }
$.fn.csvSelector = function(source, name, result, allowSlash) {
return this.each(function() {
const selector = $(this),
input = $('input[type=text]', this);
let selected = [];
const lookup = {};
for (var i = 0; i < source.length; i++) {
lookup[source[i].id] = source[i];
}
selector.click(function() { input.focus(); })
.delegate('.badge a', 'click', function() {
const id = $(this).parent().data("id");
for (let i = 0; i < selected.length; i++) {
if (selected[i] == id) {
selected.splice(i, 1);
}
}
recreate();
});
function selectItem(id) {
for (let i = 0; i < selected.length; i++) {
if (selected[i] == id) {
return false;
}
}
selected.push(id);
return true;
}
function addTag(id, value) {
const tag = $('<span class="badge badge-pill badge-primary"/>')
.text(value)
.data("id", id)
.append(' <a>x</a>')
.insertBefore(input);
input.attr("placeholder", null);
hide_error(input);
}
function recreate() {
selector.find("span").remove();
for (let i = 0; i < selected.length; i++) {
const value = lookup[selected[i]] || {value: selected[i]};
addTag(selected[i], value.value);
}
result.val(selected.join(","))
}
function readFromResult() {
selected = [];
const selected_raw = result.val().split(",");
for (let i = 0; i < selected_raw.length; i++) {
const raw = selected_raw[i].trim();
if (lookup[raw] || raw.match(/^([a-z0-9_]+)$/)) {
selected.push(raw);
}
}
recreate();
}
readFromResult();
result.change(readFromResult);
input.focusout(function() {
const item = input.val();
if (item.length == 0) {
input.data("ui-autocomplete").search("");
} else if (item.match(/^([a-z0-9_]+)$/)) {
selectItem(item);
recreate();
input.val("");
}
return true;
});
input.keydown(function(e) {
if (e.keyCode === $.ui.keyCode.TAB && $(this).data('ui-autocomplete').menu.active)
e.preventDefault();
else if (e.keyCode === $.ui.keyCode.COMMA) {
var item = input.val();
if (item.length == 0) {
input.data("ui-autocomplete").search("");
} else if (item.match(/^([a-z0-9_]+)$/)) {
selectItem(item);
recreate();
input.val("");
} else {
show_error(input, "Only lowercase alphanumeric and number names allowed.");
}
e.preventDefault();
return true;
} else if (e.keyCode === $.ui.keyCode.BACKSPACE) {
if (input.val() == "") {
var item = selected[selected.length - 1];
selected.splice(selected.length - 1, 1);
recreate();
if (!(item.indexOf("/") > 0))
input.val(item);
e.preventDefault();
return true;
}
}
})
.autocomplete({
minLength: 0,
source: source,
select: function(event, ui) {
selectItem(ui.item.id);
recreate();
input.val("");
return false;
}
});
input.data('ui-autocomplete')._renderItem = function(ul, item) {
return $('<li/>')
.data('item.autocomplete', item)
.append($('<a/>').text(item.toString()))
.appendTo(ul);
};
input.data('ui-autocomplete')._resizeMenu = function(ul, item) {
var ul = this.menu.element;
ul.outerWidth(Math.max(
ul.width('').outerWidth(),
selector.outerWidth()
));
};
});
}
$(function() { $(function() {
$(".multichoice_selector").each(function() { $(".multichoice_selector").each(function() {
const ele = $(this); const ele = $(this);
@@ -253,27 +119,25 @@
const options = []; const options = [];
sel.find("option").each(function() { sel.find("option").each(function() {
const text = $(this).text(); const text = $(this).text();
options.push({ const option = {
id: $(this).attr("value"), id: $(this).attr("value"),
value: text, text: text,
selected: !!$(this).attr("selected"), selected: !!$(this).attr("selected"),
toString: function() { return make_bold(text); }, toString: function() { return make_bold(text); },
}); };
const idx = text.indexOf(":");
if (idx > 0) {
option.title = text.substring(0, idx);
option.description = text.substring(idx + 1);
} else {
option.title = text
}
options.push(option);
}); });
ele.selectSelector(options, sel); ele.selectSelector(options, sel);
}); });
$(".metapackage_selector").each(function() {
const input = $(this).parent().children("input[type='text']");
input.hide();
$(this).csvSelector(meta_packages, input.attr("name"), input);
});
$(".deps_selector").each(function() {
const input = $(this).parent().children("input[type='text']");
input.hide();
$(this).csvSelector(all_packages, input.attr("name"), input);
});
}); });
})(jQuery); })(jQuery);

View File

@@ -9,6 +9,7 @@
border: 1px solid #444 !important; border: 1px solid #444 !important;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
max-width: 700px;
max-height: 400px; max-height: 400px;
li { li {
@@ -22,6 +23,13 @@
padding: 0.3em 0.5em !important; padding: 0.3em 0.5em !important;
margin: 0 !important; margin: 0 !important;
} }
a:hover, a:focus, li:focus a {
font-weight: normal !important;
text-decoration: none;
padding: 0.3em 0.5em !important;
margin: 0 !important;
}
} }
.bulletselector { .bulletselector {

View File

@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}title{% endblock %} - {{ config.USER_APP_NAME }}</title> <title>{% block title %}title{% endblock %} - {{ config.USER_APP_NAME }}</title>
<link rel="stylesheet" type="text/css" href="/static/libs/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="/static/libs/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/static/custom.css?v=22"> <link rel="stylesheet" type="text/css" href="/static/custom.css?v=23">
<link rel="search" type="application/opensearchdescription+xml" href="/static/opensearch.xml" title="ContentDB" /> <link rel="search" type="application/opensearchdescription+xml" href="/static/opensearch.xml" title="ContentDB" />
<link rel="shortcut icon" href="/favicon-16.png" sizes="16x16"> <link rel="shortcut icon" href="/favicon-16.png" sizes="16x16">
<link rel="icon" href="/favicon-128.png" sizes="128x128"> <link rel="icon" href="/favicon-128.png" sizes="128x128">
@@ -196,7 +196,7 @@
<script src="/static/libs/easymde.min.js"></script> <script src="/static/libs/easymde.min.js"></script>
<link rel="stylesheet" type="text/css" href="/static/libs/easymde.min.css"> <link rel="stylesheet" type="text/css" href="/static/libs/easymde.min.css">
<link href="/static/fa/css/all.css" rel="stylesheet"> <link href="/static/fa/css/all.css" rel="stylesheet">
<script src="/static/markdowntextarea.js"></script> <script src="/static/markdowntextarea.js?v=2"></script>
{% block scriptextra %}{% endblock %} {% block scriptextra %}{% endblock %}
</body> </body>

View File

@@ -58,9 +58,9 @@
{%- endmacro %} {%- endmacro %}
{% macro form_scripts() -%} {% macro form_scripts() -%}
<link href="/static/libs/jquery-ui.min.css?v=2" rel="stylesheet" type="text/css"> <link href="/static/libs/jquery-ui.min.css" rel="stylesheet" type="text/css">
<script src="/static/libs/jquery-ui.min.js?v=2"></script> <script src="/static/libs/jquery-ui.min.js"></script>
<script src="/static/tagselector.js?v=3"></script> <script src="/static/tagselector.js?v=4"></script>
{% endmacro %} {% endmacro %}
{% macro package_lists() -%} {% macro package_lists() -%}
@@ -113,38 +113,6 @@
</div> </div>
{% endmacro %} {% endmacro %}
{% macro render_mpackage_field(field, label=None, label_visible=true, right_url=None, right_label=None) -%}
<div class="form-group {% if field.errors %}has-danger{% endif %} {{ kwargs.pop('class_', '') }}">
{% if field.type != 'HiddenField' and label_visible %}
{% if not label %}{% set label=field.label.text %}{% endif %}
<label for="{{ field.id }}">{{ label|safe }}</label>
{% endif %}
<div class="metapackage_selector bulletselector form-control">
<input type="text" placeholder="Comma-seperated values">
<div class="clearboth"></div>
</div>
{{ field(class_='form-control', **kwargs) }}
<div class="invalid-remaining invalid-feedback"></div>
{{ render_errors(field) }}
</div>
{% endmacro %}
{% macro render_deps_field(field, label=None, label_visible=true, right_url=None, right_label=None) -%}
<div class="form-group {% if field.errors %}has-danger{% endif %} {{ kwargs.pop('class_', '') }}">
{% if field.type != 'HiddenField' and label_visible %}
{% if not label %}{% set label=field.label.text %}{% endif %}
<label for="{{ field.id }}">{{ label|safe }}</label>
{% endif %}
<div class="deps_selector bulletselector form-control">
<input type="text" placeholder="Comma-seperated values">
<div class="clearboth"></div>
</div>
{{ field(class_='form-control', **kwargs) }}
<div class="invalid-remaining invalid-feedback"></div>
{{ render_errors(field) }}
</div>
{% endmacro %}
{% macro render_checkbox_field(field, label=None) -%} {% macro render_checkbox_field(field, label=None) -%}
{% if not label %}{% set label=field.label.text %}{% endif %} {% if not label %}{% set label=field.label.text %}{% endif %}
<div class="checkbox {{ kwargs.pop('class_', '') }}"> <div class="checkbox {{ kwargs.pop('class_', '') }}">

View File

@@ -8,7 +8,7 @@
{% endblock %} {% endblock %}
{% from "macros/forms.html" import render_field, render_field_prefix_button, render_submit_field, form_scripts, {% from "macros/forms.html" import render_field, render_field_prefix_button, render_submit_field, form_scripts,
render_multiselect_field, render_mpackage_field, render_deps_field, package_lists %} render_multiselect_field, package_lists %}
{% block scriptextra %} {% block scriptextra %}
{{ form_scripts() }} {{ form_scripts() }}
@@ -87,19 +87,6 @@
{{ render_field(form.desc, class_="pkg_meta", fieldclass="form-control markdown") }} {{ render_field(form.desc, class_="pkg_meta", fieldclass="form-control markdown") }}
</fieldset> </fieldset>
<fieldset class="pkg_meta">
<legend class="not_txp">{{ _("Dependencies") }}</legend>
<p class="alert alert-secondary not_txp">
<span class="fas fa-info pr-1"></span>
{{ _("Note: This information is now updated automatically when creating a release.") }}
</p>
{# {{ render_deps_field(form.harddep_str, class_="not_txp not_game", placeholder="Comma separated list") }}
{{ render_deps_field(form.softdep_str, class_="not_txp not_game", placeholder="Comma separated list") }} #}
</fieldset>
<fieldset class="mt-4"> <fieldset class="mt-4">
<legend class="pkg_meta">{{ _("Repository and Links") }}</legend> <legend class="pkg_meta">{{ _("Repository and Links") }}</legend>

View File

@@ -77,7 +77,7 @@
{% endblock %} {% endblock %}
{% block scriptextra %} {% block scriptextra %}
<script src="/static/libs/jquery-ui.min.js?v=2"></script> <script src="/static/libs/jquery-ui.min.js"></script>
<script> <script>
function update() { function update() {
const elements = Array.from(document.getElementsByClassName("sortable")[0].children); const elements = Array.from(document.getElementsByClassName("sortable")[0].children);