mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
8.3 KiB
272 lines
8.3 KiB
import { signal, QueryList, isSignal, effect } from '@angular/core';
|
|
import { Subscription, Subject } from 'rxjs';
|
|
import { Typeahead } from './_typeahead-chunk.mjs';
|
|
import { hasModifierKey } from './keycodes.mjs';
|
|
import { PAGE_DOWN, PAGE_UP, END, HOME, LEFT_ARROW, RIGHT_ARROW, UP_ARROW, DOWN_ARROW, TAB } from './_keycodes-chunk.mjs';
|
|
|
|
class ListKeyManager {
|
|
_items;
|
|
_activeItemIndex = signal(-1, ...(ngDevMode ? [{
|
|
debugName: "_activeItemIndex"
|
|
}] : []));
|
|
_activeItem = signal(null, ...(ngDevMode ? [{
|
|
debugName: "_activeItem"
|
|
}] : []));
|
|
_wrap = false;
|
|
_typeaheadSubscription = Subscription.EMPTY;
|
|
_itemChangesSubscription;
|
|
_vertical = true;
|
|
_horizontal = null;
|
|
_allowedModifierKeys = [];
|
|
_homeAndEnd = false;
|
|
_pageUpAndDown = {
|
|
enabled: false,
|
|
delta: 10
|
|
};
|
|
_effectRef;
|
|
_typeahead;
|
|
_skipPredicateFn = item => item.disabled;
|
|
constructor(_items, injector) {
|
|
this._items = _items;
|
|
if (_items instanceof QueryList) {
|
|
this._itemChangesSubscription = _items.changes.subscribe(newItems => this._itemsChanged(newItems.toArray()));
|
|
} else if (isSignal(_items)) {
|
|
if (!injector && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
|
throw new Error('ListKeyManager constructed with a signal must receive an injector');
|
|
}
|
|
this._effectRef = effect(() => this._itemsChanged(_items()), {
|
|
...(ngDevMode ? {
|
|
debugName: "_effectRef"
|
|
} : {}),
|
|
injector
|
|
});
|
|
}
|
|
}
|
|
tabOut = new Subject();
|
|
change = new Subject();
|
|
skipPredicate(predicate) {
|
|
this._skipPredicateFn = predicate;
|
|
return this;
|
|
}
|
|
withWrap(shouldWrap = true) {
|
|
this._wrap = shouldWrap;
|
|
return this;
|
|
}
|
|
withVerticalOrientation(enabled = true) {
|
|
this._vertical = enabled;
|
|
return this;
|
|
}
|
|
withHorizontalOrientation(direction) {
|
|
this._horizontal = direction;
|
|
return this;
|
|
}
|
|
withAllowedModifierKeys(keys) {
|
|
this._allowedModifierKeys = keys;
|
|
return this;
|
|
}
|
|
withTypeAhead(debounceInterval = 200) {
|
|
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
const items = this._getItemsArray();
|
|
if (items.length > 0 && items.some(item => typeof item.getLabel !== 'function')) {
|
|
throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.');
|
|
}
|
|
}
|
|
this._typeaheadSubscription.unsubscribe();
|
|
const items = this._getItemsArray();
|
|
this._typeahead = new Typeahead(items, {
|
|
debounceInterval: typeof debounceInterval === 'number' ? debounceInterval : undefined,
|
|
skipPredicate: item => this._skipPredicateFn(item)
|
|
});
|
|
this._typeaheadSubscription = this._typeahead.selectedItem.subscribe(item => {
|
|
this.setActiveItem(item);
|
|
});
|
|
return this;
|
|
}
|
|
cancelTypeahead() {
|
|
this._typeahead?.reset();
|
|
return this;
|
|
}
|
|
withHomeAndEnd(enabled = true) {
|
|
this._homeAndEnd = enabled;
|
|
return this;
|
|
}
|
|
withPageUpDown(enabled = true, delta = 10) {
|
|
this._pageUpAndDown = {
|
|
enabled,
|
|
delta
|
|
};
|
|
return this;
|
|
}
|
|
setActiveItem(item) {
|
|
const previousActiveItem = this._activeItem();
|
|
this.updateActiveItem(item);
|
|
if (this._activeItem() !== previousActiveItem) {
|
|
this.change.next(this._activeItemIndex());
|
|
}
|
|
}
|
|
onKeydown(event) {
|
|
const keyCode = event.keyCode;
|
|
const modifiers = ['altKey', 'ctrlKey', 'metaKey', 'shiftKey'];
|
|
const isModifierAllowed = modifiers.every(modifier => {
|
|
return !event[modifier] || this._allowedModifierKeys.indexOf(modifier) > -1;
|
|
});
|
|
switch (keyCode) {
|
|
case TAB:
|
|
this.tabOut.next();
|
|
return;
|
|
case DOWN_ARROW:
|
|
if (this._vertical && isModifierAllowed) {
|
|
this.setNextItemActive();
|
|
break;
|
|
} else {
|
|
return;
|
|
}
|
|
case UP_ARROW:
|
|
if (this._vertical && isModifierAllowed) {
|
|
this.setPreviousItemActive();
|
|
break;
|
|
} else {
|
|
return;
|
|
}
|
|
case RIGHT_ARROW:
|
|
if (this._horizontal && isModifierAllowed) {
|
|
this._horizontal === 'rtl' ? this.setPreviousItemActive() : this.setNextItemActive();
|
|
break;
|
|
} else {
|
|
return;
|
|
}
|
|
case LEFT_ARROW:
|
|
if (this._horizontal && isModifierAllowed) {
|
|
this._horizontal === 'rtl' ? this.setNextItemActive() : this.setPreviousItemActive();
|
|
break;
|
|
} else {
|
|
return;
|
|
}
|
|
case HOME:
|
|
if (this._homeAndEnd && isModifierAllowed) {
|
|
this.setFirstItemActive();
|
|
break;
|
|
} else {
|
|
return;
|
|
}
|
|
case END:
|
|
if (this._homeAndEnd && isModifierAllowed) {
|
|
this.setLastItemActive();
|
|
break;
|
|
} else {
|
|
return;
|
|
}
|
|
case PAGE_UP:
|
|
if (this._pageUpAndDown.enabled && isModifierAllowed) {
|
|
const targetIndex = this._activeItemIndex() - this._pageUpAndDown.delta;
|
|
this._setActiveItemByIndex(targetIndex > 0 ? targetIndex : 0, 1);
|
|
break;
|
|
} else {
|
|
return;
|
|
}
|
|
case PAGE_DOWN:
|
|
if (this._pageUpAndDown.enabled && isModifierAllowed) {
|
|
const targetIndex = this._activeItemIndex() + this._pageUpAndDown.delta;
|
|
const itemsLength = this._getItemsArray().length;
|
|
this._setActiveItemByIndex(targetIndex < itemsLength ? targetIndex : itemsLength - 1, -1);
|
|
break;
|
|
} else {
|
|
return;
|
|
}
|
|
default:
|
|
if (isModifierAllowed || hasModifierKey(event, 'shiftKey')) {
|
|
this._typeahead?.handleKey(event);
|
|
}
|
|
return;
|
|
}
|
|
this._typeahead?.reset();
|
|
event.preventDefault();
|
|
}
|
|
get activeItemIndex() {
|
|
return this._activeItemIndex();
|
|
}
|
|
get activeItem() {
|
|
return this._activeItem();
|
|
}
|
|
isTyping() {
|
|
return !!this._typeahead && this._typeahead.isTyping();
|
|
}
|
|
setFirstItemActive() {
|
|
this._setActiveItemByIndex(0, 1);
|
|
}
|
|
setLastItemActive() {
|
|
this._setActiveItemByIndex(this._getItemsArray().length - 1, -1);
|
|
}
|
|
setNextItemActive() {
|
|
this._activeItemIndex() < 0 ? this.setFirstItemActive() : this._setActiveItemByDelta(1);
|
|
}
|
|
setPreviousItemActive() {
|
|
this._activeItemIndex() < 0 && this._wrap ? this.setLastItemActive() : this._setActiveItemByDelta(-1);
|
|
}
|
|
updateActiveItem(item) {
|
|
const itemArray = this._getItemsArray();
|
|
const index = typeof item === 'number' ? item : itemArray.indexOf(item);
|
|
const activeItem = itemArray[index];
|
|
this._activeItem.set(activeItem == null ? null : activeItem);
|
|
this._activeItemIndex.set(index);
|
|
this._typeahead?.setCurrentSelectedItemIndex(index);
|
|
}
|
|
destroy() {
|
|
this._typeaheadSubscription.unsubscribe();
|
|
this._itemChangesSubscription?.unsubscribe();
|
|
this._effectRef?.destroy();
|
|
this._typeahead?.destroy();
|
|
this.tabOut.complete();
|
|
this.change.complete();
|
|
}
|
|
_setActiveItemByDelta(delta) {
|
|
this._wrap ? this._setActiveInWrapMode(delta) : this._setActiveInDefaultMode(delta);
|
|
}
|
|
_setActiveInWrapMode(delta) {
|
|
const items = this._getItemsArray();
|
|
for (let i = 1; i <= items.length; i++) {
|
|
const index = (this._activeItemIndex() + delta * i + items.length) % items.length;
|
|
const item = items[index];
|
|
if (!this._skipPredicateFn(item)) {
|
|
this.setActiveItem(index);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
_setActiveInDefaultMode(delta) {
|
|
this._setActiveItemByIndex(this._activeItemIndex() + delta, delta);
|
|
}
|
|
_setActiveItemByIndex(index, fallbackDelta) {
|
|
const items = this._getItemsArray();
|
|
if (!items[index]) {
|
|
return;
|
|
}
|
|
while (this._skipPredicateFn(items[index])) {
|
|
index += fallbackDelta;
|
|
if (!items[index]) {
|
|
return;
|
|
}
|
|
}
|
|
this.setActiveItem(index);
|
|
}
|
|
_getItemsArray() {
|
|
if (isSignal(this._items)) {
|
|
return this._items();
|
|
}
|
|
return this._items instanceof QueryList ? this._items.toArray() : this._items;
|
|
}
|
|
_itemsChanged(newItems) {
|
|
this._typeahead?.setItems(newItems);
|
|
const activeItem = this._activeItem();
|
|
if (activeItem) {
|
|
const newIndex = newItems.indexOf(activeItem);
|
|
if (newIndex > -1 && newIndex !== this._activeItemIndex()) {
|
|
this._activeItemIndex.set(newIndex);
|
|
this._typeahead?.setCurrentSelectedItemIndex(newIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export { ListKeyManager };
|
|
//# sourceMappingURL=_list-key-manager-chunk.mjs.map
|
|
|