Skip to content

Commit 310492b

Browse files
committed
feat(web-core): create useRanked composable
1 parent 7fd4528 commit 310492b

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# `useRanked` composable
2+
3+
This composable helps sort items based on a predefined ranking order.
4+
5+
It takes an array of items and a ranking order, and returns a computed sorted array according to the ranking.
6+
7+
## Usage
8+
9+
### Signature 1: Sorting ranks directly
10+
11+
```ts
12+
const sortedRanks = useRanked(ranks, ranking)
13+
```
14+
15+
| | Required | Type | Default | Description |
16+
| --------- | :------: | --------------------------- | ------- | ---------------------------------------------------------- |
17+
| `ranks` || `MaybeRefOrGetter<TRank[]>` | | The array of ranks to sort |
18+
| `ranking` || `TRank[]` | | The array of ranks ordered from highest to lowest priority |
19+
20+
#### Return value
21+
22+
| | Type | Description |
23+
| ------------- | ---------------------- | --------------------------------------------------------------- |
24+
| `sortedRanks` | `ComputedRef<TRank[]>` | A computed array of ranks sorted according to the ranking order |
25+
26+
- Ranks are sorted based on their position in the `ranking` array (lower index = higher priority)
27+
- Ranks not found in the `ranking` array are placed after all defined ranks in their original order
28+
29+
#### Example: Sorting priority levels
30+
31+
```ts
32+
import { useRanked } from '@core/composables/ranked.composable'
33+
import { ref } from 'vue'
34+
35+
// Priority levels in random order
36+
const currentPriorities = ref(['high', 'medium', 'low', 'critical', 'low', 'medium', 'high'])
37+
38+
// Define the ranking order (from highest to lowest priority)
39+
const priorityRanking = ['critical', 'high', 'medium', 'low']
40+
41+
// Sort priorities according to the ranking
42+
const sortedPriorities = useRanked(currentPriorities, priorityRanking)
43+
44+
// sortedPriorities.value will be:
45+
// ['critical', 'high', 'high', 'medium', 'medium', 'low', 'low']
46+
```
47+
48+
### Signature 2: Sorting items by their rank
49+
50+
```ts
51+
const sortedItems = useRanked(items, getRank, ranking)
52+
```
53+
54+
| | Required | Type | Default | Description |
55+
| --------- | :------: | --------------------------- | ------- | ---------------------------------------------------------- |
56+
| `items` || `MaybeRefOrGetter<TItem[]>` | | The array of items to sort |
57+
| `getRank` || `(item: TItem) => TRank` | | A function to extract rank from an item |
58+
| `ranking` || `TRank[]` | | The array of ranks ordered from highest to lowest priority |
59+
60+
#### Return value
61+
62+
| | Type | Description |
63+
| ------------- | ---------------------- | ------------------------------------------------- |
64+
| `sortedItems` | `ComputedRef<TItem[]>` | A computed array of items sorted by their ranking |
65+
66+
- Items are sorted based on their rank in the `ranking` array (lower index = higher priority)
67+
- Items with ranks not found in the `ranking` array are placed after all ranked items in their original order
68+
69+
#### Example: Sorting tasks by priority
70+
71+
```ts
72+
import { useRanked } from '@core/composables/ranked.composable'
73+
import { ref } from 'vue'
74+
75+
// Tasks with different priorities
76+
const tasks = ref([
77+
{
78+
id: 1,
79+
title: 'Fix login bug',
80+
priority: 'high',
81+
},
82+
{
83+
id: 2,
84+
title: 'Update documentation',
85+
priority: 'low',
86+
},
87+
{
88+
id: 3,
89+
title: 'Server outage',
90+
priority: 'critical',
91+
},
92+
{
93+
id: 4,
94+
title: 'Refactor components',
95+
priority: 'medium',
96+
},
97+
{
98+
id: 5,
99+
title: 'Design review',
100+
priority: 'medium',
101+
},
102+
])
103+
104+
// Define the ranking order (from highest to lowest priority)
105+
const priorityRanking = ['critical', 'high', 'medium', 'low']
106+
107+
// Sort tasks by priority according to the ranking
108+
const sortedTasks = useRanked(tasks, task => task.priority, priorityRanking)
109+
110+
// sortedTasks.value will be:
111+
// [
112+
// { id: 3, title: 'Server outage', priority: 'critical' },
113+
// { id: 1, title: 'Fix login bug', priority: 'high' },
114+
// { id: 4, title: 'Refactor components', priority: 'medium' },
115+
// { id: 5, title: 'Design review', priority: 'medium' },
116+
// { id: 2, title: 'Update documentation', priority: 'low' }
117+
// ]
118+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { clamp, useSorted } from '@vueuse/core'
2+
import { computed, type ComputedRef, type MaybeRefOrGetter, toValue } from 'vue'
3+
4+
export function useRanked<TRank extends string | number>(
5+
ranks: MaybeRefOrGetter<TRank[]>,
6+
ranking: NoInfer<TRank>[]
7+
): ComputedRef<TRank[]>
8+
9+
export function useRanked<TItem, TRank extends string | number>(
10+
items: MaybeRefOrGetter<TItem[]>,
11+
getRank: (item: TItem) => TRank,
12+
ranking: NoInfer<TRank>[]
13+
): ComputedRef<TItem[]>
14+
15+
export function useRanked<TItem, TRank extends string | number>(
16+
items: MaybeRefOrGetter<TItem[]>,
17+
ranksOrGetRank: TRank[] | ((item: TItem) => TRank),
18+
ranksOrNone?: TRank[]
19+
) {
20+
const getRank = typeof ranksOrGetRank === 'function' ? ranksOrGetRank : (item: TItem) => item as unknown as TRank
21+
22+
const ranks = ranksOrNone === undefined ? (ranksOrGetRank as TRank[]) : ranksOrNone
23+
24+
const ranksMap = computed(() => Object.fromEntries(ranks.map((rank, index) => [rank, index + 1]))) as ComputedRef<
25+
Record<TRank, number>
26+
>
27+
28+
function getRankNumber(item: TItem): number {
29+
return ranksMap.value[getRank(item)] ?? toValue(items).length + 1
30+
}
31+
32+
function compare(item1: TItem, item2: TItem) {
33+
return clamp(getRankNumber(item1) - getRankNumber(item2), -1, 1) as -1 | 0 | 1
34+
}
35+
36+
return useSorted(items, compare)
37+
}

0 commit comments

Comments
 (0)