non native dropdown
This commit is contained in:
83
app.js
83
app.js
@@ -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);
|
||||
|
||||
|
||||
14
index.html
14
index.html
@@ -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>
|
||||
|
||||
|
||||
101
styles.css
101
styles.css
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user