(Add) AsideNav

This commit is contained in:
kakkokari-gtyih 2023-09-24 03:02:04 +09:00
parent 6623b2acae
commit 98c9a32709
12 changed files with 138 additions and 68 deletions

View File

@ -1,3 +1,5 @@
import type { NavItem } from '@nuxt/content/dist/runtime/types';
export function resolveObjPath(o: object, s: string): any {
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
s = s.replace(/^\./, ''); // strip a leading dot
@ -40,7 +42,7 @@ export function isLocalPath(link: string, base?: string): boolean {
}
export const findDeepObject = (obj: Record<string, any>, condition: (v: any) => boolean): Record<string, any> | null => {
export const findDeepObject = (obj: NavItem, condition: (v: NavItem) => boolean): NavItem | null => {
if (condition(obj)) {
return obj;
}

View File

@ -2,14 +2,14 @@
<table>
<thead>
<tr>
<th>Permisson</th>
<th>Description</th>
<th>{{ $t('_api._permissions.title') }}</th>
<th>{{ $t('_api._permissions.description') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="permission in permissions">
<td><code>{{ permission }}</code></td>
<td>{{ $t(`_api._permissions.${permission}`) }}</td>
<td>{{ $t(`_api._permissions._types.${permission}`) }}</td>
</tr>
</tbody>
</table>

View File

@ -1,61 +1,78 @@
<template>
<ul class="toc-links mb-4 space-y-2">
<ul class="toc-links text-sm" :class="[
`depth-${depth}`,
depth === 1 ? 'mb-4' : 'mb-2',
]">
<li
v-for="link in findDeepObject((links[0] as NavItem), (v) => realBasePath.replace(/\/$/, '') === v?._path.replace(/\/$/, ''))?.children ?? []"
v-for="link in realLinks ?? []"
:key="link.text"
:class="[`depth-${link.depth}`]"
:class="[
depth === 2 && 'border-l-2 flex flex-col',
path.includes(link._path) ? 'border-accent-500' : 'border-gray-300',
]"
>
<GNuxtLink
:to="link._path"
@click.passive="emit('child-click');"
:class="['hover:text-accent-600']"
:class="[
'block hover:text-accent-600',
depth === 1 && 'text-base',
depth === 2 ? 'px-2 py-1' : 'py-1',
isSamePath(path, link._path) && 'text-accent-600 font-bold',
]"
>
{{ link.text }}
<div class="flex">
<div v-if="link.children && link.children.filter((v) => !isSamePath(v._path, link._path)).length > 0" class="mr-2">
<ArrowIco
:class="[
'transition-transform',
path.includes(link._path) && 'rotate-90'
]"
/>
</div>
<div>{{ link.title }}</div>
</div>
</GNuxtLink>
<AsideNav
class="mt-2"
v-if="link.children"
:links="link.children"
v-if="link.children && link.children.filter((v) => !isSamePath(v._path, link._path)).length > 0"
v-show="path.includes(link._path)"
:links="[link]"
:depth="depth + 1"
/>
</li>
</ul>
</template>
<script setup lang="ts">
import type { PropType } from 'vue'
import type { NavItem } from '@nuxt/content/dist/runtime/types'
import { findDeepObject } from 'assets/js/misc';
import { findDeepObject } from '@/assets/js/misc';
import { isSamePath } from 'ufo';
import ArrowIco from "bi/chevron-right.svg";
const props = defineProps({
links: {
type: Array as PropType<NavItem[]>,
default: () => []
},
basePath: {
type: String,
default: '',
}
const props = withDefaults(defineProps<{
links: NavItem[];
depth?: number;
}>(), {
depth: 1,
});
const { locale } = useI18n();
const route = useRoute();
const { path } = useRoute();
const realBasePath = computed<string>(() => {
if (props.basePath) {
return props.basePath;
const realLinks = findDeepObject(props.links[0], (v) => {
if (props.depth === 1) {
return isSamePath(`/${locale.value}/docs/`, v._path);
} else {
return v._path.includes(props.links[0]._path);
}
return route.path.replace(/^.*\/docs/, `/${locale.value}/docs`);
});
})?.children?.filter((v) => !isSamePath(v._path, props.links[0]._path));
const emit = defineEmits(['move', 'child-click']);
</script>
<style scoped>
.toc-links ::v-deep(.depth-3) {
@apply ml-2;
}
.toc-links ::v-deep(.depth-4) {
@apply ml-4;
.toc-links:not(.depth-1) {
@apply ml-6;
}
</style>

View File

@ -9,7 +9,7 @@
</div>
<ul class="hidden lg:col-span-4 lg:space-x-8 xl:space-x-10 lg:flex justify-center">
<li v-for="item in NavData.center">
<GNuxtLink :to="localePath(item.to)" :class="['rounded-full px-4 py-1.5 hover:bg-slate-300 dark:hover:bg-slate-800 hover:bg-opacity-50 bg-blend-multiply', { 'bg-slate-200 dark:bg-slate-800 font-bold': currentPath.replace(/\/$/, '').includes(localePath(item.to).replace(/\/$/, '')) }]">
<GNuxtLink :to="localePath(item.to)" :class="['rounded-full px-4 py-1.5 hover:bg-slate-300 dark:hover:bg-slate-800 hover:bg-opacity-50 bg-blend-multiply', { 'bg-slate-200 dark:bg-slate-800 font-bold': currentPath.includes(item.to) }]">
<component v-if="item.icon" :is="item.icon" class="h-5 w-5" />
<template v-else>
{{ $t(item.i18n) }}
@ -77,9 +77,11 @@ const { locales, locale: currentLocale } = useI18n();
const { path } = useRoute();
const { afterEach } = useRouter();
const currentPath = ref(path);
afterEach((to) => {
currentPath.value = to.path;
})
});
const switchLocalePath = useSwitchLocalePath();
const localePath = useLocalePath();

View File

@ -0,0 +1,2 @@
title: "開発者向け"
description: "プラグイン・Play開発者や、APIを利用した外部アプリケーションの開発者向けリソース。"

View File

@ -2,5 +2,10 @@
<div class="text-slate-800 dark:text-slate-200 bg-slate-100 dark:bg-gray-900">
<noscript class="block bg-accent-800 text-white text-center py-1.5 px-3 keep-all relative z-[10005]">Please turn on Javascript from your browser's settings.</noscript>
ページが見つかりませんでした
<pre>{{ error }}</pre>
</div>
</template>
<script setup lang="ts">
const error = useError();
</script>

View File

@ -6,13 +6,40 @@ useHead({
class: 'scroll-pt-32 lg:scroll-pt-20',
},
});
const { locale } = useI18n();
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation(queryContent(`/${locale.value}/docs/`)));
</script>
<template>
<div class="bg-white dark:bg-slate-950">
<GNav @toggleNav="isNavOpen = !isNavOpen" :is-open="isNavOpen" :slim="true" :disable-shadow="true" />
<div class="main-content">
<slot></slot>
<div class="relative container mx-auto max-w-screen-xl p-6 lg:py-0 grid docs-root pb-12">
<slot name="spToc"></slot>
<div class="hidden lg:block">
<slot name="right">
<div class="sticky top-16 h-[calc(100vh-4rem)] overflow-y-scroll border-r border-slate-200 dark:border-slate-700 py-6 pr-3">
<DocsAsideNav :links="navigation ?? []" />
</div>
</slot>
</div>
<div class="pt-6 lg:p-6 w-full overflow-x-hidden">
<slot name="main"></slot>
</div>
<div class="hidden lg:block text-sm">
<slot name="left"></slot>
</div>
</div>
</div>
<GFooter class="bg-slate-100 dark:bg-gray-900" />
</div>
</template>
<style scoped>
@screen lg {
.docs-root {
grid-template-columns: 16rem 1fr 14rem;
}
}
</style>

View File

@ -160,3 +160,7 @@ _links:
_aiChan:
title: "藍"
description: "Misskeyの看板娘、藍のウェブサイトです。"
_api:
_permissions:
title: "権限"
description: "説明"

View File

@ -9,6 +9,7 @@
"postinstall": "nuxt prepare"
},
"devDependencies": {
"@babel/parser": "^7.22.16",
"@modyfi/vite-plugin-yaml": "^1.0.4",
"@nuxt/content": "^2.8.0",
"@nuxtjs/color-mode": "^3.3.0",

View File

@ -1,19 +1,19 @@
<template>
<div class="relative container mx-auto max-w-screen-xl p-6 lg:py-0 grid docs-root pb-12">
<div v-if="data?.body" class="lg:hidden sticky top-16 -mx-6 -mt-6 bg-white px-6 bg-opacity-60 backdrop-blur-lg z-[9890] border-b text-sm">
<details :open="openState">
<summary class="py-4 cursor-pointer">{{ $t('_docs._toc.title') }}</summary>
<div class="pb-4 overflow-y-auto">
<DocsTocLinks :links="data?.body.toc.links" @child-click="openState = false" />
</div>
</details>
</div>
<div class="hidden lg:block">
<div class="sticky top-16 h-[calc(100vh-4rem)] overflow-y-scroll border-r border-slate-200 dark:border-slate-700 py-6 pr-6">
<DocsAsideNav :links="navigation" />
<NuxtLayout name="docs">
<template #spToc>
<div v-if="data?.body" class="lg:hidden sticky top-16 -mx-6 -mt-6 bg-white px-6 bg-opacity-60 backdrop-blur-lg z-[9890] border-b text-sm">
<details :open="openState">
<summary class="py-4 cursor-pointer">
{{ $t('_docs._toc.title') }}
</summary>
<div class="pb-4 overflow-y-auto">
<DocsTocLinks :links="data?.body.toc.links" @child-click="openState = false" />
</div>
</details>
</div>
</div>
<div class="pt-6 lg:p-6 w-full overflow-x-hidden">
</template>
<template #main>
<template v-if="data?.body">
<ContentRenderer v-if="data.body.children.length > 0" :value="data" class="markdown-body w-full mb-6">
</ContentRenderer>
@ -25,14 +25,15 @@
<MkIndex :is-dir="data?._file?.endsWith('index.md') || (!data?._file)" />
</div>
</template>
</div>
<div class="hidden lg:block text-sm">
</template>
<template #left>
<div class="sticky top-16 h-[calc(100vh-4rem)] overflow-y-auto py-6 pl-6">
<h3 class="font-bold mb-6">{{ $t('_docs._toc.title') }}</h3>
<DocsTocLinks v-if="data?.body" :links="data?.body.toc.links" class="break-words" />
</div>
</div>
</div>
</template>
</NuxtLayout>
</template>
<script setup lang="ts">
@ -42,7 +43,7 @@ const { locale, locales } = useI18n();
const openState = ref<boolean>(false);
definePageMeta({
layout: 'docs',
layout: false,
});
defineI18nRoute({
@ -55,7 +56,6 @@ const route = useRoute();
const slugs = (route.params.slug as string[]).filter((v) => v !== '');
const { data } = await useAsyncData(`blog-${locale.value}-${slugs.join('-')}`, () => queryContent(`/${locale.value}/docs/${slugs.join('/')}`).findOne());
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation(queryContent(`/${locale.value}/docs/${slugs[0]}`)));
if (!data.value) {
throw createError({ statusCode: 404, statusMessage: 'page not found' });
@ -63,11 +63,3 @@ if (!data.value) {
route.meta.title = data.value?.title;
</script>
<style scoped>
@screen lg {
.docs-root {
grid-template-columns: 14rem 1fr 14rem;
}
}
</style>

View File

@ -10,6 +10,9 @@ dependencies:
version: 4.1.0
devDependencies:
'@babel/parser':
specifier: ^7.22.16
version: 7.22.16
'@modyfi/vite-plugin-yaml':
specifier: ^1.0.4
version: 1.0.4(vite@4.4.9)

View File

@ -2,7 +2,9 @@
import { locales } from "@/nuxt.config";
import * as misskey from "misskey-js";
import yaml from "js-yaml";
import fs from "fs";
import path from "path";
import { parse } from "@babel/parser";
// オブジェクト obj1 にないキーだけを、元オブジェクトにマージする関数
function mergeObjects(obj1: Record<string, any>, obj2: Record<string, any>): Record<string, any> {
@ -28,5 +30,18 @@ export function genApiTranslationFiles() {
out._permissions[permission] = 'Untranslated / 未翻訳';
});
// エンティティ(TODO)
const endpointDTSPath = require.resolve('misskey-js', { paths: ['/built/api.types.d.ts'] });
/*
const ep = fs.readFileSync(endpointDTSPath, "utf-8");
const parsedEP = parse(ep, {
sourceType: "module",
plugins: [[
"typescript", {
dts: true,
},
]],
});
console.log(JSON.stringify(parsedEP));
*/
}