Block calendar swiping outside possible range
This commit is contained in:
@@ -54,7 +54,7 @@ export function createSwipePopoverCalendarHTML({
|
|||||||
monthLabelTextClass = 'text-[10px] font-semibold leading-none tabular-nums whitespace-nowrap',
|
monthLabelTextClass = 'text-[10px] font-semibold leading-none tabular-nums whitespace-nowrap',
|
||||||
}) {
|
}) {
|
||||||
const weekdayHeader = `
|
const weekdayHeader = `
|
||||||
<div class="grid grid-cols-7 gap-1.5 text-center text-[8px] font-medium uppercase tracking-wide mb-1 leading-none" style="color:rgb(var(--text-dim-rgb));">
|
<div class="grid grid-cols-7 gap-1.5 text-center text-[8px] font-medium uppercase tracking-wide mb-1.5 leading-none" style="color:rgb(var(--text-dim-rgb));">
|
||||||
${weekdays.map((d) => `<div>${d}</div>`).join('')}
|
${weekdays.map((d) => `<div>${d}</div>`).join('')}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -75,7 +75,7 @@ export function createSwipePopoverCalendarHTML({
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="pb-3 px-3 flex items-center justify-end gap-3">
|
<div class="pb-3 px-3 flex items-center justify-end gap-3">
|
||||||
<div class="flex h-[2.05rem] min-w-0 max-w-[min(100%,20rem)] items-center justify-center rounded-full border" style="background:rgb(var(--app-bg-rgb)); border-color:rgb(var(--card-raised-rgb));">
|
<div class="flex h-[2.05rem] min-w-0 max-w-[min(100%,20rem)] items-center justify-center rounded-full border" style="background:transparent; border-color:rgb(var(--border-input-rgb));">
|
||||||
<span id="${idPrefix}-month-label" class="h-full shrink-0 inline-flex min-w-[5.75rem] max-w-[9rem] items-center justify-center px-3 ${monthLabelTextClass}" style="color:rgb(var(--text-body-soft-rgb));"></span>
|
<span id="${idPrefix}-month-label" class="h-full shrink-0 inline-flex min-w-[5.75rem] max-w-[9rem] items-center justify-center px-3 ${monthLabelTextClass}" style="color:rgb(var(--text-body-soft-rgb));"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,6 +111,7 @@ export function initSwipePopoverCalendar({
|
|||||||
theme = DEFAULT_THEME,
|
theme = DEFAULT_THEME,
|
||||||
getMonthAnchor,
|
getMonthAnchor,
|
||||||
setMonthAnchor,
|
setMonthAnchor,
|
||||||
|
canNavigateToMonth,
|
||||||
getSelectionKeys,
|
getSelectionKeys,
|
||||||
onSelectionCommit,
|
onSelectionCommit,
|
||||||
resolveDayState,
|
resolveDayState,
|
||||||
@@ -188,6 +189,23 @@ export function initSwipePopoverCalendar({
|
|||||||
track.style.transform = `translate3d(${restOffset + dx}px, 0, 0)`;
|
track.style.transform = `translate3d(${restOffset + dx}px, 0, 0)`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getNavigationTarget = (monthDelta) => {
|
||||||
|
const anchor = normalizeMonth(getMonthAnchor());
|
||||||
|
return startOfMonth(new Date(anchor.getFullYear(), anchor.getMonth() + monthDelta, 1));
|
||||||
|
};
|
||||||
|
|
||||||
|
const canNavigate = (monthDelta) => {
|
||||||
|
if (typeof canNavigateToMonth !== 'function') return true;
|
||||||
|
const anchor = normalizeMonth(getMonthAnchor());
|
||||||
|
const target = getNavigationTarget(monthDelta);
|
||||||
|
return canNavigateToMonth(target, { currentMonth: anchor, monthDelta }) !== false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAllowedDragDx = (dx) => {
|
||||||
|
if (dx === 0) return 0;
|
||||||
|
return canNavigate(dx > 0 ? -1 : 1) ? dx : 0;
|
||||||
|
};
|
||||||
|
|
||||||
const getSelectedSet = (previewSelection = null) => {
|
const getSelectedSet = (previewSelection = null) => {
|
||||||
const raw = previewSelection ?? getSelectionKeys();
|
const raw = previewSelection ?? getSelectionKeys();
|
||||||
if (Array.isArray(raw)) return new Set(raw);
|
if (Array.isArray(raw)) return new Set(raw);
|
||||||
@@ -259,12 +277,16 @@ export function initSwipePopoverCalendar({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const commitNavigation = (monthDelta) => {
|
const commitNavigation = (monthDelta) => {
|
||||||
|
if (!canNavigate(monthDelta)) {
|
||||||
|
setDragTranslate(0, ANIMATION_MS);
|
||||||
|
setTimeout(() => syncGripVisibility(false), ANIMATION_MS + 20);
|
||||||
|
return;
|
||||||
|
}
|
||||||
animatingNav = true;
|
animatingNav = true;
|
||||||
const targetDx = monthDelta < 0 ? panelWidth : -panelWidth;
|
const targetDx = monthDelta < 0 ? panelWidth : -panelWidth;
|
||||||
setDragTranslate(targetDx, ANIMATION_MS);
|
setDragTranslate(targetDx, ANIMATION_MS);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const anchor = normalizeMonth(getMonthAnchor());
|
setMonthAnchor(getNavigationTarget(monthDelta));
|
||||||
setMonthAnchor(startOfMonth(new Date(anchor.getFullYear(), anchor.getMonth() + monthDelta, 1)));
|
|
||||||
render();
|
render();
|
||||||
resetTrackPosition();
|
resetTrackPosition();
|
||||||
animatingNav = false;
|
animatingNav = false;
|
||||||
@@ -352,7 +374,7 @@ export function initSwipePopoverCalendar({
|
|||||||
axis = Math.abs(dx) >= Math.abs(dy) ? 'x' : 'y';
|
axis = Math.abs(dx) >= Math.abs(dy) ? 'x' : 'y';
|
||||||
if (axis === 'x') syncGripVisibility(true);
|
if (axis === 'x') syncGripVisibility(true);
|
||||||
}
|
}
|
||||||
if (axis === 'x') setDragTranslate(dx, 0);
|
if (axis === 'x') setDragTranslate(getAllowedDragDx(dx), 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
const endGesture = (e) => {
|
const endGesture = (e) => {
|
||||||
@@ -360,7 +382,8 @@ export function initSwipePopoverCalendar({
|
|||||||
ptrId = null;
|
ptrId = null;
|
||||||
if (!moved || axis !== 'x') return;
|
if (!moved || axis !== 'x') return;
|
||||||
const dx = e ? e.clientX - startX : 0;
|
const dx = e ? e.clientX - startX : 0;
|
||||||
if (Math.abs(dx) >= SWIPE_THRESHOLD) commitNavigation(dx > 0 ? -1 : 1);
|
const monthDelta = dx > 0 ? -1 : 1;
|
||||||
|
if (Math.abs(dx) >= SWIPE_THRESHOLD && canNavigate(monthDelta)) commitNavigation(monthDelta);
|
||||||
else {
|
else {
|
||||||
setDragTranslate(0, ANIMATION_MS);
|
setDragTranslate(0, ANIMATION_MS);
|
||||||
setTimeout(() => syncGripVisibility(false), ANIMATION_MS + 20);
|
setTimeout(() => syncGripVisibility(false), ANIMATION_MS + 20);
|
||||||
|
|||||||
@@ -313,6 +313,11 @@ function bindPantryCalendarInteractions() {
|
|||||||
if (nextAnchor.getTime() < minAnchor.getTime()) return;
|
if (nextAnchor.getTime() < minAnchor.getTime()) return;
|
||||||
calendarMonthAnchor = nextAnchor;
|
calendarMonthAnchor = nextAnchor;
|
||||||
},
|
},
|
||||||
|
canNavigateToMonth: (nextMonth) => {
|
||||||
|
const nextAnchor = startOfMonth(nextMonth);
|
||||||
|
const minAnchor = startOfMonth(getToday());
|
||||||
|
return nextAnchor.getTime() >= minAnchor.getTime();
|
||||||
|
},
|
||||||
getSelectionKeys: () => dateKey(horizonEndDate),
|
getSelectionKeys: () => dateKey(horizonEndDate),
|
||||||
onSelectionCommit: (selectedKey) => {
|
onSelectionCommit: (selectedKey) => {
|
||||||
selectHorizonDate(new Date(`${selectedKey}T00:00:00`));
|
selectHorizonDate(new Date(`${selectedKey}T00:00:00`));
|
||||||
|
|||||||
Reference in New Issue
Block a user