Refactor dropdown and table components, add dynamic table functionality, and update stylesheets for improved UI

This commit is contained in:
Yaro Kasear 2025-08-18 15:05:24 -05:00
parent 3d20462c71
commit 32993800fe
7 changed files with 89 additions and 8 deletions

View file

@ -0,0 +1,4 @@
.dropdown-search-input:focus {
outline: none !important;
box-shadow: none !important;
}

View file

@ -15,6 +15,8 @@ function DropDown(cfg) {
this.selectedId = v;
this.$refs.clear?.classList.remove('d-none');
}
this.$refs.button.addEventListener('shown.bs.dropdown', (e) => this.onShown(e));
},
itemSelect(e) {
@ -79,6 +81,27 @@ function DropDown(cfg) {
}
this.$dispatch('dropdown:cleared', {});
},
onShown() {
const { menu, search, content } = this.$refs || {};
if (!menu || !search || !content) return;
requestAnimationFrame(() => {
const viewportH = window.innerHeight;
const menuTop = menu.getBoundingClientRect().top;
const capByViewport = viewportH * 0.40;
const spaceBelow = viewportH - menuTop - 12;
const menuCap = Math.max(0, Math.min(capByViewport, spaceBelow));
const inputH = search.offsetHeight || 0;
const contentMax = Math.max(0, menuCap - inputH);
content.style.maxHeight = `${contentMax - 2}px`;
requestAnimationFrame(() => search.focus());
});
}
};
}

View file

@ -15,7 +15,7 @@
recordId: {{ record_id|tojson if record_id else "null" }},
field: {{ field_name|tojson if field_name else "null" }}
})'
hx-preserve x-init="init()">
hx-preserve x-init="init()" x-ref="dropdown">
<div class="btn-group w-100">
<button
class="btn btn-outline-dark dropdown-toggle overflow-x-hidden w-100 rounded-end{% if current_item and enabled %}-0 border-end-0{% endif %} dropdown-button"
@ -34,11 +34,11 @@
{{ icons.render_icon('x-lg', 16) }}
</button>
<input type="hidden" name="{{ id }}" id="{{ id }}" value="{{ current_item.id if current_item else '' }}" x-ref="hidden">
<ul class="dropdown-menu w-100 pt-0" style="max-height: 40vh; z-index: 9999;" id="menu{{ id }}">
<ul class="dropdown-menu w-100 pt-0" style="max-height: 40vh; z-index: 9999;" id="menu{{ id }}" x-ref="menu">
<input type="text"
class="form-control rounded-bottom-0 border-start-0 border-top-0 border-end-0 dropdown-search-input"
id="search{{ id }}" placeholder="Search...">
<div class="overflow-auto overflow-x-hidden" style="z-index: 9999;" id="{{ id }}DropdownContent">
id="search{{ id }}" placeholder="Search..." x-ref="search">
<div class="overflow-auto overflow-x-hidden" style="z-index: 9999;" id="{{ id }}DropdownContent" x-ref="content">
{% if list %}
{% for item in list %}
<li><a class="dropdown-item" data-inv-value="{{ item.id }}">{{

View file

@ -50,3 +50,56 @@
<div class="container text-center">No data.</div>
{% endif %}
{% endmacro %}
{% macro dynamic_table(headers, rows, id, entry_route=None, title=None, per_page=15) %}
<!-- Table Fragment -->
{% if rows %}
{% if title %}
<label for="datatable-{{ id|default('table')|replace(' ', '-')|lower }}" class="form-label">{{ title }}</label>
{% endif %}
<div class="table-responsive">
<table id="datatable-{{ id|default('table')|replace(' ', '-')|lower }}"
class="table table-bordered table-sm table-hover table-striped table-light m-0{% if title %} caption-top{% endif %}">
<thead class="sticky-top">
<tr>
{% for h in headers %}
<th class="text-nowrap">{{ h }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr {% if entry_route %}onclick="window.location='{{ url_for('main.' + entry_route, id=row.id) }}'"
style="cursor: pointer;"{% endif %}{% if row['highlight'] %} class="table-info"{% endif %}>
{% for cell in row.cells %}
<td class="text-nowrap{% if cell.type=='bool' %} text-center{% endif %}">
{% if cell.type == 'bool' %}
{{ cell.html | safe }}
{% elif cell.url %}
<a class="link-success link-underline-opacity-0" href="{{ cell.url }}">{{ cell.text }}</a>
{% else %}
{{ cell.text or '-' }}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
new DataTable('#datatable-{{ id|default('table')|replace(' ', ' - ')|lower }}', {
pageLength: {{ per_page }},
scrollX: true,
scrollY: '60vh',
scrollCollapse: true,
})
})
</script>
{% else %}
<div class="container text-center">No data.</div>
{% endif %}
{% endmacro %}

View file

@ -28,7 +28,8 @@
href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.8.1/github-markdown.min.css"
integrity="sha512-BrOPA520KmDMqieeM7XFe6a3u3Sb3F1JBaQnrIAmWg3EYrciJ+Qqe6ZcKCdfPv26rGcgTrJnZ/IdQEct8h3Zhw=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/widget.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/combobox.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/dropdown.css') }}">
<style>
{% block style %}{% endblock %}
</style>

View file

@ -26,5 +26,5 @@
) }}
{% endblock %}
{% block content %}
{{ tables.render_table(headers=header, rows=rows, id='table', entry_route=entry_route) }}
{{ tables.dynamic_table(headers=header, rows=rows, id='table', entry_route=entry_route) }}
{% endblock %}