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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy