(opts: Options)
| 14 | } |
| 15 | |
| 16 | export function createScrollPersistence(opts: Options) { |
| 17 | const wait = opts.debounceMs ?? 200 |
| 18 | const [cache, setCache] = createStore<Record<string, ScrollMap>>({}) |
| 19 | const dirty = new Set<string>() |
| 20 | const timers = new Map<string, ReturnType<typeof setTimeout>>() |
| 21 | |
| 22 | function clone(input?: ScrollMap) { |
| 23 | const out: ScrollMap = {} |
| 24 | if (!input) return out |
| 25 | |
| 26 | for (const key of Object.keys(input)) { |
| 27 | const pos = input[key] |
| 28 | if (!pos) continue |
| 29 | out[key] = { x: pos.x, y: pos.y } |
| 30 | } |
| 31 | |
| 32 | return out |
| 33 | } |
| 34 | |
| 35 | function seed(sessionKey: string) { |
| 36 | const next = clone(opts.getSnapshot(sessionKey)) |
| 37 | const current = cache[sessionKey] |
| 38 | if (!current) { |
| 39 | setCache(sessionKey, next) |
| 40 | return |
| 41 | } |
| 42 | |
| 43 | if (Object.keys(current).length > 0) return |
| 44 | if (Object.keys(next).length === 0) return |
| 45 | setCache(sessionKey, next) |
| 46 | } |
| 47 | |
| 48 | function scroll(sessionKey: string, tab: string) { |
| 49 | seed(sessionKey) |
| 50 | return cache[sessionKey]?.[tab] ?? opts.getSnapshot(sessionKey)?.[tab] |
| 51 | } |
| 52 | |
| 53 | function schedule(sessionKey: string) { |
| 54 | const prev = timers.get(sessionKey) |
| 55 | if (prev) clearTimeout(prev) |
| 56 | timers.set( |
| 57 | sessionKey, |
| 58 | setTimeout(() => flush(sessionKey), wait), |
| 59 | ) |
| 60 | } |
| 61 | |
| 62 | function setScroll(sessionKey: string, tab: string, pos: SessionScroll) { |
| 63 | seed(sessionKey) |
| 64 | |
| 65 | const prev = cache[sessionKey]?.[tab] |
| 66 | if (prev?.x === pos.x && prev?.y === pos.y) return |
| 67 | |
| 68 | setCache(sessionKey, tab, { x: pos.x, y: pos.y }) |
| 69 | dirty.add(sessionKey) |
| 70 | schedule(sessionKey) |
| 71 | } |
| 72 | |
| 73 | function flush(sessionKey: string) { |
no outgoing calls
no test coverage detected