feat: add paper-style mobile navigation
ci / gitleaks (push) Successful in 8s
ci / docker (push) Successful in 3m35s

This commit is contained in:
2026-09-18 08:34:36 +08:00
parent a4c1ac5978
commit 7bfc5d2a44
6 changed files with 155 additions and 12 deletions
+7 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { archivePanelFlags, calendarModeLabel, changedHabitFields, clampFabPosition, countdownDayText, countdownKindLabel, dateKey, dayBefore, defaultView, formatArchivedAt, formatAuditAction, formatAuditEntity, formatHabitApiError, formatHabitHistoryNumber, formatLocalShortDateTime, formatUserAgent, habitActionState, habitButtonNotice, habitButtonValue, habitHistoryWindow, habitWeek, invalidateHabitGridCache, isFabDrag, isHabitComplete, isHabitScheduledToday, isTaskView, mergeHabitHistory, nextHabitSwipeValue, nextTotalAfterLocalTaskAdd, nextTotalAfterLocalTaskRemoval, snapFabPosition, normalizeRequiredName, numericHabitInputValue, performHabitRestore, performTrashMutation, previousHabitSwipeValue, quickTaskFields, readCountdownCache, readHabitGridCache, readStoredBoolean, readStoredNavigation, shouldToggleRowSwipe, validateHabitForm, writeCountdownCache, writeHabitGridCache, writeStoredBoolean, writeStoredNavigation } from './mvp-utils'
import { archivePanelFlags, calendarModeLabel, changedHabitFields, clampFabPosition, countdownDayText, countdownKindLabel, dateKey, dayBefore, defaultView, fabBottomReserved, formatArchivedAt, formatAuditAction, formatAuditEntity, formatHabitApiError, formatHabitHistoryNumber, formatLocalShortDateTime, formatUserAgent, habitActionState, habitButtonNotice, habitButtonValue, habitHistoryWindow, habitWeek, invalidateHabitGridCache, isFabDrag, isHabitComplete, isHabitScheduledToday, isTaskView, mergeHabitHistory, nextHabitSwipeValue, nextTotalAfterLocalTaskAdd, nextTotalAfterLocalTaskRemoval, snapFabPosition, normalizeRequiredName, numericHabitInputValue, performHabitRestore, performTrashMutation, previousHabitSwipeValue, quickTaskFields, readCountdownCache, readHabitGridCache, readStoredBoolean, readStoredNavigation, shouldToggleRowSwipe, validateHabitForm, writeCountdownCache, writeHabitGridCache, writeStoredBoolean, writeStoredNavigation } from './mvp-utils'
describe('MVP view utilities', () => {
it('formats a local date as YYYY-MM-DD', () => {
@@ -202,6 +202,12 @@ describe('MVP view utilities', () => {
expect(clampFabPosition(120, 300, 390, 844)).toEqual({ x: 120, y: 300 })
})
it('reserves the paper navigation, safe area, and gap below a dragged add button', () => {
expect(fabBottomReserved(0)).toBe(76)
expect(fabBottomReserved(34)).toBe(110)
expect(clampFabPosition(500, 900, 390, 844, 56, 14, fabBottomReserved(34))).toEqual({ x: 320, y: 678 })
})
it('snaps the add button to the nearest horizontal edge while preserving its safe vertical position', () => {
expect(snapFabPosition(40, 300, 390, 844, 56, 14, 84)).toEqual({ x: 14, y: 300 })
expect(snapFabPosition(280, 300, 390, 844, 56, 14, 84)).toEqual({ x: 320, y: 300 })
+4
View File
@@ -190,6 +190,10 @@ export async function performTrashMutation(
return { mutated: true, refreshed: await refresh() }
}
export function fabBottomReserved(safeAreaBottom = 0, navigationHeight = 64, gap = 12) {
return navigationHeight + Math.max(0, safeAreaBottom) + gap
}
export function clampFabPosition(x: number, y: number, viewportWidth: number, viewportHeight: number, size = 56, margin = 14, bottomReserved = 84) {
return {
x: Math.min(Math.max(x, margin), Math.max(margin, viewportWidth - size - margin)),