Refactor brand management UI; replace datalist with combo box widget and enhance styling for better usability
This commit is contained in:
parent
e2b8579362
commit
dba2438937
4 changed files with 82 additions and 83 deletions
20
static/css/widget.css
Normal file
20
static/css/widget.css
Normal 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; /* Bootstrap’s 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;
|
||||||
|
}
|
|
@ -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;
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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');
|
||||||
|
|
|
@ -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 %}
|
Loading…
Add table
Add a link
Reference in a new issue