refactor(tui): extract shared frontend helpers (#37868)
This commit is contained in:
parent
deee40c572
commit
0eb71d0fc7
52 changed files with 1655 additions and 1614 deletions
|
|
@ -11,6 +11,7 @@ import { useDialog, type DialogContext } from "./dialog"
|
|||
import { Locale } from "../util/locale"
|
||||
import { getScrollAcceleration } from "../util/scroll"
|
||||
import { useConfig } from "../config"
|
||||
import { moveSelection, reconcileSelection } from "./select-controller"
|
||||
|
||||
export interface DialogSelectProps<T> {
|
||||
title: string
|
||||
|
|
@ -221,8 +222,10 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
|||
() => props.options,
|
||||
() => {
|
||||
if (!props.preserveSelection) {
|
||||
const next = Math.min(store.selected, flat().length - 1)
|
||||
if (next >= 0 && next !== store.selected) setStore("selected", next)
|
||||
const count = flat().length
|
||||
if (count === 0) return
|
||||
const next = reconcileSelection(store.selected, count)
|
||||
if (next !== store.selected) setStore("selected", next)
|
||||
return
|
||||
}
|
||||
if (resetSelection && store.filter.length > 0) {
|
||||
|
|
@ -266,8 +269,8 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
|||
})
|
||||
return
|
||||
}
|
||||
const next = Math.min(store.selected, flat().length - 1)
|
||||
if (next < 0) return
|
||||
const next = reconcileSelection(store.selected, flat().length)
|
||||
if (flat().length === 0) return
|
||||
setStore("selected", next)
|
||||
selection = flat()[next]
|
||||
},
|
||||
|
|
@ -296,10 +299,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
|||
function move(direction: number) {
|
||||
if (props.locked) return
|
||||
if (flat().length === 0) return
|
||||
let next = store.selected + direction
|
||||
if (next < 0) next = flat().length - 1
|
||||
if (next >= flat().length) next = 0
|
||||
moveTo(next, true)
|
||||
moveTo(moveSelection(store.selected, { count: flat().length, delta: direction, policy: "wrap" }), true)
|
||||
}
|
||||
|
||||
function moveTo(next: number, center = false, preserve = true) {
|
||||
|
|
|
|||
38
packages/tui/src/ui/select-controller.ts
Normal file
38
packages/tui/src/ui/select-controller.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
export function reconcileSelection(selected: number, count: number) {
|
||||
return Math.max(0, Math.min(count - 1, selected))
|
||||
}
|
||||
|
||||
export function moveSelection(selected: number, input: { count: number; delta: number; policy: "clamp" | "wrap" }) {
|
||||
if (input.count <= 0) return 0
|
||||
const next = selected + input.delta
|
||||
if (input.policy === "clamp") return reconcileSelection(next, input.count)
|
||||
if (next < 0) return input.count - 1
|
||||
if (next >= input.count) return 0
|
||||
return next
|
||||
}
|
||||
|
||||
export function revealSelectionOffset(offset: number, input: { count: number; limit: number; selected: number }) {
|
||||
const max = maxOffset(input.count, input.limit)
|
||||
if (input.selected < offset) return Math.min(max, input.selected)
|
||||
if (input.selected >= offset + input.limit) return Math.min(max, input.selected - input.limit + 1)
|
||||
return Math.max(0, Math.min(max, offset))
|
||||
}
|
||||
|
||||
export function moveSelectionOffset(
|
||||
offset: number,
|
||||
input: { count: number; limit: number; selected: number; direction: -1 | 1 },
|
||||
) {
|
||||
const max = maxOffset(input.count, input.limit)
|
||||
const margin = Math.max(0, Math.min(2, Math.floor((input.limit - 1) / 2)))
|
||||
if (input.direction < 0 && input.selected < offset + margin) {
|
||||
return Math.max(0, Math.min(max, input.selected - margin))
|
||||
}
|
||||
if (input.direction > 0 && input.selected > offset + input.limit - margin - 1) {
|
||||
return Math.min(max, input.selected - input.limit + margin + 1)
|
||||
}
|
||||
return Math.max(0, Math.min(max, offset))
|
||||
}
|
||||
|
||||
function maxOffset(count: number, limit: number) {
|
||||
return Math.max(0, count - limit)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue