feat: improve task search reveal interaction
ci / gitleaks (push) Successful in 11s
ci / docker (push) Successful in 3m45s

This commit is contained in:
2026-09-11 07:07:15 +08:00
parent 64b8525720
commit 36952fdbcc
5 changed files with 151 additions and 12 deletions
+38
View File
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest'
import {
clampSearchPullDistance,
isSearchShortcut,
isAtSearchPullOrigin,
shouldHideSearchAfterSwipe,
shouldRevealSearchAfterPull,
} from './mobile-search'
describe('mobile task search gestures', () => {
it('clamps downward pull distance without treating upward motion as pull', () => {
expect(clampSearchPullDistance(-20)).toBe(0)
expect(clampSearchPullDistance(24)).toBe(24)
expect(clampSearchPullDistance(100)).toBe(72)
})
it('reveals only after crossing the pull threshold', () => {
expect(shouldRevealSearchAfterPull(55)).toBe(false)
expect(shouldRevealSearchAfterPull(56)).toBe(true)
})
it('requires every relevant scroll container to be at the top', () => {
expect(isAtSearchPullOrigin(0, 0, 0)).toBe(true)
expect(isAtSearchPullOrigin(0, 12, 0)).toBe(false)
})
it('hides only after an upward swipe when the query is empty', () => {
expect(shouldHideSearchAfterSwipe(-36, '')).toBe(true)
expect(shouldHideSearchAfterSwipe(-35, '')).toBe(false)
expect(shouldHideSearchAfterSwipe(-80, 'dodo')).toBe(false)
})
it('recognizes command/control K without hijacking plain typing', () => {
expect(isSearchShortcut({ key: 'k', metaKey: true, ctrlKey: false })).toBe(true)
expect(isSearchShortcut({ key: 'K', metaKey: false, ctrlKey: true })).toBe(true)
expect(isSearchShortcut({ key: 'k', metaKey: false, ctrlKey: false })).toBe(false)
})
})
+23
View File
@@ -0,0 +1,23 @@
export const SEARCH_PULL_THRESHOLD = 56
export const SEARCH_PULL_LIMIT = 72
export const SEARCH_HIDE_SWIPE_THRESHOLD = 36
export function clampSearchPullDistance(distance: number) {
return Math.min(SEARCH_PULL_LIMIT, Math.max(0, distance))
}
export function shouldRevealSearchAfterPull(distance: number) {
return distance >= SEARCH_PULL_THRESHOLD
}
export function isAtSearchPullOrigin(...scrollPositions: number[]) {
return scrollPositions.every((position) => position <= 0)
}
export function shouldHideSearchAfterSwipe(deltaY: number, query: string) {
return !query.trim() && deltaY <= -SEARCH_HIDE_SWIPE_THRESHOLD
}
export function isSearchShortcut(event: Pick<KeyboardEvent, 'key' | 'metaKey' | 'ctrlKey'>) {
return (event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k'
}