platform/packages/ui/src/focus.ts
Andrey Sobolev b834d4c4ee
Fix channel editor focus (#1769)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
2022-05-17 03:06:57 +07:00

127 lines
2.9 KiB
TypeScript

import { getContext, onDestroy, setContext } from 'svelte'
/**
* @public
*/
export interface FocusManager {
next: (inc?: 1 | -1) => void
setFocus: (idx: number) => void
setFocusPos: (order: number) => void
updateFocus: (idx: number, order: number) => void
// Check if current manager has focus
hasFocus: () => boolean
}
class FocusManagerImpl implements FocusManager {
counter = 0
elements: Array<{
id: number
order: number
focus: () => boolean
isFocus: () => boolean
}> = []
current = 0
register (order: number, focus: () => boolean, isFocus: () => boolean): number {
const el = { id: this.counter++, order, focus, isFocus }
this.elements.push(el)
this.sort()
return el.id
}
unregister (idx: number): void {
this.elements = this.elements.filter((it) => it.id !== idx)
this.sort()
}
sort (): void {
// this.needSort = 0
this.elements.sort((a, b) => {
return a.order - b.order
})
}
next (inc?: 1 | -1): void {
while (true) {
this.current = this.current + (inc ?? 1)
if (this.elements[Math.abs(this.current) % this.elements.length].focus()) {
break
}
}
}
setFocus (idx: number): void {
this.current = this.elements.findIndex((it) => it.id === idx) ?? 0
this.elements[Math.abs(this.current) % this.elements.length].focus()
}
setFocusPos (order: number): void {
const idx = this.elements.findIndex((it) => it.order === order)
if (idx !== undefined) {
this.current = idx
this.elements[Math.abs(this.current) % this.elements.length].focus()
}
}
updateFocus (idx: number, order: number): void {
const el = this.elements.find((it) => it.id === idx)
if (el !== undefined) {
if (el.order !== order) {
el.order = order
this.sort()
}
}
}
hasFocus (): boolean {
for (const el of this.elements) {
if (el.isFocus()) {
return true
}
}
return false
}
}
/**
* @public
*/
export function createFocusManager (): FocusManager {
const mgr = new FocusManagerImpl()
setFocusManager(mgr)
return mgr
}
export function setFocusManager (manager: FocusManager): void {
setContext('ui.focus.elements', manager)
}
/**
* @public
*/
export function getFocusManager (): FocusManager | undefined {
return getContext('ui.focus.elements')
}
/**
* Register new focus reciever if order !== -1
* @public
*/
export function registerFocus (
order: number,
item: { focus: () => boolean, isFocus: () => boolean }
): { idx: number, focusManager?: FocusManager } {
const focusManager = getFocusManager() as FocusManagerImpl
if (order === -1) {
return { idx: -1, focusManager }
}
const idx = focusManager?.register(order, item.focus, item.isFocus) ?? -1
if (idx !== -1) {
onDestroy(() => {
focusManager.unregister(idx)
})
}
return { idx, focusManager }
}