import { has } from 'lodash'; import { generate as shortid } from 'shortid'; import { ConfigSettings } from '../types'; export type LayerStackPriority = 'high' | 'low' | number; export interface LayerStackEntryOptions { item: TItem; priority: LayerStackPriority; tags?: string | string[]; } export interface LayerStackEntry extends LayerStackEntryOptions { id: string; sequence: number; tags: string[]; } export type LayerStackEntryWithOptions< TItem = {}, TEntryOptions extends LayerStackEntryOptions = LayerStackEntryOptions > = TEntryOptions & LayerStackEntry; /** * Compares ConfigSettingsStackLayer with lowest priority is lowest index. * Order first by priority property value, 'low' first, then integer values high to low (i.e. 1 is highest priority and last in sorted array), * then 'high' prioritiest last. For matching priority property values, order by sequence value. * @param a first layer to compare * @param b second layer to compare */ const compareLayerEntries = (a: LayerStackEntry, b: LayerStackEntry): number => { if (a.priority === b.priority) { return a.sequence - b.sequence; } if (a.priority === 'high' || b.priority === 'low') { return 1; } if (a.priority === 'low' || b.priority === 'high') { return -1; } return (b.priority as number) - (a.priority as number); }; export class LayerStack> { private _entries: Array> = []; private _nextSequence = 1; public add(item: TItem, priority?: LayerStackPriority, tags?: string | string[]): TEntryOptions { const entry: TEntryOptions = ({ item, priority, tags, } as unknown) as TEntryOptions; return this.addEntry(entry); } public addEntry(entryOptions: TEntryOptions): LayerStackEntryWithOptions { const entry: LayerStackEntryWithOptions = ({ ...entryOptions, id: shortid(), priority: entryOptions.priority || 'high', sequence: this._nextSequence++, tags: entryOptions.tags ? (Array.isArray(entryOptions.tags) ? entryOptions.tags : [entryOptions.tags]) : [], } as unknown) as (LayerStackEntryWithOptions); this._entries.push(entry); return entry; } public get entries(): Array> { return this._entries.sort(compareLayerEntries); } public getEntries(filter: (entry: LayerStackEntryWithOptions) => boolean) { return this.entries.filter(filter); } public getItems(filter: (entry: LayerStackEntryWithOptions) => boolean) { return this.entries.filter(filter); } public getItemsByTag(tags: string | string[], matchType: 'and' | 'or' = 'or') { const aTags = Array.isArray(tags) ? tags : [tags]; return this.entries.filter(l => { return matchType === 'and' ? aTags.every(t => l.tags.indexOf(t) >= 0) : aTags.some(t => l.tags.indexOf(t) >= 0); }); } private getItemsFromEntries = (entries: Array> = this.entries) => { return entries.map(l => l.item); }; public get items(): TItem[] { return this.getItemsFromEntries() as TItem[]; } }