Skip to content
This repository was archived by the owner on Jan 6, 2024. It is now read-only.

Commit cd9e534

Browse files
feat: dynamic show route meta fields (#199)
1 parent c06df4e commit cd9e534

File tree

4 files changed

+141
-15
lines changed

4 files changed

+141
-15
lines changed

packages/client/components/RoutesTable.vue

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
import type { RouteMeta, RouteRecordNormalized } from 'vue-router'
3+
import { tryGetAllMetaKeys } from '~/logic/routes'
34
45
const props = defineProps<{
56
pages: RouteRecordNormalized[]
@@ -20,27 +21,85 @@ function metaToString(meta: RouteMeta, num: number = 0) {
2021
const metaStr = JSON.stringify(meta, null, num)
2122
return metaStr === '{}' ? '-' : metaStr
2223
}
24+
25+
const allMetaKeys = computed(() => tryGetAllMetaKeys(props.pages))
26+
27+
const activeMetaKeys = useStorage<string[]>('__vue-devtools-route-active-meta-keys__', [],
28+
localStorage,
29+
)
30+
31+
// ensure that activeMetaKeys is always a subset of allMetaKeys
32+
watch(allMetaKeys, (v) => {
33+
activeMetaKeys.value = activeMetaKeys.value
34+
.filter(key => v.includes(key))
35+
})
36+
37+
const dynamicTableColumns = computed(() => activeMetaKeys.value.map(key => ({
38+
head: `meta.${key}`,
39+
key,
40+
})))
41+
42+
const tableHeadMeta = computed(() => {
43+
return {
44+
normal: activeMetaKeys.value ? 2 : 1,
45+
dynamic: activeMetaKeys.value.length,
46+
}
47+
})
48+
49+
const normalHead = ['', 'Route Path', 'Name']
50+
51+
const showDynamicSelector = ref(false)
52+
const dynamicSelectorEl = ref<HTMLElement>()
53+
onClickOutside(dynamicSelectorEl, () => {
54+
showDynamicSelector.value = false
55+
})
2356
</script>
2457

2558
<template>
2659
<div>
2760
<table w-full>
28-
<thead border="b base">
61+
<thead border="b base" text-left leading-8>
2962
<tr>
30-
<th text-left />
31-
<th text-left>
32-
Route Path
33-
</th>
34-
<th text-left>
35-
Name
63+
<th v-for="head of normalHead" :key="head" :rowspan="tableHeadMeta.normal">
64+
{{ head }}
3665
</th>
37-
<th text-left>
38-
Route Meta
66+
<template v-if="allMetaKeys.length">
67+
<th :colspan="tableHeadMeta.dynamic">
68+
<div flex items-center justify-between>
69+
<span>Route Meta</span>
70+
<div ref="dynamicSelectorEl" relative>
71+
<button text="xs gray-400" relative @click="() => showDynamicSelector = !showDynamicSelector">
72+
<i mingcute:filter-fill />
73+
</button>
74+
<VDDropdown v-model:show="showDynamicSelector" top-40px w-200px :items="allMetaKeys" position="right">
75+
<template #item="{ item }">
76+
<div flex items-center gap2 truncate p5px font-normal leading-none>
77+
<input
78+
v-model="activeMetaKeys"
79+
cursor-pointer
80+
type="checkbox"
81+
:value="item"
82+
>
83+
<span>{{ item }}</span>
84+
</div>
85+
</template>
86+
</VDDropdown>
87+
</div>
88+
</div>
89+
</th>
90+
</template>
91+
</tr>
92+
<tr b="t-1px gray/20">
93+
<th v-for="{ head, key } of dynamicTableColumns" :key="key">
94+
{{ head }}
3995
</th>
4096
</tr>
4197
</thead>
4298
<tbody>
43-
<tr v-for="item of sorted" :key="item.name" class="group" h-7 border="b dashed transparent hover:base">
99+
<tr
100+
v-for="item of sorted" :key="item.name"
101+
class="group" h-7 border="b dashed transparent hover:base" text-left text-sm font-mono
102+
>
44103
<td w-20 pr-1>
45104
<div flex items-center justify-end>
46105
<VDBadge
@@ -57,7 +116,7 @@ function metaToString(meta: RouteMeta, num: number = 0) {
57116
/>
58117
</div>
59118
</td>
60-
<td text-sm>
119+
<td>
61120
<div flex="inline gap3" items-center>
62121
<RoutePathItem
63122
:route="item"
@@ -67,10 +126,15 @@ function metaToString(meta: RouteMeta, num: number = 0) {
67126
/>
68127
</div>
69128
</td>
70-
<td w-30 ws-nowrap pr-1 text-left text-sm font-mono op50>
129+
<td w-30 ws-nowrap pr-1 op50>
71130
{{ item.name ?? '-' }}
72131
</td>
73-
<td w-50 ws-nowrap pr-1 text-left text-sm font-mono op50 hover="text-primary op100">
132+
<template v-if="dynamicTableColumns.length">
133+
<td v-for="{ key } in dynamicTableColumns " :key="key" truncate ws-nowrap op50>
134+
{{ item.meta[key]?.toString() ?? '-' }}
135+
</td>
136+
</template>
137+
<td v-else w-50 ws-nowrap pr-1 op50 hover="text-primary op100">
74138
<span inline-block w-50 cursor-pointer overflow-hidden text-ellipsis :title="metaToString(item.meta, 2)" @click="() => $emit('selectMeta', item.meta)">{{ metaToString(item.meta) }}</span>
75139
</td>
76140
</tr>

packages/client/logic/routes.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,11 @@ export function initRoutes(buffer: [string, Record<string, any>][]) {
301301
subscribeRouterChanged(router.value)
302302
}
303303
}
304+
305+
export function tryGetAllMetaKeys(route: RouteRecordNormalized[]) {
306+
const keys = new Set<string>()
307+
route.forEach((record) => {
308+
Object.keys(record.meta).forEach(key => keys.add(key))
309+
})
310+
return Array.from(keys)
311+
}

packages/playground/src/main.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createApp } from 'vue'
2+
import type { RouteRecordRaw } from 'vue-router'
23
import { createRouter, createWebHistory } from 'vue-router'
34
import { createPinia } from 'pinia'
45
import Home from './pages/Home.vue'
@@ -21,14 +22,28 @@ const pinia = createPinia()
2122
// })
2223
const app = createApp(App)
2324

24-
const routes = [
25-
{ path: '/', component: Home, name: 'home', alias: '/index' },
25+
const routes: RouteRecordRaw[] = [
26+
{
27+
path: '/',
28+
component: Home,
29+
name: 'home',
30+
alias: '/index',
31+
meta: {
32+
title: 'Home',
33+
some: 'a',
34+
foo: 'bar',
35+
},
36+
},
2637
{
2738
path: '/about',
2839
component: About,
2940
children: [
3041
{ path: '/about/:id', component: About },
3142
],
43+
meta: {
44+
title: 'About',
45+
some: 'b',
46+
},
3247
},
3348
// { path: '/:articleName', component: About },
3449
]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<script setup lang="ts" generic="T">
2+
const props = withDefaults(defineProps<{
3+
show: boolean
4+
items: T[]
5+
position?: 'left' | 'right'
6+
}>(), {
7+
position: 'left',
8+
})
9+
10+
const emit = defineEmits<{
11+
'update:show': [boolean]
12+
}>()
13+
14+
defineSlots<{
15+
item(props: { item: T }): any
16+
}>()
17+
18+
const show = useVModel(props, 'show', emit, { passive: true })
19+
</script>
20+
21+
<template>
22+
<Transition
23+
name="dropdown"
24+
enter-active-class="transition-all"
25+
leave-active-class="transition-all"
26+
enter-from-class="opacity-0 scale-95"
27+
leave-to-class="opacity-0 scale-95"
28+
>
29+
<div
30+
v-if="show"
31+
absolute z-100 min-w-100px rounded p-2 shadow-lg bg-base
32+
:class="[position === 'left' ? 'left-0' : 'right-0']"
33+
>
34+
<div v-for="item of items" :key="`${item}`">
35+
<slot name="item" :item="item" />
36+
</div>
37+
</div>
38+
</Transition>
39+
</template>

0 commit comments

Comments
 (0)