non native dropdown
This commit is contained in:
83
app.js
83
app.js
@@ -10,7 +10,8 @@ let currentBarcodeNumber = null;
|
|||||||
|
|
||||||
// DOM Elements
|
// DOM Elements
|
||||||
const form = document.getElementById('stickerForm');
|
const form = document.getElementById('stickerForm');
|
||||||
const garageSelect = document.getElementById('garage');
|
const garageSelectContainer = document.getElementById('garageSelect');
|
||||||
|
const garageSelectInput = document.getElementById('garage');
|
||||||
const regNumberInput = document.getElementById('regNumber');
|
const regNumberInput = document.getElementById('regNumber');
|
||||||
const fromDateInput = document.getElementById('fromDate');
|
const fromDateInput = document.getElementById('fromDate');
|
||||||
const toDateInput = document.getElementById('toDate');
|
const toDateInput = document.getElementById('toDate');
|
||||||
@@ -25,8 +26,9 @@ async function init() {
|
|||||||
setDefaultDates();
|
setDefaultDates();
|
||||||
await loadTemplateList();
|
await loadTemplateList();
|
||||||
setupEventListeners();
|
setupEventListeners();
|
||||||
|
setupCustomSelect();
|
||||||
generateBarcodeNumber();
|
generateBarcodeNumber();
|
||||||
await loadTemplate(garageSelect.value);
|
await loadTemplate(garageSelectInput.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load template list from index.json
|
// Load template list from index.json
|
||||||
@@ -35,12 +37,24 @@ async function loadTemplateList() {
|
|||||||
const response = await fetch('templates/index.json');
|
const response = await fetch('templates/index.json');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
garageSelect.innerHTML = '';
|
const optionsContainer = garageSelectContainer.querySelector('.custom-select-options');
|
||||||
data.templates.forEach(template => {
|
const valueDisplay = garageSelectContainer.querySelector('.custom-select-value');
|
||||||
const option = document.createElement('option');
|
optionsContainer.innerHTML = '';
|
||||||
option.value = template.id;
|
|
||||||
|
data.templates.forEach((template, index) => {
|
||||||
|
const option = document.createElement('div');
|
||||||
|
option.className = 'custom-select-option';
|
||||||
|
option.dataset.value = template.id;
|
||||||
option.textContent = template.name;
|
option.textContent = template.name;
|
||||||
garageSelect.appendChild(option);
|
|
||||||
|
if (index === 0) {
|
||||||
|
option.classList.add('selected');
|
||||||
|
valueDisplay.textContent = template.name;
|
||||||
|
valueDisplay.classList.remove('placeholder');
|
||||||
|
garageSelectInput.value = template.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsContainer.appendChild(option);
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load template list:', error);
|
console.error('Failed to load template list:', error);
|
||||||
@@ -170,13 +184,58 @@ function generateBarcodeNumber() {
|
|||||||
currentBarcodeNumber = Math.floor(1000000000 + Math.random() * 9000000000).toString();
|
currentBarcodeNumber = Math.floor(1000000000 + Math.random() * 9000000000).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup event listeners
|
// Setup custom select dropdown
|
||||||
function setupEventListeners() {
|
function setupCustomSelect() {
|
||||||
// Template change
|
const trigger = garageSelectContainer.querySelector('.custom-select-trigger');
|
||||||
garageSelect.addEventListener('change', async () => {
|
const optionsContainer = garageSelectContainer.querySelector('.custom-select-options');
|
||||||
await loadTemplate(garageSelect.value);
|
const valueDisplay = garageSelectContainer.querySelector('.custom-select-value');
|
||||||
|
|
||||||
|
// Toggle dropdown on trigger click
|
||||||
|
trigger.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
garageSelectContainer.classList.toggle('open');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle option selection
|
||||||
|
optionsContainer.addEventListener('click', async (e) => {
|
||||||
|
const option = e.target.closest('.custom-select-option');
|
||||||
|
if (!option) return;
|
||||||
|
|
||||||
|
// Update selected state
|
||||||
|
optionsContainer.querySelectorAll('.custom-select-option').forEach(opt => {
|
||||||
|
opt.classList.remove('selected');
|
||||||
|
});
|
||||||
|
option.classList.add('selected');
|
||||||
|
|
||||||
|
// Update display and hidden input
|
||||||
|
valueDisplay.textContent = option.textContent;
|
||||||
|
valueDisplay.classList.remove('placeholder');
|
||||||
|
garageSelectInput.value = option.dataset.value;
|
||||||
|
|
||||||
|
// Close dropdown
|
||||||
|
garageSelectContainer.classList.remove('open');
|
||||||
|
|
||||||
|
// Load the selected template
|
||||||
|
await loadTemplate(option.dataset.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close dropdown when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!garageSelectContainer.contains(e.target)) {
|
||||||
|
garageSelectContainer.classList.remove('open');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close on escape key
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
garageSelectContainer.classList.remove('open');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup event listeners
|
||||||
|
function setupEventListeners() {
|
||||||
// Real-time preview updates
|
// Real-time preview updates
|
||||||
const updatePreview = () => renderTemplate(currentTemplateId);
|
const updatePreview = () => renderTemplate(currentTemplateId);
|
||||||
|
|
||||||
|
|||||||
14
index.html
14
index.html
@@ -60,12 +60,16 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<!-- Garage Selection -->
|
<!-- Garage Selection -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label" for="garage">Select Garage</label>
|
<label class="form-label">Select Garage</label>
|
||||||
<div class="select-wrapper">
|
<div class="custom-select" id="garageSelect">
|
||||||
<select id="garage" class="form-select" required form="stickerForm">
|
<div class="custom-select-trigger">
|
||||||
|
<span class="custom-select-value">Select a garage...</span>
|
||||||
|
<span class="material-icons custom-select-icon">expand_more</span>
|
||||||
|
</div>
|
||||||
|
<div class="custom-select-options">
|
||||||
<!-- Populated dynamically from templates/index.json -->
|
<!-- Populated dynamically from templates/index.json -->
|
||||||
</select>
|
</div>
|
||||||
<span class="material-icons select-icon">expand_more</span>
|
<input type="hidden" id="garage" name="garage" required form="stickerForm">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
101
styles.css
101
styles.css
@@ -390,6 +390,107 @@ body {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Custom Select Dropdown */
|
||||||
|
.custom-select {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-trigger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid var(--md-sys-color-outline-variant);
|
||||||
|
border-radius: var(--md-sys-shape-corner-medium);
|
||||||
|
background-color: var(--md-sys-color-surface-container);
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
|
font: var(--md-sys-typescale-body-large);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-trigger:hover {
|
||||||
|
border-color: var(--md-sys-color-outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select.open .custom-select-trigger {
|
||||||
|
border-color: var(--md-sys-color-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-value {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-value.placeholder {
|
||||||
|
color: var(--md-sys-color-outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-icon {
|
||||||
|
color: var(--md-sys-color-outline);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select.open .custom-select-icon {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-options {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 4px);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: var(--md-sys-color-surface);
|
||||||
|
border: 1px solid var(--md-sys-color-outline-variant);
|
||||||
|
border-radius: var(--md-sys-shape-corner-medium);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 100;
|
||||||
|
max-height: 240px;
|
||||||
|
overflow-y: auto;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transform: translateY(-8px);
|
||||||
|
transition: opacity 0.2s, transform 0.2s, visibility 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select.open .custom-select-options {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-option {
|
||||||
|
padding: 12px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.15s;
|
||||||
|
font: var(--md-sys-typescale-body-large);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-option:hover {
|
||||||
|
background-color: var(--md-sys-color-surface-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-option.selected {
|
||||||
|
background-color: var(--md-sys-color-primary);
|
||||||
|
color: var(--md-sys-color-on-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-option:first-child {
|
||||||
|
border-radius: var(--md-sys-shape-corner-medium) var(--md-sys-shape-corner-medium) 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-option:last-child {
|
||||||
|
border-radius: 0 0 var(--md-sys-shape-corner-medium) var(--md-sys-shape-corner-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-option:only-child {
|
||||||
|
border-radius: var(--md-sys-shape-corner-medium);
|
||||||
|
}
|
||||||
|
|
||||||
/* Form Row */
|
/* Form Row */
|
||||||
.form-row {
|
.form-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
Reference in New Issue
Block a user