UNPKG

azure-devops-ui

Version:

React components for building web UI in Azure DevOps

143 lines (142 loc) 6.02 kB
import { Observable, ObservableLike } from '../Core/Observable'; /** * An AggregateItemProvider takes a set of arrays, ObservableArrays, and ItemProviders and returns listItems from them as if they were a single * ItemProvider. */ export class AggregateItemProvider extends Observable { constructor() { super(); this.internalCollections = []; this.items = []; } get length() { return this.items.length; } get value() { return this.items; } get collections() { return this.internalCollections; } /** * Adds an additional collection of items to the end of the array * * @param collection Array of items or an observable array of items */ push(collection) { let collectionEntry; if (ObservableLike.isObservable(collection)) { const provider = collection; const subscriber = this.getSubscriber(this.internalCollections.length); collectionEntry = { items: provider.value, provider, subscriber }; if (this.subscriberCount) { ObservableLike.subscribe(collectionEntry.provider, subscriber); } } else if (collection.value) { collectionEntry = { items: collection.value, provider: collection }; } else if (collection.length) { collectionEntry = { items: collection }; } if (collectionEntry) { this.internalCollections.push(collectionEntry); if (collectionEntry.items.length) { this.items.push(...collectionEntry.items); if (this.subscriberCount) { this.notify({ addedItems: collectionEntry.items, index: this.items.length - collectionEntry.items.length }, "push"); } } } } getItem(index) { try { let accumulatedIndex = 0; for (let collectionIndex = 0; collectionIndex < this.internalCollections.length; collectionIndex++) { const collection = this.internalCollections[collectionIndex]; const provider = collection.provider; const collectionLength = provider ? provider.length : collection.items.length; // If the overall index falls within the current collection... if (index < accumulatedIndex + collectionLength) { const localIndex = index - accumulatedIndex; if (provider && provider.getItem) { return provider.getItem(localIndex); } else { return collection.items[localIndex]; } } accumulatedIndex += collectionLength; } // Return undefined if index is out of bounds. return undefined; } catch (error) { document.dispatchEvent(new CustomEvent('vss-telemetry-proxy', { detail: { area: "VssDropdown", component: "AggregateItemProvider", feature: "VssDropdown.AggregateItemProvider", level: 3, method: "getItem", message: "An error occurred while getting item", properties: { error: error.message, stack: error.stack, index: index, values: this.value } }, bubbles: true })); } } subscribe(observer, action) { const subscription = super.subscribe(observer, action); if (this.subscriberCount === 1) { for (const collection of this.internalCollections) { if (collection.subscriber && collection.provider && collection.provider.subscribe) { collection.provider.subscribe(collection.subscriber); } } // Collections may have changed between the time they were pushed and subscribed to. // Update the aggregate provider's items to match the current state of its collections. const currentItems = []; for (const collection of this.internalCollections) { currentItems.push(...collection.items); } this.items.splice(0, this.items.length, ...currentItems); } return subscription; } unsubscribe(observer, action) { super.unsubscribe(observer, action); if (this.subscriberCount === 0) { for (const collection of this.internalCollections) { if (collection.subscriber && collection.provider && collection.provider.unsubscribe) { collection.provider.unsubscribe(collection.subscriber); } } } } getSubscriber(collectionIndex) { return (args) => { // Find the index in our aggregate array let index = args.index; for (let i = 0; i < collectionIndex; i++) { index += this.internalCollections[i].items.length; } if (args.changedItems) { // Handle change event this.items.splice(index, args.changedItems.length, ...args.changedItems); this.notify({ changedItems: args.changedItems, index }, "change"); } else { // Handle splice, push, pop events const removedItems = args.removedItems || []; const addedItems = args.addedItems || []; this.items.splice(index, removedItems.length, ...addedItems); this.notify({ removedItems, addedItems, index }, "splice"); } }; } }