mirror of
https://iceshrimp.dev/Crimekillz/jointrashposs.git
synced 2024-11-22 08:53:49 +01:00
(Add) AsideNav
This commit is contained in:
parent
6623b2acae
commit
98c9a32709
@ -1,3 +1,5 @@
|
|||||||
|
import type { NavItem } from '@nuxt/content/dist/runtime/types';
|
||||||
|
|
||||||
export function resolveObjPath(o: object, s: string): any {
|
export function resolveObjPath(o: object, s: string): any {
|
||||||
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
|
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
|
||||||
s = s.replace(/^\./, ''); // strip a leading dot
|
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)) {
|
if (condition(obj)) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Permisson</th>
|
<th>{{ $t('_api._permissions.title') }}</th>
|
||||||
<th>Description</th>
|
<th>{{ $t('_api._permissions.description') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="permission in permissions">
|
<tr v-for="permission in permissions">
|
||||||
<td><code>{{ permission }}</code></td>
|
<td><code>{{ permission }}</code></td>
|
||||||
<td>{{ $t(`_api._permissions.${permission}`) }}</td>
|
<td>{{ $t(`_api._permissions._types.${permission}`) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -1,61 +1,78 @@
|
|||||||
<template>
|
<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
|
<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"
|
: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
|
<GNuxtLink
|
||||||
:to="link._path"
|
:to="link._path"
|
||||||
@click.passive="emit('child-click');"
|
@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>
|
</GNuxtLink>
|
||||||
<AsideNav
|
<AsideNav
|
||||||
class="mt-2"
|
v-if="link.children && link.children.filter((v) => !isSamePath(v._path, link._path)).length > 0"
|
||||||
v-if="link.children"
|
v-show="path.includes(link._path)"
|
||||||
:links="link.children"
|
:links="[link]"
|
||||||
|
:depth="depth + 1"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PropType } from 'vue'
|
|
||||||
import type { NavItem } from '@nuxt/content/dist/runtime/types'
|
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({
|
const props = withDefaults(defineProps<{
|
||||||
links: {
|
links: NavItem[];
|
||||||
type: Array as PropType<NavItem[]>,
|
depth?: number;
|
||||||
default: () => []
|
}>(), {
|
||||||
},
|
depth: 1,
|
||||||
basePath: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { locale } = useI18n();
|
const { locale } = useI18n();
|
||||||
const route = useRoute();
|
const { path } = useRoute();
|
||||||
|
|
||||||
const realBasePath = computed<string>(() => {
|
const realLinks = findDeepObject(props.links[0], (v) => {
|
||||||
if (props.basePath) {
|
if (props.depth === 1) {
|
||||||
return props.basePath;
|
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']);
|
const emit = defineEmits(['move', 'child-click']);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.toc-links ::v-deep(.depth-3) {
|
.toc-links:not(.depth-1) {
|
||||||
@apply ml-2;
|
@apply ml-6;
|
||||||
}
|
|
||||||
.toc-links ::v-deep(.depth-4) {
|
|
||||||
@apply ml-4;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ul class="hidden lg:col-span-4 lg:space-x-8 xl:space-x-10 lg:flex justify-center">
|
<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">
|
<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" />
|
<component v-if="item.icon" :is="item.icon" class="h-5 w-5" />
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ $t(item.i18n) }}
|
{{ $t(item.i18n) }}
|
||||||
@ -77,9 +77,11 @@ const { locales, locale: currentLocale } = useI18n();
|
|||||||
const { path } = useRoute();
|
const { path } = useRoute();
|
||||||
const { afterEach } = useRouter();
|
const { afterEach } = useRouter();
|
||||||
const currentPath = ref(path);
|
const currentPath = ref(path);
|
||||||
|
|
||||||
afterEach((to) => {
|
afterEach((to) => {
|
||||||
currentPath.value = to.path;
|
currentPath.value = to.path;
|
||||||
})
|
});
|
||||||
|
|
||||||
const switchLocalePath = useSwitchLocalePath();
|
const switchLocalePath = useSwitchLocalePath();
|
||||||
const localePath = useLocalePath();
|
const localePath = useLocalePath();
|
||||||
|
|
||||||
|
2
content/ja/docs/4.for-developers/_dir.yml
Normal file
2
content/ja/docs/4.for-developers/_dir.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
title: "開発者向け"
|
||||||
|
description: "プラグイン・Play開発者や、APIを利用した外部アプリケーションの開発者向けリソース。"
|
@ -2,5 +2,10 @@
|
|||||||
<div class="text-slate-800 dark:text-slate-200 bg-slate-100 dark:bg-gray-900">
|
<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>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const error = useError();
|
||||||
|
</script>
|
@ -6,13 +6,40 @@ useHead({
|
|||||||
class: 'scroll-pt-32 lg:scroll-pt-20',
|
class: 'scroll-pt-32 lg:scroll-pt-20',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { locale } = useI18n();
|
||||||
|
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation(queryContent(`/${locale.value}/docs/`)));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-white dark:bg-slate-950">
|
<div class="bg-white dark:bg-slate-950">
|
||||||
<GNav @toggleNav="isNavOpen = !isNavOpen" :is-open="isNavOpen" :slim="true" :disable-shadow="true" />
|
<GNav @toggleNav="isNavOpen = !isNavOpen" :is-open="isNavOpen" :slim="true" :disable-shadow="true" />
|
||||||
<div class="main-content">
|
<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>
|
</div>
|
||||||
<GFooter class="bg-slate-100 dark:bg-gray-900" />
|
<GFooter class="bg-slate-100 dark:bg-gray-900" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@screen lg {
|
||||||
|
.docs-root {
|
||||||
|
grid-template-columns: 16rem 1fr 14rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -160,3 +160,7 @@ _links:
|
|||||||
_aiChan:
|
_aiChan:
|
||||||
title: "藍"
|
title: "藍"
|
||||||
description: "Misskeyの看板娘、藍のウェブサイトです。"
|
description: "Misskeyの看板娘、藍のウェブサイトです。"
|
||||||
|
_api:
|
||||||
|
_permissions:
|
||||||
|
title: "権限"
|
||||||
|
description: "説明"
|
@ -9,6 +9,7 @@
|
|||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/parser": "^7.22.16",
|
||||||
"@modyfi/vite-plugin-yaml": "^1.0.4",
|
"@modyfi/vite-plugin-yaml": "^1.0.4",
|
||||||
"@nuxt/content": "^2.8.0",
|
"@nuxt/content": "^2.8.0",
|
||||||
"@nuxtjs/color-mode": "^3.3.0",
|
"@nuxtjs/color-mode": "^3.3.0",
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative container mx-auto max-w-screen-xl p-6 lg:py-0 grid docs-root pb-12">
|
<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">
|
<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">
|
<details :open="openState">
|
||||||
<summary class="py-4 cursor-pointer">{{ $t('_docs._toc.title') }}</summary>
|
<summary class="py-4 cursor-pointer">
|
||||||
|
{{ $t('_docs._toc.title') }}
|
||||||
|
</summary>
|
||||||
<div class="pb-4 overflow-y-auto">
|
<div class="pb-4 overflow-y-auto">
|
||||||
<DocsTocLinks :links="data?.body.toc.links" @child-click="openState = false" />
|
<DocsTocLinks :links="data?.body.toc.links" @child-click="openState = false" />
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
<div class="hidden lg:block">
|
</template>
|
||||||
<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" />
|
<template #main>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="pt-6 lg:p-6 w-full overflow-x-hidden">
|
|
||||||
<template v-if="data?.body">
|
<template v-if="data?.body">
|
||||||
<ContentRenderer v-if="data.body.children.length > 0" :value="data" class="markdown-body w-full mb-6">
|
<ContentRenderer v-if="data.body.children.length > 0" :value="data" class="markdown-body w-full mb-6">
|
||||||
</ContentRenderer>
|
</ContentRenderer>
|
||||||
@ -25,14 +25,15 @@
|
|||||||
<MkIndex :is-dir="data?._file?.endsWith('index.md') || (!data?._file)" />
|
<MkIndex :is-dir="data?._file?.endsWith('index.md') || (!data?._file)" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</template>
|
||||||
<div class="hidden lg:block text-sm">
|
|
||||||
|
<template #left>
|
||||||
<div class="sticky top-16 h-[calc(100vh-4rem)] overflow-y-auto py-6 pl-6">
|
<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>
|
<h3 class="font-bold mb-6">{{ $t('_docs._toc.title') }}</h3>
|
||||||
<DocsTocLinks v-if="data?.body" :links="data?.body.toc.links" class="break-words" />
|
<DocsTocLinks v-if="data?.body" :links="data?.body.toc.links" class="break-words" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</NuxtLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -42,7 +43,7 @@ const { locale, locales } = useI18n();
|
|||||||
const openState = ref<boolean>(false);
|
const openState = ref<boolean>(false);
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'docs',
|
layout: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
defineI18nRoute({
|
defineI18nRoute({
|
||||||
@ -55,7 +56,6 @@ const route = useRoute();
|
|||||||
const slugs = (route.params.slug as string[]).filter((v) => v !== '');
|
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 } = 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) {
|
if (!data.value) {
|
||||||
throw createError({ statusCode: 404, statusMessage: 'page not found' });
|
throw createError({ statusCode: 404, statusMessage: 'page not found' });
|
||||||
@ -63,11 +63,3 @@ if (!data.value) {
|
|||||||
|
|
||||||
route.meta.title = data.value?.title;
|
route.meta.title = data.value?.title;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
@screen lg {
|
|
||||||
.docs-root {
|
|
||||||
grid-template-columns: 14rem 1fr 14rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -10,6 +10,9 @@ dependencies:
|
|||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@babel/parser':
|
||||||
|
specifier: ^7.22.16
|
||||||
|
version: 7.22.16
|
||||||
'@modyfi/vite-plugin-yaml':
|
'@modyfi/vite-plugin-yaml':
|
||||||
specifier: ^1.0.4
|
specifier: ^1.0.4
|
||||||
version: 1.0.4(vite@4.4.9)
|
version: 1.0.4(vite@4.4.9)
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
import { locales } from "@/nuxt.config";
|
import { locales } from "@/nuxt.config";
|
||||||
import * as misskey from "misskey-js";
|
import * as misskey from "misskey-js";
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import { parse } from "@babel/parser";
|
||||||
|
|
||||||
// オブジェクト obj1 にないキーだけを、元オブジェクトにマージする関数
|
// オブジェクト obj1 にないキーだけを、元オブジェクトにマージする関数
|
||||||
function mergeObjects(obj1: Record<string, any>, obj2: Record<string, any>): Record<string, any> {
|
function mergeObjects(obj1: Record<string, any>, obj2: Record<string, any>): Record<string, any> {
|
||||||
@ -28,5 +30,18 @@ export function genApiTranslationFiles() {
|
|||||||
out._permissions[permission] = 'Untranslated / 未翻訳';
|
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));
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user