fix: clear date selection when clicking an unavailable/closed day
bits-ui blocks onValueChange for unavailable dates, leaving stale selection. Adds native click detection on [data-unavailable] elements to call onchange(undefined) so the time picker hides and the user knows nothing was picked.
This commit is contained in:
@@ -485,6 +485,21 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Clear selection when navigating to a month that doesn't contain the selected date.
|
||||
// Runs AFTER all synchronous state changes settle, so clicking a date in a different
|
||||
// month (fires both onPlaceholderChange and onValueChange) keeps the new selection,
|
||||
// while clicking prev/next arrows without picking a date clears it.
|
||||
$effect(() => {
|
||||
if (
|
||||
newDate &&
|
||||
placeholderDate &&
|
||||
(newDate.month !== placeholderDate.month || newDate.year !== placeholderDate.year)
|
||||
) {
|
||||
newDate = undefined;
|
||||
newTime = '';
|
||||
}
|
||||
});
|
||||
|
||||
// ─── API calls ──────────────────────────────────────────
|
||||
async function fetchHoursRange(startDate: CalendarDate, months: number) {
|
||||
loadingHours = true;
|
||||
|
||||
@@ -437,6 +437,21 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Clear selection when navigating to a month that doesn't contain the selected date.
|
||||
// Runs AFTER all synchronous state changes settle, so clicking a date in a different
|
||||
// month (fires both onPlaceholderChange and onValueChange) keeps the new selection,
|
||||
// while clicking prev/next arrows without picking a date clears it.
|
||||
$effect(() => {
|
||||
if (
|
||||
selectedDate &&
|
||||
placeholder &&
|
||||
(selectedDate.month !== placeholder.month || selectedDate.year !== placeholder.year)
|
||||
) {
|
||||
selectedDate = undefined;
|
||||
selectedTime = null;
|
||||
}
|
||||
});
|
||||
|
||||
// =============== Reset State ===============
|
||||
function resetState() {
|
||||
currentStep = 1;
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
open: boolean;
|
||||
bookingId: string;
|
||||
onReschedule?: () => void;
|
||||
onChanged?: () => void;
|
||||
}
|
||||
|
||||
let { open = $bindable(), bookingId, onReschedule }: Props = $props();
|
||||
let { open = $bindable(), bookingId, onReschedule, onChanged }: Props = $props();
|
||||
|
||||
let selectedBooking = $state<Booking | null>(null);
|
||||
let showApprovalModal = $state(false);
|
||||
@@ -83,6 +84,7 @@
|
||||
toast.success('Booking cancelled');
|
||||
showCancelModal = false;
|
||||
fetchBookingDetails();
|
||||
onChanged?.();
|
||||
} else {
|
||||
const text = await response.text();
|
||||
toast.error('Failed to cancel: ' + extractErrorMessage(text));
|
||||
@@ -770,6 +772,7 @@
|
||||
onApproved={() => {
|
||||
showApprovalModal = false;
|
||||
fetchBookingDetails();
|
||||
onChanged?.();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
@@ -783,6 +786,7 @@
|
||||
showRescheduleModal = false;
|
||||
fetchBookingDetails();
|
||||
onReschedule?.();
|
||||
onChanged?.();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -409,6 +409,21 @@
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Clear selection when navigating to a month that doesn't contain the selected date.
|
||||
// Runs AFTER all synchronous state changes settle, so clicking a date in a different
|
||||
// month (fires both onPlaceholderChange and onValueChange) keeps the new selection,
|
||||
// while clicking prev/next arrows without picking a date clears it.
|
||||
$effect(() => {
|
||||
if (
|
||||
selectedDate &&
|
||||
placeholder &&
|
||||
(selectedDate.month !== placeholder.month || selectedDate.year !== placeholder.year)
|
||||
) {
|
||||
selectedDate = undefined;
|
||||
selectedTime = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Modal.Root bind:open>
|
||||
|
||||
@@ -678,6 +678,21 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Clear selection when navigating to a month that doesn't contain the selected date.
|
||||
// Runs AFTER all synchronous state changes settle, so clicking a date in a different
|
||||
// month (fires both onPlaceholderChange and onValueChange) keeps the new selection,
|
||||
// while clicking prev/next arrows without picking a date clears it.
|
||||
$effect(() => {
|
||||
if (
|
||||
selectedDate &&
|
||||
placeholder &&
|
||||
(selectedDate.month !== placeholder.month || selectedDate.year !== placeholder.year)
|
||||
) {
|
||||
selectedDate = undefined;
|
||||
selectedTime = null;
|
||||
}
|
||||
});
|
||||
|
||||
async function fetchHoursRange(startDate: CalendarDate, months: number) {
|
||||
// Calculate end month manually (CalendarDate is immutable)
|
||||
let endYear = startDate.year;
|
||||
|
||||
@@ -54,5 +54,14 @@
|
||||
onPlaceholderChange(p as CalendarDate);
|
||||
}
|
||||
}}
|
||||
// Detect clicks on unavailable dates (bits-ui blocks onValueChange for these,
|
||||
// so the parent's selection stays stale). Clear the selection so the time
|
||||
// picker hides and the user knows they didn't pick anything.
|
||||
onclick={(e: MouseEvent) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (target.closest('[data-unavailable]')) {
|
||||
onchange?.(undefined);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user