(add) サーバー一覧をリスト表示できるように

This commit is contained in:
kakkokari-gtyih 2023-12-23 15:56:34 +09:00
parent 265f93a91c
commit 27b66197bb
9 changed files with 144 additions and 46 deletions

View File

@ -27,4 +27,16 @@ $primary: #86b300;
// button // button
@import "bootstrap/scss/buttons"; @import "bootstrap/scss/buttons";
@import "bootstrap/scss/button-group"; @import "bootstrap/scss/button-group";
// Overrides
.btn-outline-primary {
--bs-btn-hover-color: #fff;
--bs-btn-active-color: #fff;
}
.btn-primary {
--bs-btn-color: #fff;
--bs-btn-hover-color: #fff;
--bs-btn-active-color: #fff;
--bs-btn-disabled-color: #fff;
}

View File

@ -1,5 +1,5 @@
<template> <template>
<button class="btn btn-primary inline-block mb-4 !text-white hover:!text-white" @click="click()"> <button class="btn btn-primary inline-block mb-4" @click="click()">
{{ isEnabledAiChanMode ? '藍モードを無効にする' : '藍モードを有効にする' }} {{ isEnabledAiChanMode ? '藍モードを無効にする' : '藍モードを有効にする' }}
</button> </button>
</template> </template>

View File

@ -1,5 +1,5 @@
<template> <template>
<button class="btn btn-outline-primary hover:!text-white focus-visible:!text-white" @click="copy"> <button class="btn btn-outline-primary" @click="copy">
<CopyIco class="w-4 h-4" v-if="!copied" /> <CopyIco class="w-4 h-4" v-if="!copied" />
<CheckIco class="w-4 h-4" v-else /> <CheckIco class="w-4 h-4" v-else />
</button> </button>

View File

@ -58,7 +58,7 @@
<label class="form-label inline-block" for="userDefinedInstanceInput">{{ $t('_share.domain') }}</label> <label class="form-label inline-block" for="userDefinedInstanceInput">{{ $t('_share.domain') }}</label>
<div class="input-group"> <div class="input-group">
<input id="userDefinedInstanceInput" class="form-control" autocomplete="off" placeholder="misskey.example.com" v-model="userDefinedInstanceInput" :disabled="iFetching" /> <input id="userDefinedInstanceInput" class="form-control" autocomplete="off" placeholder="misskey.example.com" v-model="userDefinedInstanceInput" :disabled="iFetching" />
<button type="submit" class="btn btn-primary !text-white hover:!text-white focus-visible:!text-white" :disabled="iFetching"><PlusIco class="h-4 w-4 stroke-1 stroke-current" /></button> <button type="submit" class="btn btn-primary" :disabled="iFetching"><PlusIco class="h-4 w-4 stroke-1 stroke-current" /></button>
</div> </div>
<div class="form-text">{{ $t('_share.compatibleWith') }}</div> <div class="form-text">{{ $t('_share.compatibleWith') }}</div>
</form> </form>

View File

@ -22,7 +22,7 @@
<label class="form-label" for="query">{{ $t('_servers._search.query') }}</label> <label class="form-label" for="query">{{ $t('_servers._search.query') }}</label>
<div class="input-group"> <div class="input-group">
<input class="form-control" type="search" autocomplete="off" id="query" v-model="f_query_partial" /> <input class="form-control" type="search" autocomplete="off" id="query" v-model="f_query_partial" />
<button type="submit" class="btn btn-outline-primary hover:!text-white"> <button type="submit" class="btn btn-outline-primary">
<SearchIco class="stroke-[0.5] stroke-current" /> <SearchIco class="stroke-[0.5] stroke-current" />
</button> </button>
</div> </div>
@ -43,7 +43,7 @@
<option value="notesCount">{{ $t('_servers._search.notesCount') }}</option> <option value="notesCount">{{ $t('_servers._search.notesCount') }}</option>
<option value="usersCount">{{ $t('_servers._search.usersCount') }}</option> <option value="usersCount">{{ $t('_servers._search.usersCount') }}</option>
</select> </select>
<button class="btn btn-outline-primary hover:!text-white" @click="switchOrder()"> <button class="btn btn-outline-primary" @click="switchOrder()">
<SortDownIco v-if="f_order === 'desc'" class="stroke-[0.5] stroke-current" /> <SortDownIco v-if="f_order === 'desc'" class="stroke-[0.5] stroke-current" />
<SortUpIco v-else class="stroke-[0.5] stroke-current" /> <SortUpIco v-else class="stroke-[0.5] stroke-current" />
</button> </button>
@ -70,24 +70,56 @@
</label> </label>
</div> </div>
</div> </div>
<h3 class="pt-2 text-xl font-bold">{{ $t('_servers._view.title') }}</h3>
<div class="btn-group w-full" role="group">
<input type="radio" class="btn-check" name="btnradio" id="btnradio1" autocomplete="off" value="grid" v-model="v_view">
<label class="btn btn-outline-primary truncate" for="btnradio1"><GridIco class="mr-1" />{{ $t('_servers._view.grid') }}</label>
<input type="radio" class="btn-check" name="btnradio" id="btnradio2" autocomplete="off" value="list" v-model="v_view">
<label class="btn btn-outline-primary truncate" for="btnradio2"><ListIco class="mr-1" />{{ $t('_servers._view.list') }}</label>
</div>
</div> </div>
</aside> </aside>
<div> <div>
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2"> <div
<ServersItem v-if="filteredInstances.length > 0" v-for="item in filteredInstances.slice(0, f_limit)" :instance="item" /> class="grid gap-4"
<div v-else-if="data" class="rounded-lg p-6 min-h-[40vh] flex items-center sm:col-span-2 md:col-span-2 lg:col-span-2 bg-slate-100 dark:bg-slate-800"> :class="[
(v_view === 'grid') && 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2',
(v_view === 'list') && 'grid-cols-1',
]"
>
<ServersItem v-if="filteredInstances.length > 0" v-for="item in filteredInstances.slice(0, f_limit)" :instance="item" :view="v_view" />
<div
v-else-if="data"
class="rounded-lg p-6 min-h-[40vh] flex items-center bg-slate-100 dark:bg-slate-800"
:class="[
(v_view === 'grid') && 'sm:col-span-2 md:col-span-2 lg:col-span-2'
]"
>
<div class="mx-auto text-center"> <div class="mx-auto text-center">
<img src="https://xn--931a.moe/assets/info.jpg" class="rounded-lg mx-auto mb-4" /> <img src="https://xn--931a.moe/assets/info.jpg" class="rounded-lg mx-auto mb-4" />
<p class="max-w-xs">{{ $t('_servers._list.notFound') }}</p> <p class="max-w-xs">{{ $t('_servers._list.notFound') }}</p>
</div> </div>
</div> </div>
<div v-else class="rounded-lg p-6 min-h-[40vh] flex items-center sm:col-span-2 md:col-span-2 lg:col-span-2 bg-slate-100 dark:bg-slate-800"> <div
v-else
class="rounded-lg p-6 min-h-[40vh] flex items-center bg-slate-100 dark:bg-slate-800"
:class="[
(v_view === 'grid') && 'sm:col-span-2 md:col-span-2 lg:col-span-2'
]"
>
<div class="mx-auto text-center"> <div class="mx-auto text-center">
<MkLoading class="mx-auto"></MkLoading> <MkLoading class="mx-auto"></MkLoading>
<p class="max-w-xs">{{ $t('loading') }}</p> <p class="max-w-xs">{{ $t('loading') }}</p>
</div> </div>
</div> </div>
<button v-if="f_limit < filteredInstances.length" @click="f_limit += 24" class="btn btn-outline-primary btn-lg hover:!text-white block sm:col-span-2 md:col-span-3 lg:col-span-2 px-4"> <button
v-if="f_limit < filteredInstances.length" @click="f_limit += 24"
class="btn btn-outline-primary btn-lg block px-4"
:class="[
(v_view === 'grid') && 'sm:col-span-2 md:col-span-2 lg:col-span-2'
]"
>
<ArrowIco class="mr-1" />{{ $t('_servers._list.showMore') }} <ArrowIco class="mr-1" />{{ $t('_servers._list.showMore') }}
</button> </button>
</div> </div>
@ -105,6 +137,8 @@ import SortUpIco from 'bi/sort-down-alt.svg';
import SortDownIco from 'bi/sort-down.svg'; import SortDownIco from 'bi/sort-down.svg';
import ArrowIco from 'bi/arrow-down-circle.svg'; import ArrowIco from 'bi/arrow-down-circle.svg';
import XIco from 'bi/x.svg'; import XIco from 'bi/x.svg';
import GridIco from 'bi/grid-3x2-gap.svg';
import ListIco from 'bi/view-stacked.svg';
const { t, locale } = useI18n(); const { t, locale } = useI18n();
const route = useRoute(); const route = useRoute();
@ -114,7 +148,6 @@ const emits = defineEmits<{
// //
const sortOpen = ref(false); const sortOpen = ref(false);
// //
// //
@ -123,6 +156,7 @@ type MiHubSFStorage = {
f_orderBy: 'recomendded' | 'notesCount' | 'notesPer15Days' | 'usersCount'; f_orderBy: 'recomendded' | 'notesCount' | 'notesPer15Days' | 'usersCount';
f_order: 'asc' | 'desc'; f_order: 'asc' | 'desc';
f_registerAcceptance: 'public' | 'inviteOnly' | null; f_registerAcceptance: 'public' | 'inviteOnly' | null;
v_view: 'grid' | 'list';
}; };
let savedSettings: MiHubSFStorage | null = null; let savedSettings: MiHubSFStorage | null = null;
@ -139,10 +173,12 @@ const f_order = ref<MiHubSFStorage['f_order']>(savedSettings?.f_order ?? 'desc')
const f_registerAcceptance = ref<MiHubSFStorage['f_registerAcceptance']>(savedSettings?.f_registerAcceptance || null); const f_registerAcceptance = ref<MiHubSFStorage['f_registerAcceptance']>(savedSettings?.f_registerAcceptance || null);
const f_limit = ref<number>(24); const f_limit = ref<number>(24);
const v_view = ref<MiHubSFStorage['v_view']>(savedSettings?.v_view ?? 'grid');
// //
// //
watch([f_langs, f_orderBy, f_order, f_registerAcceptance], (to, from) => { watch([f_langs, f_orderBy, f_order, f_registerAcceptance, v_view], (to, from) => {
f_limit.value = 24; f_limit.value = 24;
const newSettings: MiHubSFStorage = { const newSettings: MiHubSFStorage = {
@ -150,6 +186,7 @@ watch([f_langs, f_orderBy, f_order, f_registerAcceptance], (to, from) => {
f_orderBy: to[1], f_orderBy: to[1],
f_order: to[2], f_order: to[2],
f_registerAcceptance: to[3], f_registerAcceptance: to[3],
v_view: to[4],
}; };
if (process.client) { if (process.client) {

View File

@ -1,36 +1,78 @@
<template> <template>
<div class="border border-gray-300 dark:border-gray-800 dark:bg-slate-800 rounded-lg shadow-lg overflow-hidden focus-within:ring-2 ring-accent-500 ring-offset-2"> <div class="border border-gray-300 dark:border-gray-800 dark:bg-slate-800 rounded-lg shadow-lg overflow-hidden focus-within:ring-2 ring-accent-500 ring-offset-2">
<GNuxtLink :to="`https://${instance.url}`" target="_blank"> <GNuxtLink :to="`https://${instance.url}`" target="_blank" class="relative">
<div class="relative aspect-video bg-gray-200 dark:bg-gray-600"> <template v-if="view === 'grid'">
<img v-if="instance.banner" :src="`https://instanceapp.misskey.page/instance-banners/${instance.url}.webp`" class="w-full h-full object-cover" /> <div class="relative aspect-video bg-gray-200 dark:bg-gray-600">
<img v-else-if="instance.background" :src="`https://instanceapp.misskey.page/instance-backgrounds/${instance.url}.webp`" class="w-full h-full object-cover" /> <img v-if="instance.banner" loading="lazy" :src="`https://instanceapp.misskey.page/instance-banners/${instance.url}.webp`" class="w-full h-full object-cover" />
<div class="absolute h-1/2 bottom-0 left-0 w-full bg-gradient-to-b from-transparent to-black text-white p-4 flex items-end"> <img v-else-if="instance.background" loading="lazy" :src="`https://instanceapp.misskey.page/instance-backgrounds/${instance.url}.webp`" class="w-full h-full object-cover" />
<div class="absolute h-1/2 bottom-0 left-0 w-full bg-gradient-to-b from-transparent to-black text-white p-4 flex items-end">
<div class="h-14 w-14 min-w-0 flex-shrink-0 mr-4">
<img v-if="instance.icon" :src="`https://instanceapp.misskey.page/instance-icons/${instance.url}.webp`" class="w-full h-full rounded" />
</div>
<div class="min-w-0 flex flex-col justify-end">
<h2 class="font-bold text-2xl whitespace-nowrap truncate">{{ instance.name }}</h2>
<p class="opacity-90 text-sm truncate">{{ instance.url }} / v.{{ instance.nodeinfo?.software.version }}</p>
</div>
</div>
</div>
<div class="p-4">
<p class="description h-12 mb-2">{{ instance.description }}</p>
<div class="grid grid-cols-3 text-center">
<dl>
<dt class="text-xs opacity-90">{{ $t('_servers._statistics.notes') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.stats?.originalNotesCount.toLocaleString() }}</dd>
</dl>
<dl>
<dt class="text-xs opacity-90">{{ $t('_servers._statistics.users') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.stats?.originalUsersCount.toLocaleString() }}</dd>
</dl>
<dl>
<dt class="text-xs opacity-90">{{ $t('_servers._registerAcceptance.title') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.meta?.disableRegistration ? $t('_servers._registerAcceptance.inviteOnly') : $t('_servers._registerAcceptance.public')}}</dd>
</dl>
</div>
</div>
</template>
<template v-else-if="view === 'list'">
<div class="absolute h-full w-4/5 top-0 left-0 overflow-hidden">
<img v-if="instance.banner" loading="lazy" :src="`https://instanceapp.misskey.page/instance-banners/${instance.url}.webp`" class="h-full w-full object-cover object-center opacity-40 blur-md" />
<img v-else-if="instance.background" loading="lazy" :src="`https://instanceapp.misskey.page/instance-backgrounds/${instance.url}.webp`" class="h-full w-full object-cover object-center opacity-40 blur-md" />
<div class="absolute top-0 left-0 h-full w-full bg-gradient-to-r from-transparent to-white dark:to-slate-800"></div>
</div>
<div class="relative flex w-full items-center p-2">
<div class="h-14 w-14 min-w-0 flex-shrink-0 mr-4"> <div class="h-14 w-14 min-w-0 flex-shrink-0 mr-4">
<img v-if="instance.icon" :src="`https://instanceapp.misskey.page/instance-icons/${instance.url}.webp`" class="w-full h-full" /> <img v-if="instance.icon" :src="`https://instanceapp.misskey.page/instance-icons/${instance.url}.webp`" class="w-full h-full rounded" />
</div> </div>
<div class="min-w-0 flex flex-col justify-end"> <div class="truncate">
<h2 class="font-bold text-2xl whitespace-nowrap truncate">{{ instance.name }}</h2> <h2 class="font-bold text-xl whitespace-nowrap truncate">{{ instance.name }}</h2>
<p class="opacity-90 text-sm truncate">{{ instance.url }} / v.{{ instance.nodeinfo?.software.version }}</p> <p class="opacity-90 hidden sm:block text-sm truncate">{{ instance.url }} / v.{{ instance.nodeinfo?.software.version }}</p>
<p class="text-sm flex sm:hidden space-x-2">
<dl class="flex space-x-1">
<dt class="opacity-90">{{ $t('_servers._statistics.users') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.stats?.originalUsersCount.toLocaleString() }}</dd>
</dl>
<dl class="flex space-x-1">
<dt class="opacity-90">{{ $t('_servers._registerAcceptance.title') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.meta?.disableRegistration ? $t('_servers._registerAcceptance.inviteOnly') : $t('_servers._registerAcceptance.public')}}</dd>
</dl>
</p>
</div>
<div class="ml-auto flex-shrink-0 hidden min-w-[17rem] sm:grid grid-cols-3 text-center">
<dl>
<dt class="text-xs opacity-90">{{ $t('_servers._statistics.notes') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.stats?.originalNotesCount.toLocaleString() }}</dd>
</dl>
<dl>
<dt class="text-xs opacity-90">{{ $t('_servers._statistics.users') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.stats?.originalUsersCount.toLocaleString() }}</dd>
</dl>
<dl>
<dt class="text-xs opacity-90">{{ $t('_servers._registerAcceptance.title') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.meta?.disableRegistration ? $t('_servers._registerAcceptance.inviteOnly') : $t('_servers._registerAcceptance.public')}}</dd>
</dl>
</div> </div>
</div> </div>
</div> </template>
<div class="p-4">
<p class="description h-12 mb-2">{{ instance.description }}</p>
<div class="grid grid-cols-3 text-center">
<dl>
<dt class="text-xs opacity-90">{{ $t('_servers._statistics.notes') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.stats?.originalNotesCount.toLocaleString() }}</dd>
</dl>
<dl>
<dt class="text-xs opacity-90">{{ $t('_servers._statistics.users') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.stats?.originalUsersCount.toLocaleString() }}</dd>
</dl>
<dl>
<dt class="text-xs opacity-90">{{ $t('_servers._registerAcceptance.title') }}</dt>
<dd class="font-bold text-accent-600">{{ instance.meta?.disableRegistration ? $t('_servers._registerAcceptance.inviteOnly') : $t('_servers._registerAcceptance.public')}}</dd>
</dl>
</div>
</div>
</GNuxtLink> </GNuxtLink>
</div> </div>
</template> </template>
@ -38,9 +80,12 @@
<script setup lang="ts"> <script setup lang="ts">
import type { InstanceItem } from '@/types/instances-info'; import type { InstanceItem } from '@/types/instances-info';
defineProps<{ withDefaults(defineProps<{
instance: InstanceItem; instance: InstanceItem;
}>() view?: 'grid' | 'list';
}>(), {
view: 'grid',
});
</script> </script>
<style scoped> <style scoped>

View File

@ -130,6 +130,10 @@ _servers:
_list: _list:
notFound: "指定された条件に合致するサーバーは見つかりませんでした。" notFound: "指定された条件に合致するサーバーは見つかりませんでした。"
showMore: "もっと見る" showMore: "もっと見る"
_view:
title: "表示形式"
list: "リスト"
grid: "グリッド"
_docs: _docs:
title: "ドキュメント" title: "ドキュメント"

View File

@ -11,7 +11,7 @@
<label class="mb-1" for="aidToDateAid">aid / aidx</label> <label class="mb-1" for="aidToDateAid">aid / aidx</label>
<input class="form-control" id="aidToDateAid" v-model="aidToDateAid" /> <input class="form-control" id="aidToDateAid" v-model="aidToDateAid" />
<div class="my-2"> <div class="my-2">
<button class="btn btn-primary !text-white" @click="doAidToDate()">{{ $t('_aidConverter.aidToDate') }}</button> <button class="btn btn-primary" @click="doAidToDate()">{{ $t('_aidConverter.aidToDate') }}</button>
</div> </div>
<div class="mb-2 p-4 rounded-lg border bg-white dark:bg-[#212529] border-gray-200 dark:border-gray-600"> <div class="mb-2 p-4 rounded-lg border bg-white dark:bg-[#212529] border-gray-200 dark:border-gray-600">
{{ aidToDateResult }} {{ aidToDateResult }}
@ -26,7 +26,7 @@
<option value="aidx">aidx</option> <option value="aidx">aidx</option>
</select> </select>
<div class="my-2"> <div class="my-2">
<button class="btn btn-primary !text-white" @click="doDateToAid()">{{ $t('_aidConverter.dateToAid') }}</button> <button class="btn btn-primary" @click="doDateToAid()">{{ $t('_aidConverter.dateToAid') }}</button>
</div> </div>
<div class="mb-2 p-4 rounded-lg border bg-white dark:bg-[#212529] border-gray-200 dark:border-gray-600"> <div class="mb-2 p-4 rounded-lg border bg-white dark:bg-[#212529] border-gray-200 dark:border-gray-600">
{{ dateToAidResult }} {{ dateToAidResult }}

View File

@ -33,7 +33,7 @@
<label for="mfmPlaygroundDomain">{{ $t('_mfmPlayground.domain') }}</label> <label for="mfmPlaygroundDomain">{{ $t('_mfmPlayground.domain') }}</label>
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" id="mfmPlaygroundDomain" v-model="mfmHost" /> <input type="text" class="form-control" id="mfmPlaygroundDomain" v-model="mfmHost" />
<GNuxtLink :to="shareURL" target="_blank" class="btn btn-primary !text-white">{{ $t('_mfmPlayground.noteIt') }}<SendIco class="ml-1" /></GNuxtLink> <GNuxtLink :to="shareURL" target="_blank" class="btn btn-primary">{{ $t('_mfmPlayground.noteIt') }}<SendIco class="ml-1" /></GNuxtLink>
</div> </div>
</div> </div>
<div> <div>
@ -42,7 +42,7 @@
{{ $t('_mfmPlayground.clearEmojiCacheDescription') }} {{ $t('_mfmPlayground.clearEmojiCacheDescription') }}
</div> </div>
<div class="w-1/2"> <div class="w-1/2">
<button @click="clearEmojiCache()" class="btn w-full btn-outline-primary hover:!text-white">{{ $t('_mfmPlayground.clearEmojiCache') }}</button> <button @click="clearEmojiCache()" class="btn w-full btn-outline-primary">{{ $t('_mfmPlayground.clearEmojiCache') }}</button>
</div> </div>
</div> </div>
</div> </div>