Refactor brand management UI; replace datalist with combo box widget and enhance styling for better usability

This commit is contained in:
Yaro Kasear 2025-06-18 09:59:07 -05:00
parent e2b8579362
commit dba2438937
4 changed files with 82 additions and 83 deletions

20
static/css/widget.css Normal file
View file

@ -0,0 +1,20 @@
.combo-box-widget .form-control:focus,
.combo-box-widget .form-select:focus,
.combo-box-widget .btn:focus {
box-shadow: none !important;
outline: none !important;
border-color: #ced4da !important; /* Bootstraps default neutral border */
background-color: inherit; /* Or explicitly #fff if needed */
color: inherit;
}
.combo-box-widget .btn-primary:focus,
.combo-box-widget .btn-danger:focus {
background-color: inherit; /* Keep button from darkening */
color: inherit;
}
.combo-box-widget:focus-within {
box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25);
border-radius: 0.375rem;
}

View file

@ -1,20 +0,0 @@
document.querySelectorAll('[data-datalist-bind]').forEach(input => {
const datalistSelector = input.dataset.datalistBind;
const hiddenSelector = input.dataset.hiddenTarget;
const datalist = document.querySelector(datalistSelector);
const hidden = document.querySelector(hiddenSelector);
if (!datalist || !hidden) return;
input.addEventListener('input', function () {
const value = this.value;
const options = datalist.querySelectorAll('option');
let foundId = '';
options.forEach(option => {
if (option.value === value) {
foundId = option.dataset.id || '';
}
});
hidden.value = foundId;
});
});

View file

@ -17,21 +17,9 @@
href="https://cdn.datatables.net/v/bs5/jq-3.7.0/moment-2.29.4/dt-2.3.2/b-3.2.3/b-colvis-3.2.3/b-print-3.2.3/cr-2.1.1/cc-1.0.4/r-3.0.4/rg-1.5.1/rr-1.5.0/sc-2.4.3/sr-1.4.1/datatables.min.css" href="https://cdn.datatables.net/v/bs5/jq-3.7.0/moment-2.29.4/dt-2.3.2/b-3.2.3/b-colvis-3.2.3/b-print-3.2.3/cr-2.1.1/cc-1.0.4/r-3.0.4/rg-1.5.1/rr-1.5.0/sc-2.4.3/sr-1.4.1/datatables.min.css"
rel="stylesheet" integrity="sha384-gdnBcErvPbrURVoR9w3NhVMliw+ZmcTCmq+64xj2Ksx21nRJFX3qW0zFvBotL5rm" rel="stylesheet" integrity="sha384-gdnBcErvPbrURVoR9w3NhVMliw+ZmcTCmq+64xj2Ksx21nRJFX3qW0zFvBotL5rm"
crossorigin="anonymous"> crossorigin="anonymous">
<link rel="stylesheet" href="{{ url_for('static', filename='css/widget.css') }}">
<style> <style>
{% block style %} {% block style %}
input[type="checkbox"][disabled] {
pointer-events: none;
opacity: 1;
filter: none;
appearance: auto;
-webkit-appearance: checkbox;
background-color: white !important;
color: black !important;
}
input[type="checkbox"][disabled]::-moz-checkbox {
background-color: white;
}
{% endblock %} {% endblock %}
</style> </style>
</head> </head>
@ -74,7 +62,6 @@
src="https://cdn.datatables.net/v/bs5/jq-3.7.0/moment-2.29.4/dt-2.3.2/b-3.2.3/b-colvis-3.2.3/b-print-3.2.3/cr-2.1.1/cc-1.0.4/r-3.0.4/rg-1.5.1/rr-1.5.0/sc-2.4.3/sr-1.4.1/datatables.min.js" src="https://cdn.datatables.net/v/bs5/jq-3.7.0/moment-2.29.4/dt-2.3.2/b-3.2.3/b-colvis-3.2.3/b-print-3.2.3/cr-2.1.1/cc-1.0.4/r-3.0.4/rg-1.5.1/rr-1.5.0/sc-2.4.3/sr-1.4.1/datatables.min.js"
integrity="sha384-tNYRX2RiDDDRKCJgPF8Pw3rTxC1GUe1pt5qH1SBmwcazrEUj7Ii4C1Tz9wCCRUI4" integrity="sha384-tNYRX2RiDDDRKCJgPF8Pw3rTxC1GUe1pt5qH1SBmwcazrEUj7Ii4C1Tz9wCCRUI4"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script src="{{ url_for('static', filename='js/datalist.js') }}"></script>
<script> <script>
const searchInput = document.querySelector('#search'); const searchInput = document.querySelector('#search');
const searchButton = document.querySelector('#searchButton'); const searchButton = document.querySelector('#searchButton');

