added leagues
This commit is contained in:
@@ -41,6 +41,7 @@ templ DatePickerWithDefault(id, name, label, placeholder string, required bool,
|
||||
showPicker: false,
|
||||
selectedDate: defaultValue || "",
|
||||
displayDate: defaultValue || "",
|
||||
lastValidDate: defaultValue || "",
|
||||
tempYear: new Date().getFullYear(),
|
||||
tempMonth: new Date().getMonth(),
|
||||
months: [
|
||||
@@ -81,6 +82,7 @@ templ DatePickerWithDefault(id, name, label, placeholder string, required bool,
|
||||
const y = this.tempYear;
|
||||
this.displayDate = d + "/" + m + "/" + y;
|
||||
this.selectedDate = this.displayDate;
|
||||
this.lastValidDate = this.displayDate;
|
||||
this.$refs.dateInput.value = this.displayDate;
|
||||
this.showPicker = false;
|
||||
// Manually trigger input event so Alpine.js @input handler fires
|
||||
@@ -112,11 +114,107 @@ templ DatePickerWithDefault(id, name, label, placeholder string, required bool,
|
||||
this.tempYear === today.getFullYear()
|
||||
);
|
||||
},
|
||||
handleInput(event) {
|
||||
let value = event.target.value;
|
||||
const oldValue = this.displayDate;
|
||||
|
||||
// Remove non-numeric characters except /
|
||||
value = value.replace(/[^\d/]/g, '');
|
||||
|
||||
// Handle backspace - if user deletes a slash, delete the number before it too
|
||||
if (value.length < oldValue.length) {
|
||||
// User is deleting
|
||||
if (oldValue.charAt(value.length) === '/' && value.charAt(value.length - 1) !== '/') {
|
||||
// They just deleted a slash, remove the digit before it
|
||||
value = value.substring(0, value.length - 1);
|
||||
}
|
||||
} else {
|
||||
// Auto-insert slashes when typing forward
|
||||
if (value.length === 2 && !value.includes('/')) {
|
||||
value = value + '/';
|
||||
} else if (value.length === 5 && value.split('/').length === 2) {
|
||||
value = value + '/';
|
||||
}
|
||||
}
|
||||
|
||||
// Limit to DD/MM/YYYY format (10 characters)
|
||||
if (value.length > 10) {
|
||||
value = value.substring(0, 10);
|
||||
}
|
||||
|
||||
this.displayDate = value;
|
||||
event.target.value = value;
|
||||
|
||||
// Validate complete date
|
||||
if (value.length === 10) {
|
||||
const parts = value.split('/');
|
||||
if (parts.length === 3) {
|
||||
const day = parseInt(parts[0]);
|
||||
const month = parseInt(parts[1]);
|
||||
const year = parseInt(parts[2]);
|
||||
|
||||
// Basic validation
|
||||
if (month >= 1 && month <= 12 && day >= 1 && day <= 31 && year >= 2001 && year <= 2051) {
|
||||
this.selectedDate = value;
|
||||
this.lastValidDate = value;
|
||||
this.tempYear = year;
|
||||
this.tempMonth = month - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
handleBackspace(event) {
|
||||
const input = event.target;
|
||||
const cursorPos = input.selectionStart;
|
||||
const value = input.value;
|
||||
|
||||
// Check if we're about to delete a slash at position 2 or 5
|
||||
if (cursorPos === 3 && value.charAt(2) === '/') {
|
||||
// Delete both the slash and the character before it
|
||||
event.preventDefault();
|
||||
const newValue = value.substring(0, 1) + value.substring(3);
|
||||
this.displayDate = newValue;
|
||||
input.value = newValue;
|
||||
this.$nextTick(() => {
|
||||
input.setSelectionRange(1, 1);
|
||||
});
|
||||
} else if (cursorPos === 6 && value.charAt(5) === '/') {
|
||||
// Delete both the slash and the character before it
|
||||
event.preventDefault();
|
||||
const newValue = value.substring(0, 4) + value.substring(6);
|
||||
this.displayDate = newValue;
|
||||
input.value = newValue;
|
||||
this.$nextTick(() => {
|
||||
input.setSelectionRange(4, 4);
|
||||
});
|
||||
}
|
||||
// Otherwise, let default backspace behavior work
|
||||
},
|
||||
isValidDate(dateString) {
|
||||
if (!dateString || dateString.length !== 10) return false;
|
||||
const parts = dateString.split('/');
|
||||
if (parts.length !== 3) return false;
|
||||
|
||||
const day = parseInt(parts[0]);
|
||||
const month = parseInt(parts[1]);
|
||||
const year = parseInt(parts[2]);
|
||||
|
||||
// Check ranges
|
||||
if (month < 1 || month > 12) return false;
|
||||
if (year < 2001 || year > 2051) return false;
|
||||
if (day < 1 || day > 31) return false;
|
||||
|
||||
// Check valid day for month
|
||||
const daysInMonth = new Date(year, month, 0).getDate();
|
||||
if (day > daysInMonth) return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<label for={ id } class="block text-sm font-medium mb-2">{ label }</label>
|
||||
<div class="relative">
|
||||
<div class="relative" x-ref="datePickerContainer">
|
||||
<input
|
||||
type="text"
|
||||
id={ id }
|
||||
@@ -125,20 +223,23 @@ templ DatePickerWithDefault(id, name, label, placeholder string, required bool,
|
||||
class="py-3 px-4 pr-10 block w-full rounded-lg text-sm bg-base border-2 border-overlay0 focus:border-blue outline-none"
|
||||
placeholder={ placeholder }
|
||||
pattern="\d{2}/\d{2}/\d{4}"
|
||||
inputmode="numeric"
|
||||
if required {
|
||||
required
|
||||
}
|
||||
readonly
|
||||
x-model="displayDate"
|
||||
@click="showPicker = !showPicker"
|
||||
@input="handleInput($event)"
|
||||
@focus="showPicker = true"
|
||||
@keydown.backspace="handleBackspace($event)"
|
||||
@keydown.enter.prevent="if (isValidDate(displayDate)) { lastValidDate = displayDate; } else { displayDate = lastValidDate; $event.target.value = lastValidDate; } $event.target.blur(); showPicker = false;"
|
||||
if onChange != "" {
|
||||
@input={ onChange }
|
||||
@input.debounce.500ms={ onChange }
|
||||
}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-subtext0 hover:text-text"
|
||||
@click="showPicker = !showPicker"
|
||||
@click="showPicker = !showPicker; if(showPicker) { $refs.dateInput.focus(); }"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||||
@@ -148,7 +249,7 @@ templ DatePickerWithDefault(id, name, label, placeholder string, required bool,
|
||||
<div
|
||||
x-show="showPicker"
|
||||
x-cloak
|
||||
@click.away="showPicker = false"
|
||||
@click.outside="if(!$refs.datePickerContainer.contains($event.target)) { showPicker = false }"
|
||||
class="absolute z-50 mt-2 bg-mantle border-2 border-surface1 rounded-lg shadow-lg p-4 w-80"
|
||||
>
|
||||
<!-- Month/Year Navigation -->
|
||||
|
||||
Reference in New Issue
Block a user