fix: Scrolling up action would leave focusIndexes at incorrect states

This commit is contained in:
Aleksi Lassila
2024-06-06 22:27:07 +03:00
parent e039a0c11e
commit 05719d8c04
2 changed files with 98 additions and 2 deletions

View File

@@ -20,14 +20,14 @@
if (selectable && get(selectable.focusIndex) === 0) {
history.back();
} else {
selectable?.focusChild(0) || selectable?.focus();
selectable?.focusChild(0, { cycleTo: true }) || selectable?.focus({ cycleTo: true });
}
}
function handleGoToTop() {
const selectable = get(topSelectable);
if (topSelectable) {
selectable?.focusChild(0) || selectable?.focus();
selectable?.focusChild(0, { cycleTo: true }) || selectable?.focus({ cycleTo: true });
} else handleGoBack();
}
</script>

View File

@@ -13,6 +13,7 @@ export type FlowDirection = 'vertical' | 'horizontal';
type FocusEventOptions = {
setFocusedElement: boolean | HTMLElement;
propagate: boolean;
cycleTo?: boolean;
onFocus?: (
superOnFocus: FocusHandler,
...args: Parameters<FocusHandler>
@@ -83,6 +84,11 @@ export type KeyEventHandler = (selectable: Selectable, options: KeyEventOptions)
export type ActiveChildStore = typeof Selectable.prototype.activeChild;
enum FocusOrder {
First,
Last
}
export class Selectable {
id: number;
name: string;
@@ -216,6 +222,59 @@ export class Selectable {
propagateFocusUpdates(_options, this);
if (_options.setFocusedElement) {
if (_options.cycleTo) {
const previouslyFocused = get(Selectable.focusedObject);
if (previouslyFocused) {
const parents = this.getParents();
const otherParents = [];
let commonParent: Selectable | undefined = undefined;
let el = previouslyFocused;
while (el) {
const parent = el.parent;
if (parent) otherParents.push(parent);
if (parent && parents.includes(parent)) {
commonParent = parent;
// const thisBeforeParent = parents[parents.indexOf(commonParent) - 1];
// after = thisBeforeParent
// ? parent.children.indexOf(el) < parent.children.indexOf(thisBeforeParent)
// : false;
// thatChild = parents[parents.indexOf(commonParent) - 1];
break;
} else if (parent) {
el = parent;
} else break;
}
if (commonParent) {
const targetChild = parents[parents.indexOf(commonParent) - 1];
const previousChild = otherParents[otherParents.indexOf(commonParent) - 1];
let order: FocusOrder | undefined = undefined;
const direction = commonParent.direction;
for (const child of commonParent.children) {
if (child === targetChild) {
if (order !== undefined) {
break;
}
order = FocusOrder.First;
continue;
} else if (child === previousChild) {
if (order !== undefined) {
child.recursiveSetFocusIndex(direction, order);
break;
}
order = FocusOrder.Last;
}
if (order !== undefined) {
child.recursiveSetFocusIndex(direction, order);
}
}
}
}
}
if (_options.setFocusedElement === true) {
this.htmlElement.focus({ preventScroll: true });
} else {
@@ -762,6 +821,43 @@ export class Selectable {
return childToFocus;
}
private getParents(): Selectable[] {
const parents = [];
let parent = this.parent;
while (parent) {
parents.push(parent);
parent = parent.parent;
}
return parents;
}
private recursiveSetFocusIndex(direction: FlowDirection, order: FocusOrder) {
for (const child of this.children) {
child.recursiveSetFocusIndex(direction, order);
}
if (this.direction === direction || this.gridColumns) {
if (order === FocusOrder.First) {
this.focusIndex.set(0);
} else {
this.focusIndex.set(this.children.length - 1);
}
}
}
private getCommonParent(other: Selectable): Selectable | undefined {
const parents = this.getParents();
const otherParents = other.getParents();
for (const parent of parents) {
if (otherParents.includes(parent)) return parent;
}
return undefined;
}
select() {
this.onSelect?.();
}