non native dropdown

This commit is contained in:
2026-03-22 23:52:25 +05:00
parent 06a7f3a5ed
commit ce704f4ada
3 changed files with 181 additions and 17 deletions

83
app.js
View File

@@ -10,7 +10,8 @@ let currentBarcodeNumber = null;
// DOM Elements
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 fromDateInput = document.getElementById('fromDate');
const toDateInput = document.getElementById('toDate');
@@ -25,8 +26,9 @@ async function init() {
setDefaultDates();
await loadTemplateList();
setupEventListeners();
setupCustomSelect();
generateBarcodeNumber();
await loadTemplate(garageSelect.value);
await loadTemplate(garageSelectInput.value);
}
// Load template list from index.json
@@ -35,12 +37,24 @@ async function loadTemplateList() {
const response = await fetch('templates/index.json');
const data = await response.json();
garageSelect.innerHTML = '';
data.templates.forEach(template => {
const option = document.createElement('option');
option.value = template.id;
const optionsContainer = garageSelectContainer.querySelector('.custom-select-options');
const valueDisplay = garageSelectContainer.querySelector('.custom-select-value');
optionsContainer.innerHTML = '';
data.templates.forEach((template, index) => {
const option = document.createElement('div');
option.className = 'custom-select-option';
option.dataset.value = template.id;
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) {
console.error('Failed to load template list:', error);
@@ -170,13 +184,58 @@ function generateBarcodeNumber() {
currentBarcodeNumber = Math.floor(1000000000 + Math.random() * 9000000000).toString();
}
// Setup event listeners
function setupEventListeners() {
// Template change
garageSelect.addEventListener('change', async () => {
await loadTemplate(garageSelect.value);
// Setup custom select dropdown
function setupCustomSelect() {
const trigger = garageSelectContainer.querySelector('.custom-select-trigger');
const optionsContainer = garageSelectContainer.querySelector('.custom-select-options');
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
const updatePreview = () => renderTemplate(currentTemplateId);

View File

@@ -60,12 +60,16 @@
<div class="card-body">
<!-- Garage Selection -->
<div class="form-group">
<label class="form-label" for="garage">Select Garage</label>
<div class="select-wrapper">
<select id="garage" class="form-select" required form="stickerForm">
<label class="form-label">Select Garage</label>
<div class="custom-select" id="garageSelect">
<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 -->
</select>
<span class="material-icons select-icon">expand_more</span>
</div>
<input type="hidden" id="garage" name="garage" required form="stickerForm">
</div>
</div>

View File

@@ -390,6 +390,107 @@ body {
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 {
display: grid;