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

feat: dynamic show route meta fields #199

Merged
merged 1 commit into from
Aug 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 77 additions & 13 deletions packages/client/components/RoutesTable.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import type { RouteMeta, RouteRecordNormalized } from 'vue-router'
import { tryGetAllMetaKeys } from '~/logic/routes'

const props = defineProps<{
pages: RouteRecordNormalized[]
Expand All @@ -20,27 +21,85 @@ function metaToString(meta: RouteMeta, num: number = 0) {
const metaStr = JSON.stringify(meta, null, num)
return metaStr === '{}' ? '-' : metaStr
}

const allMetaKeys = computed(() => tryGetAllMetaKeys(props.pages))

const activeMetaKeys = useStorage<string[]>('__vue-devtools-route-active-meta-keys__', [],
localStorage,
)

// ensure that activeMetaKeys is always a subset of allMetaKeys
watch(allMetaKeys, (v) => {
activeMetaKeys.value = activeMetaKeys.value
.filter(key => v.includes(key))
})

const dynamicTableColumns = computed(() => activeMetaKeys.value.map(key => ({
head: `meta.${key}`,
key,
})))

const tableHeadMeta = computed(() => {
return {
normal: activeMetaKeys.value ? 2 : 1,
dynamic: activeMetaKeys.value.length,
}
})

const normalHead = ['', 'Route Path', 'Name']

const showDynamicSelector = ref(false)
const dynamicSelectorEl = ref<HTMLElement>()
onClickOutside(dynamicSelectorEl, () => {
showDynamicSelector.value = false
})
</script>

<template>
<div>
<table w-full>
<thead border="b base">
<thead border="b base" text-left leading-8>
<tr>
<th text-left />
<th text-left>
Route Path
</th>
<th text-left>
Name
<th v-for="head of normalHead" :key="head" :rowspan="tableHeadMeta.normal">
{{ head }}
</th>
<th text-left>
Route Meta
<template v-if="allMetaKeys.length">
<th :colspan="tableHeadMeta.dynamic">
<div flex items-center justify-between>
<span>Route Meta</span>
<div ref="dynamicSelectorEl" relative>
<button text="xs gray-400" relative @click="() => showDynamicSelector = !showDynamicSelector">
<i mingcute:filter-fill />
</button>
<VDDropdown v-model:show="showDynamicSelector" top-40px w-200px :items="allMetaKeys" position="right">
<template #item="{ item }">
<div flex items-center gap2 truncate p5px font-normal leading-none>
<input
v-model="activeMetaKeys"
cursor-pointer
type="checkbox"
:value="item"
>
<span>{{ item }}</span>
</div>
</template>
</VDDropdown>
</div>
</div>
</th>
</template>
</tr>
<tr b="t-1px gray/20">
<th v-for="{ head, key } of dynamicTableColumns" :key="key">
{{ head }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="item of sorted" :key="item.name" class="group" h-7 border="b dashed transparent hover:base">
<tr
v-for="item of sorted" :key="item.name"
class="group" h-7 border="b dashed transparent hover:base" text-left text-sm font-mono
>
<td w-20 pr-1>
<div flex items-center justify-end>
<VDBadge
Expand All @@ -57,7 +116,7 @@ function metaToString(meta: RouteMeta, num: number = 0) {
/>
</div>
</td>
<td text-sm>
<td>
<div flex="inline gap3" items-center>
<RoutePathItem
:route="item"
Expand All @@ -67,10 +126,15 @@ function metaToString(meta: RouteMeta, num: number = 0) {
/>
</div>
</td>
<td w-30 ws-nowrap pr-1 text-left text-sm font-mono op50>
<td w-30 ws-nowrap pr-1 op50>
{{ item.name ?? '-' }}
</td>
<td w-50 ws-nowrap pr-1 text-left text-sm font-mono op50 hover="text-primary op100">
<template v-if="dynamicTableColumns.length">
<td v-for="{ key } in dynamicTableColumns " :key="key" truncate ws-nowrap op50>
{{ item.meta[key]?.toString() ?? '-' }}
</td>
</template>
<td v-else w-50 ws-nowrap pr-1 op50 hover="text-primary op100">
<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>
</td>
</tr>
Expand Down
8 changes: 8 additions & 0 deletions packages/client/logic/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,11 @@ export function initRoutes(buffer: [string, Record<string, any>][]) {
subscribeRouterChanged(router.value)
}
}

export function tryGetAllMetaKeys(route: RouteRecordNormalized[]) {
const keys = new Set<string>()
route.forEach((record) => {
Object.keys(record.meta).forEach(key => keys.add(key))
})
return Array.from(keys)
}
19 changes: 17 additions & 2 deletions packages/playground/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createApp } from 'vue'
import type { RouteRecordRaw } from 'vue-router'
import { createRouter, createWebHistory } from 'vue-router'
import { createPinia } from 'pinia'
import Home from './pages/Home.vue'
Expand All @@ -21,14 +22,28 @@ const pinia = createPinia()
// })
const app = createApp(App)

const routes = [
{ path: '/', component: Home, name: 'home', alias: '/index' },
const routes: RouteRecordRaw[] = [
{
path: '/',
component: Home,
name: 'home',
alias: '/index',
meta: {
title: 'Home',
some: 'a',
foo: 'bar',
},
},
{
path: '/about',
component: About,
children: [
{ path: '/about/:id', component: About },
],
meta: {
title: 'About',
some: 'b',
},
},
// { path: '/:articleName', component: About },
]
Expand Down
39 changes: 39 additions & 0 deletions packages/ui-kit/src/components/Dropdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script setup lang="ts" generic="T">
const props = withDefaults(defineProps<{
show: boolean
items: T[]
position?: 'left' | 'right'
}>(), {
position: 'left',
})

const emit = defineEmits<{
'update:show': [boolean]
}>()

defineSlots<{
item(props: { item: T }): any
}>()

const show = useVModel(props, 'show', emit, { passive: true })
</script>

<template>
<Transition
name="dropdown"
enter-active-class="transition-all"
leave-active-class="transition-all"
enter-from-class="opacity-0 scale-95"
leave-to-class="opacity-0 scale-95"
>
<div
v-if="show"
absolute z-100 min-w-100px rounded p-2 shadow-lg bg-base
:class="[position === 'left' ? 'left-0' : 'right-0']"
>
<div v-for="item of items" :key="`${item}`">
<slot name="item" :item="item" />
</div>
</div>
</Transition>
</template>