View file

@ -12,12 +12,13 @@
<div class="container"> <div class="container">
<label for="brandInput" class="form-label">Brands</label> <label for="brandInput" class="form-label">Brands</label>
<div class="combo-box-widget">
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control rounded-bottom-0" id="brandInput" name="brandInput" placeholder="Add a new brand"> <input type="text" class="form-control rounded-bottom-0" id="brandInput" name="brandInput" placeholder="Add a new brand">
<button type="button" class="btn btn-primary rounded-bottom-0" id="addBrandButton" onclick="addBrand();" disabled> <button type="button" class="btn btn-primary rounded-bottom-0" id="addBrandButton" onclick="SettingsPage.addBrand();" disabled>
{{ icons.plus(16) }} {{ icons.plus(16) }}
</button> </button>
<button type="button" class="btn btn-danger rounded-bottom-0" id="removeBrandButton" onclick="removeSelectedBrands()" disabled> <button type="button" class="btn btn-danger rounded-bottom-0" id="removeBrandButton" onclick="SettingsPage.removeSelectedBrands()" disabled>
{{ icons.minus(16) }} {{ icons.minus(16) }}
</button> </button>
</div> </div>
@ -27,33 +28,29 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
</div>
</form> </form>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
const brandInput = document.querySelector('#brandInput'); const SettingsPage = (() => {
const brandList = document.querySelector('#brandList'); const brandInput = document.querySelector('#brandInput');
const addBrandButton = document.querySelector('#addBrandButton'); const brandList = document.querySelector('#brandList');
const removeBrandButton = document.querySelector('#removeBrandButton'); const addBrandButton = document.querySelector('#addBrandButton');
const removeBrandButton = document.querySelector('#removeBrandButton');
let tempIdCounter = -1;
brandInput.addEventListener('input', () => { function init() {
brandInput.addEventListener('input', () => {
addBrandButton.disabled = brandInput.value.trim() === ''; addBrandButton.disabled = brandInput.value.trim() === '';
}); });
brandList.addEventListener('change', () => { brandList.addEventListener('change', () => {
removeBrandButton.disabled = brandList.selectedOptions.length === 0; removeBrandButton.disabled = brandList.selectedOptions.length === 0;
}); });
}
function sortOptions() { function addBrand() {
const options = Array.from(brandList.options);
options.sort((a, b) => a.text.localeCompare(b.text));
brandList.innerHTML = '';
options.forEach(option => brandList.appendChild(option));
}
let tempIdCounter = -1;
function addBrand() {
const newBrand = brandInput.value.trim(); const newBrand = brandInput.value.trim();
if (newBrand) { if (newBrand) {
const option = document.createElement('option'); const option = document.createElement('option');
@ -64,10 +61,25 @@ function addBrand() {
addBrandButton.disabled = true; addBrandButton.disabled = true;
sortOptions(); sortOptions();
} }
} }
function removeSelectedBrands() { function removeSelectedBrands() {
Array.from(brandList.selectedOptions).forEach(option => option.remove()); Array.from(brandList.selectedOptions).forEach(option => option.remove());
removeBrandButton.disabled = true; removeBrandButton.disabled = true;
} }
function sortOptions() {
Array.from(brandList.options)
.sort((a, b) => a.text.localeCompare(b.text))
.forEach(option => brandList.appendChild(option));
}
return {
init,
addBrand,
removeSelectedBrands
};
})();
document.addEventListener('DOMContentLoaded', SettingsPage.init);
{% endblock %} {% endblock %}