From 7b101610d24855096a49f14c27b02d090c7550d5 Mon Sep 17 00:00:00 2001 From: Andrea Leardini Date: Tue, 22 Oct 2024 15:41:01 +0200 Subject: [PATCH 1/6] feat: add sorting support to NeTable --- src/components/NeDropdownFilter.vue | 2 +- src/components/NeTable.vue | 25 +++++- src/components/NeTableHeadCell.vue | 72 ++++++++++++++++- src/composables/useSort.ts | 76 ++++++++++++++++++ stories/NeTable.stories.ts | 120 ++++++++++++++-------------- tailwind.config.ts | 5 ++ 6 files changed, 233 insertions(+), 67 deletions(-) create mode 100644 src/composables/useSort.ts diff --git a/src/components/NeDropdownFilter.vue b/src/components/NeDropdownFilter.vue index 7051fc8..d232d06 100644 --- a/src/components/NeDropdownFilter.vue +++ b/src/components/NeDropdownFilter.vue @@ -4,7 +4,7 @@ --> diff --git a/src/composables/useSort.ts b/src/composables/useSort.ts new file mode 100644 index 0000000..dfaa0cd --- /dev/null +++ b/src/composables/useSort.ts @@ -0,0 +1,76 @@ +import { ref, toValue, watchEffect, type MaybeRefOrGetter } from 'vue' + +export function useSort( + items: MaybeRefOrGetter[]>, + sortKey: MaybeRefOrGetter, + descending: MaybeRefOrGetter = false, + sortFunctions: Record number> = {} +) { + const sortedItems = ref[]>([]) + + function defaultSortFn(a: Record, b: Record) { + const sortKeyValue = toValue(sortKey) + const valueA = a[sortKeyValue] + const valueB = b[sortKeyValue] + + // numbers + + if (typeof valueA === 'number' && typeof valueB === 'number') { + return valueA - valueB + } + + // strings + + if (typeof valueA == 'string' && typeof valueB == 'string') { + return valueA.localeCompare(valueB) + } + + // dates + + if (valueA instanceof Date && valueB instanceof Date) { + return valueA.getTime() - new Date(valueB).getTime() + } + + // booleans + + if (typeof valueA === 'boolean' && typeof valueB === 'boolean') { + return valueA === valueB ? 0 : valueA ? 1 : -1 + } + + // arrays + + if (Array.isArray(valueA) && Array.isArray(valueB)) { + return valueA.length - valueB.length + } + + console.log('sort: unknown object types', typeof valueA, typeof valueB) //// + + if (typeof valueA !== 'undefined' && typeof valueB === 'undefined') { + return -1 + } else if (typeof valueA === 'undefined' && typeof valueB !== 'undefined') { + return 1 + } else { + return 0 + } + } + + watchEffect(() => { + const itemsValue = toValue(items) + const sortKeyValue = toValue(sortKey) + const descendingValue = toValue(descending) + + const sortFunction = sortFunctions[sortKeyValue] || defaultSortFn + + // sort the items without mutating (slice) the original array + sortedItems.value = itemsValue + .slice() + .sort((a: Record, b: Record) => { + const result = sortFunction(a, b) + return descendingValue ? -result : result + }) + }) + + return { + sortedItems + } +} diff --git a/stories/NeTable.stories.ts b/stories/NeTable.stories.ts index 069643f..e92901b 100644 --- a/stories/NeTable.stories.ts +++ b/stories/NeTable.stories.ts @@ -12,6 +12,16 @@ import { Meta, StoryObj } from '@storybook/vue3' import { faTable } from '@fortawesome/free-solid-svg-icons' import { library } from '@fortawesome/fontawesome-svg-core' +const items = [ + { game: 'Super Mario 64', platform: 'Nintendo 64', year: 1996 }, + { game: 'Super Mario Odyssey', platform: 'Nintendo Switch', year: 2017 }, + { game: 'Super Mario World', platform: 'Super Nintendo', year: 1990 }, + { game: 'The Legend of Zelda', platform: 'Nintendo Entertainment System', year: 1986 }, + { game: 'The Legend of Zelda: A Link to the Past', platform: 'Super Nintendo', year: 1991 }, + { game: 'The Legend of Zelda: Breath of the Wild', platform: 'Nintendo Switch', year: 2017 }, + { game: 'The Legend of Zelda: Ocarina of Time', platform: 'Nintendo 64', year: 1998 } +] + const meta: Meta = { title: 'NeTable', component: NeTable, @@ -24,7 +34,9 @@ const meta: Meta = { cardBreakpoint: 'md', loading: false, skeletonRows: 8, - skeletonColumns: 4 + skeletonColumns: 4, + sortKey: 'game', + sortDescending: false }, render: (args) => ({ components: { @@ -37,7 +49,7 @@ const meta: Meta = { NePaginator }, setup() { - return { args } + return { args, items } }, template: ` @@ -47,40 +59,10 @@ const meta: Meta = { Year - - The Legend of Zelda: Breath of the Wild - Nintendo Switch - 2017 - - - Super Mario Odyssey - Nintendo Switch - 2017 - - - The Legend of Zelda: Ocarina of Time - Nintendo 64 - 1998 - - - Super Mario 64 - Nintendo 64 - 1996 - - - The Legend of Zelda: A Link to the Past - Super Nintendo - 1991 - - - Super Mario World - Super Nintendo - 1990 - - - The Legend of Zelda - Nintendo Entertainment System - 1986 + + {{ item.game }} + {{ item.platform }} + {{ item.year }} ` @@ -101,30 +83,10 @@ const withPaginatorTemplate = ` Year - - The Legend of Zelda: Breath of the Wild - Nintendo Switch - 2017 - - - Super Mario Odyssey - Nintendo Switch - 2017 - - - The Legend of Zelda: Ocarina of Time - Nintendo 64 - 1998 - - - Super Mario 64 - Nintendo 64 - 1996 - - - The Legend of Zelda: A Link to the Past - Super Nintendo - 1991 + + {{ item.game }} + {{ item.platform }} + {{ item.year }}