New dropdown widget!
This commit is contained in:
parent
43b3df9938
commit
5718deee6b
4 changed files with 163 additions and 63 deletions
4
inventory/static/css/components/dropdown.css
Normal file
4
inventory/static/css/components/dropdown.css
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
.inventory-dropdown {
|
||||||
|
border-color: rgb(222, 226, 230);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
95
inventory/static/js/components/dropdown.js
Normal file
95
inventory/static/js/components/dropdown.js
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
const DropDown = globalThis.DropDown ?? (globalThis.DropDown = {});
|
||||||
|
|
||||||
|
DropDown.utilities = {
|
||||||
|
filterList(id) {
|
||||||
|
value = document.getElementById(`${id}-filter`).value;
|
||||||
|
list = document.querySelectorAll(`#${id}-dropdown li`);
|
||||||
|
|
||||||
|
list.forEach(item => {
|
||||||
|
const txt = item.textContent.toLowerCase();
|
||||||
|
if (txt.includes(value)) {
|
||||||
|
item.style.display = 'list-item';
|
||||||
|
} else {
|
||||||
|
item.style.display = 'none';
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
selectItem(id, value) {
|
||||||
|
const btn = document.getElementById(`${id}-button`);
|
||||||
|
const txt = document.getElementById(`${id}-${value}`).textContent;
|
||||||
|
const inp = document.getElementById(id);
|
||||||
|
|
||||||
|
btn.dataset.value = value;
|
||||||
|
btn.textContent = txt;
|
||||||
|
|
||||||
|
inp.value = value;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
const VISIBLE_ITEMS = 10;
|
||||||
|
|
||||||
|
function setMenuMaxHeight(buttonEl) {
|
||||||
|
const menu = buttonEl?.nextElementSibling;
|
||||||
|
if (!menu || !menu.classList.contains('dropdown-menu')) return;
|
||||||
|
|
||||||
|
const input = menu.querySelector('input.form-control');
|
||||||
|
const firstItem = menu.querySelector('.dropdown-item');
|
||||||
|
if (!firstItem) return;
|
||||||
|
|
||||||
|
// Measure even if the menu is closed
|
||||||
|
const computed = getComputedStyle(menu);
|
||||||
|
const wasHidden = computed.display === 'none' || computed.visibility === 'hidden';
|
||||||
|
if (wasHidden) {
|
||||||
|
menu.style.visibility = 'hidden';
|
||||||
|
menu.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputH = input ? input.getBoundingClientRect().height : 0;
|
||||||
|
const itemH = firstItem.getBoundingClientRect().height || 0;
|
||||||
|
const itemCount = Math.min(
|
||||||
|
VISIBLE_ITEMS,
|
||||||
|
menu.querySelectorAll('.dropdown-item').length
|
||||||
|
);
|
||||||
|
|
||||||
|
const target = Math.ceil(inputH + itemH * itemCount);
|
||||||
|
menu.style.maxHeight = `${target + 10}px`;
|
||||||
|
menu.style.overflowY = 'auto';
|
||||||
|
|
||||||
|
if (wasHidden) {
|
||||||
|
menu.style.display = '';
|
||||||
|
menu.style.visibility = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onShow(e) {
|
||||||
|
setMenuMaxHeight(e.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onResize() {
|
||||||
|
document.querySelectorAll('.dropdown-toggle[data-bs-toggle="dropdown"]').forEach(btn => {
|
||||||
|
const menu = btn.nextElementSibling;
|
||||||
|
if (menu && menu.classList.contains('dropdown-menu') && menu.classList.contains('show')) {
|
||||||
|
setMenuMaxHeight(btn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(root = document) {
|
||||||
|
// Delegate so dynamically-added dropdowns work too
|
||||||
|
root.addEventListener('show.bs.dropdown', onShow);
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose for manyal calls or tests
|
||||||
|
DropDown.utilities.setMenuMaxHeight = setMenuMaxHeight;
|
||||||
|
DropDown.init = init;
|
||||||
|
|
||||||
|
// Auto-init
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => init());
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
@ -1,91 +1,84 @@
|
||||||
<!-- FIELD: {{ field_name }} ({{ field_type }}) -->
|
<!-- FIELD: {{ field_name }} ({{ field_type }}) -->
|
||||||
{% if field_type != 'hidden' and field_label %}
|
{% if field_type != 'hidden' and field_label %}
|
||||||
<label for="{{ field_name }}"
|
<label for="{{ field_name }}" {% if label_attrs %}{% for k,v in label_attrs.items() %} {{k}}{% if v is not sameas true
|
||||||
{% if label_attrs %}{% for k,v in label_attrs.items() %}
|
%}="{{ v }}" {% endif %} {% endfor %}{% endif %}>
|
||||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
{% if link_href %}
|
||||||
{% endfor %}{% endif %}>
|
<a href="{{ link_href }}" class="link-success link-underline link-underline-opacity-0 fw-semibold">
|
||||||
{% if link_href %}
|
|
||||||
<a href="{{ link_href }}" class="link-success link-underline link-underline-opacity-0 fw-semibold">
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ field_label }}
|
{{ field_label }}
|
||||||
{% if link_href %}
|
{% if link_href %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</label>
|
</label>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if field_type == 'select' %}
|
{% if field_type == 'select' %}
|
||||||
{#
|
{#
|
||||||
<select name="{{ field_name }}" id="{{ field_name }}"
|
<select name="{{ field_name }}" id="{{ field_name }}" {% if attrs %}{% for k,v in attrs.items() %} {{k}}{% if v is not
|
||||||
{% if attrs %}{% for k,v in attrs.items() %}
|
sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif %} {%- if not options %} disabled{% endif %}>
|
||||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
{% if options %}
|
||||||
{% endfor %}{% endif %}
|
<option value="">-- Select --</option>
|
||||||
{%- if not options %} disabled{% endif %}>
|
{% for opt in options %}
|
||||||
{% if options %}
|
<option value="{{ opt.value }}" {% if opt.value|string==value|string %}selected{% endif %}>
|
||||||
<option value="">-- Select --</option>
|
{{ opt.label }}
|
||||||
{% for opt in options %}
|
</option>
|
||||||
<option value="{{ opt.value }}" {% if opt.value|string == value|string %}selected{% endif %}>
|
{% endfor %}
|
||||||
{{ opt.label }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
<option value="">-- No selection available --</option>
|
|
||||||
{% endif %}
|
|
||||||
</select>
|
|
||||||
#}
|
|
||||||
{% if value %}
|
|
||||||
{% set sel_label = (options | selectattr('value', 'equalto', value) | first)['label'] %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{% set sel_label = "-- Select --" %}
|
<option value="">-- No selection available --</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button type="button" class="btn btn-outline-secondary d-block w-100" id="{{ field_name }}-button" data-value="{{ value }}">{{ sel_label }}</button>
|
</select>
|
||||||
|
#}
|
||||||
|
{% if value %}
|
||||||
|
{% set sel_label = (options | selectattr('value', 'equalto', value) | first)['label'] %}
|
||||||
|
{% else %}
|
||||||
|
{% set sel_label = "-- Select --" %}
|
||||||
|
{% endif %}
|
||||||
|
<button type="button" class="btn btn-outline-dark d-block w-100 text-start dropdown-toggle inventory-dropdown"
|
||||||
|
id="{{ field_name }}-button" data-bs-toggle="dropdown" data-value="{{ value }}">{{ sel_label }}</button>
|
||||||
|
<div class="dropdown-menu pt-0" id="{{ field_name }}-dropdown">
|
||||||
|
<input type="text" class="form-control mt-0 border-top-0 border-start-0 border-end-0 rounded-bottom-0"
|
||||||
|
id="{{ field_name }}-filter" placeholder="Filter..." oninput="DropDown.utilities.filterList('{{ field_name }}')">
|
||||||
|
{% for opt in options %}
|
||||||
|
<li><a class="dropdown-item{% if opt.value|string == value|string %} active{% endif %}"
|
||||||
|
data-value="{{ opt['value'] }}" onclick="DropDown.utilities.selectItem('{{ field_name }}', '{{ opt['value'] }}')" id="{{ field_name }}-{{ opt['value'] }}">{{ opt['label'] }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="{{ field_name }}" id="{{ field_name }}" value="{{ value }}">
|
||||||
|
|
||||||
{% elif field_type == 'textarea' %}
|
{% elif field_type == 'textarea' %}
|
||||||
<textarea name="{{ field_name }}" id="{{ field_name }}"
|
<textarea name="{{ field_name }}" id="{{ field_name }}" {% if attrs %}{% for k,v in attrs.items() %} {{k}}{% if v is not
|
||||||
{% if attrs %}{% for k,v in attrs.items() %}
|
sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif %}>{{ value if value else "" }}</textarea>
|
||||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
|
||||||
{% endfor %}{% endif %}>{{ value if value else "" }}</textarea>
|
|
||||||
|
|
||||||
{% elif field_type == 'checkbox' %}
|
{% elif field_type == 'checkbox' %}
|
||||||
<input type="checkbox" name="{{ field_name }}" id="{{ field_name }}" value="1"
|
<input type="checkbox" name="{{ field_name }}" id="{{ field_name }}" value="1" {% if value %}checked{% endif %} {% if
|
||||||
{% if value %}checked{% endif %}
|
attrs %}{% for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif
|
||||||
{% if attrs %}{% for k,v in attrs.items() %}
|
%}>
|
||||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
|
||||||
{% endfor %}{% endif %}>
|
|
||||||
|
|
||||||
{% elif field_type == 'hidden' %}
|
{% elif field_type == 'hidden' %}
|
||||||
<input type="hidden" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}">
|
<input type="hidden" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}">
|
||||||
|
|
||||||
{% elif field_type == 'display' %}
|
{% elif field_type == 'display' %}
|
||||||
<div {% if attrs %}{% for k,v in attrs.items() %}
|
<div {% if attrs %}{% for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor
|
||||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
%}{% endif %}>{{ value_label if value_label else (value if value else "") }}</div>
|
||||||
{% endfor %}{% endif %}>{{ value_label if value_label else (value if value else "") }}</div>
|
|
||||||
|
|
||||||
{% elif field_type == "date" %}
|
{% elif field_type == "date" %}
|
||||||
<input type="date" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}"
|
<input type="date" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}" {% if attrs %}{%
|
||||||
{% if attrs %}{% for k,v in attrs.items() %}
|
for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif %}>
|
||||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
|
||||||
{% endfor %}{% endif %}>
|
|
||||||
|
|
||||||
{% elif field_type == "time" %}
|
{% elif field_type == "time" %}
|
||||||
<input type="time" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}"
|
<input type="time" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}" {% if attrs %}{%
|
||||||
{% if attrs %}{% for k,v in attrs.items() %}
|
for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif %}>
|
||||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
|
||||||
{% endfor %}{% endif %}>
|
|
||||||
|
|
||||||
{% elif field_type == "datetime" %}
|
{% elif field_type == "datetime" %}
|
||||||
<input type="datetime-local" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}"
|
<input type="datetime-local" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}" {% if
|
||||||
{% if attrs %}{% for k,v in attrs.items() %}
|
attrs %}{% for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif
|
||||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
%}>
|
||||||
{% endfor %}{% endif %}>
|
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<input type="text" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}"
|
<input type="text" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}" {% if attrs %}{%
|
||||||
{% if attrs %}{% for k,v in attrs.items() %}
|
for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif %}>
|
||||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
|
||||||
{% endfor %}{% endif %}>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if help %}
|
{% if help %}
|
||||||
<div class="form-text">{{ help }}</div>
|
<div class="form-text">{{ help }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
@ -1,8 +1,16 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block styleincludes %}
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/dropdown.css') }}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
<div class="container mt-5">
|
<div class="container mt-5">
|
||||||
{{ form | safe }}
|
{{ form | safe }}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scriptincludes %}
|
||||||
|
<script src="{{ url_for('static', filename='js/components/dropdown.js') }}" defer></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue