// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html export interface Identifiable { id?: string; } interface IterableIdentifiable extends Identifiable { items: T[]; } export type Identifier = string | number; function normalize( iterable: IterableIdentifiable, idOrIndex: Identifier ): number { let normalizedIndex: number; if (typeof idOrIndex === "string") { normalizedIndex = iterable.items.findIndex((value) => value.id === idOrIndex); } else if (idOrIndex < 0) { normalizedIndex = iterable.items.length + idOrIndex; } else { normalizedIndex = idOrIndex; } return normalizedIndex >= iterable.items.length ? -1 : normalizedIndex; } function search(values: T[], index: number): T | null { return index >= 0 ? values[index] : null; } export function insert( iterable: IterableIdentifiable & T, value: T, idOrIndex: Identifier ): IterableIdentifiable & T { const index = normalize(iterable, idOrIndex); if (index >= 0) { iterable.items = [ ...iterable.items.slice(0, index), value, ...iterable.items.slice(index), ]; } return iterable; } export function add( iterable: IterableIdentifiable & T, value: T, idOrIndex: Identifier ): IterableIdentifiable & T { const index = normalize(iterable, idOrIndex); if (index >= 0) { iterable.items = [ ...iterable.items.slice(0, index + 1), value, ...iterable.items.slice(index + 1), ]; } return iterable; } function isRecursive(component: Identifiable): component is IterableIdentifiable { return Boolean(Object.prototype.hasOwnProperty.call(component, "items")); } export function updateRecursive( update: (component: T) => T, component: T, ...identifiers: Identifier[] ): T { if (identifiers.length === 0) { return update(component); } else if (isRecursive(component)) { const [identifier, ...restIdentifiers] = identifiers; const normalizedIndex = normalize(component, identifier); const foundComponent = search(component.items, normalizedIndex); if (foundComponent) { component.items[normalizedIndex] = updateRecursive( update, foundComponent as T, ...restIdentifiers ); } return component; } return component; }