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 }}) -->
|
||||
{% if field_type != 'hidden' and field_label %}
|
||||
<label for="{{ field_name }}"
|
||||
{% if label_attrs %}{% for k,v in label_attrs.items() %}
|
||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
||||
{% endfor %}{% endif %}>
|
||||
{% if link_href %}
|
||||
<a href="{{ link_href }}" class="link-success link-underline link-underline-opacity-0 fw-semibold">
|
||||
<label for="{{ field_name }}" {% if label_attrs %}{% for k,v in label_attrs.items() %} {{k}}{% if v is not sameas true
|
||||
%}="{{ v }}" {% endif %} {% endfor %}{% endif %}>
|
||||
{% if link_href %}
|
||||
<a href="{{ link_href }}" class="link-success link-underline link-underline-opacity-0 fw-semibold">
|
||||
{% endif %}
|
||||
{{ field_label }}
|
||||
{{ field_label }}
|
||||
{% if link_href %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</label>
|
||||
</a>
|
||||
{% endif %}
|
||||
</label>
|
||||
{% endif %}
|
||||
|
||||
{% if field_type == 'select' %}
|
||||
{#
|
||||
<select name="{{ field_name }}" id="{{ field_name }}"
|
||||
{% if attrs %}{% for k,v in attrs.items() %}
|
||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
||||
{% endfor %}{% endif %}
|
||||
{%- if not options %} disabled{% endif %}>
|
||||
{% if options %}
|
||||
<option value="">-- Select --</option>
|
||||
{% for opt in options %}
|
||||
<option value="{{ opt.value }}" {% if opt.value|string == value|string %}selected{% endif %}>
|
||||
{{ 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'] %}
|
||||
<select name="{{ field_name }}" id="{{ field_name }}" {% if attrs %}{% for k,v in attrs.items() %} {{k}}{% if v is not
|
||||
sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif %} {%- if not options %} disabled{% endif %}>
|
||||
{% if options %}
|
||||
<option value="">-- Select --</option>
|
||||
{% for opt in options %}
|
||||
<option value="{{ opt.value }}" {% if opt.value|string==value|string %}selected{% endif %}>
|
||||
{{ opt.label }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% set sel_label = "-- Select --" %}
|
||||
<option value="">-- No selection available --</option>
|
||||
{% 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' %}
|
||||
<textarea name="{{ field_name }}" id="{{ field_name }}"
|
||||
{% if attrs %}{% for k,v in attrs.items() %}
|
||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
||||
{% endfor %}{% endif %}>{{ value if value else "" }}</textarea>
|
||||
<textarea name="{{ field_name }}" id="{{ field_name }}" {% if attrs %}{% for k,v in attrs.items() %} {{k}}{% if v is not
|
||||
sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif %}>{{ value if value else "" }}</textarea>
|
||||
|
||||
{% elif field_type == 'checkbox' %}
|
||||
<input type="checkbox" name="{{ field_name }}" id="{{ field_name }}" value="1"
|
||||
{% if value %}checked{% endif %}
|
||||
{% if attrs %}{% for k,v in attrs.items() %}
|
||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
||||
{% endfor %}{% endif %}>
|
||||
<input type="checkbox" name="{{ field_name }}" id="{{ field_name }}" value="1" {% if value %}checked{% endif %} {% if
|
||||
attrs %}{% for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif
|
||||
%}>
|
||||
|
||||
{% 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' %}
|
||||
<div {% if attrs %}{% for k,v in attrs.items() %}
|
||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
||||
{% endfor %}{% endif %}>{{ value_label if value_label else (value if value else "") }}</div>
|
||||
<div {% if attrs %}{% for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor
|
||||
%}{% endif %}>{{ value_label if value_label else (value if value else "") }}</div>
|
||||
|
||||
{% elif field_type == "date" %}
|
||||
<input type="date" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}"
|
||||
{% if attrs %}{% for k,v in attrs.items() %}
|
||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
||||
{% endfor %}{% endif %}>
|
||||
<input type="date" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}" {% if attrs %}{%
|
||||
for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif %}>
|
||||
|
||||
{% elif field_type == "time" %}
|
||||
<input type="time" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}"
|
||||
{% if attrs %}{% for k,v in attrs.items() %}
|
||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
||||
{% endfor %}{% endif %}>
|
||||
<input type="time" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}" {% if attrs %}{%
|
||||
for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif %}>
|
||||
|
||||
{% elif field_type == "datetime" %}
|
||||
<input type="datetime-local" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}"
|
||||
{% if attrs %}{% for k,v in attrs.items() %}
|
||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
||||
{% endfor %}{% endif %}>
|
||||
<input type="datetime-local" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}" {% if
|
||||
attrs %}{% for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif
|
||||
%}>
|
||||
|
||||
{% else %}
|
||||
<input type="text" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}"
|
||||
{% if attrs %}{% for k,v in attrs.items() %}
|
||||
{{k}}{% if v is not sameas true %}="{{ v }}"{% endif %}
|
||||
{% endfor %}{% endif %}>
|
||||
<input type="text" name="{{ field_name }}" id="{{ field_name }}" value="{{ value if value else "" }}" {% if attrs %}{%
|
||||
for k,v in attrs.items() %} {{k}}{% if v is not sameas true %}="{{ v }}" {% endif %} {% endfor %}{% endif %}>
|
||||
{% endif %}
|
||||
|
||||
{% if help %}
|
||||
<div class="form-text">{{ help }}</div>
|
||||
{% endif %}
|
||||
<div class="form-text">{{ help }}</div>
|
||||
{% endif %}
|
||||
|
|
@ -1,8 +1,16 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block styleincludes %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/dropdown.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="container mt-5">
|
||||
{{ form | safe }}
|
||||
</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