diff --git a/frontend/src/components/manga/MangaListTable.vue b/frontend/src/components/manga/MangaListTable.vue index 92e0cbb..59cfc0e 100644 --- a/frontend/src/components/manga/MangaListTable.vue +++ b/frontend/src/components/manga/MangaListTable.vue @@ -6,7 +6,7 @@ import {BTable, type TableItem} from 'bootstrap-vue-next'; import type {TableField, TableFieldObject} from 'bootstrap-vue-next/dist/src/types'; import type {ViewEntry, ViewList} from '@/components/manga/MangaList.vue'; import MangaEntryDetailsModal from '@/components/manga/MangaEntryDetailsModal.vue'; -import {latestChaptersSorted, latestChapterString, newChapterCount} from '@/components/manga/util.manga'; +import {get, latestChaptersSorted, latestChapterString, newChapterCount} from '@/components/manga/util.manga'; type HeadData = { label: string, @@ -89,7 +89,7 @@ export default class MangaListTable extends Vue { if (!this.sortKey) { return this.tableEntries; } - const keyExtractor = (e: ViewEntry) => eval('e.' + this.sortKey);//TODO eval is evil + const keyExtractor = (e: ViewEntry) => get(e, this.sortKey);//TODO eval is evil const comparer = (l: ViewEntry, r: ViewEntry) => { const lkey = keyExtractor(l); const rkey = keyExtractor(r); diff --git a/frontend/src/components/manga/util.manga.ts b/frontend/src/components/manga/util.manga.ts index 3ee9ce8..4b5be6f 100644 --- a/frontend/src/components/manga/util.manga.ts +++ b/frontend/src/components/manga/util.manga.ts @@ -18,3 +18,29 @@ export function newChapterCount(entry: ViewEntry): number { const max = entry.media?.chapters || entry.chapters.reduce((l, r) => Math.max(l, r.chapter), 0); return Math.max(0, max - entry.entry.progress); } + +/** + * Dynamically get a nested value from an array or + * object with a string. + * + * @example get(person, 'friends[0].name') + * @link https://github.com/rayepps/radash/blob/master/src/object.ts#L214 + */ +export const get = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any, + path: string, + defaultValue?: TDefault +): TDefault => { + const segments = path.split(/[.[\]]/g) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let current: any = value + for (const key of segments) { + if (current === null) return defaultValue as TDefault + if (current === undefined) return defaultValue as TDefault + if (key.trim() === '') continue + current = current[key] + } + if (current === undefined) return defaultValue as TDefault + return current +}