This commit is contained in:
syuilo 2025-02-16 12:50:31 +09:00
parent 05d41f2a7a
commit a37a945f95
4 changed files with 171 additions and 26 deletions

View File

@ -0,0 +1,38 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="[$style.root, { [$style.highlighted]: highlighted }]">
<slot></slot>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref, shallowRef } from 'vue';
const props = defineProps<{
markerId: string;
}>();
const highlighted = window.location.hash.slice(1) === props.markerId;
</script>
<style lang="scss" module>
.root {
}
.highlighted {
animation: blink 1s infinite;
}
@keyframes blink {
0%, 100% {
box-shadow: 0 0 0 2px color(from var(--MI_THEME-accent) srgb r g b / 0.3);
}
50% {
box-shadow: 0 0 0 2px transparent;
}
}
</style>

View File

@ -5,26 +5,40 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="rrevdjwu" :class="{ grid }">
<div v-for="group in def" class="group">
<div v-if="group.title" class="title">{{ group.title }}</div>
<MkInput v-model="search" :placeholder="i18n.ts.search" type="search" style="margin-bottom: 16px;" @keydown.enter="searchSubmit">
<template #prefix><i class="ti ti-search"></i></template>
</MkInput>
<div class="items">
<template v-for="(item, i) in group.items">
<a v-if="item.type === 'a'" :href="item.href" :target="item.target" class="_button item" :class="{ danger: item.danger, active: item.active }">
<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
<span class="text">{{ item.text }}</span>
</a>
<button v-else-if="item.type === 'button'" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active" @click="ev => item.action(ev)">
<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
<span class="text">{{ item.text }}</span>
</button>
<MkA v-else :to="item.to" class="_button item" :class="{ danger: item.danger, active: item.active }">
<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
<span class="text">{{ item.text }}</span>
</MkA>
</template>
<template v-if="search == ''">
<div v-for="group in def" class="group">
<div v-if="group.title" class="title">{{ group.title }}</div>
<div class="items">
<template v-for="(item, i) in group.items">
<a v-if="item.type === 'a'" :href="item.href" :target="item.target" class="_button item" :class="{ danger: item.danger, active: item.active }">
<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
<span class="text">{{ item.text }}</span>
</a>
<button v-else-if="item.type === 'button'" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active" @click="ev => item.action(ev)">
<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
<span class="text">{{ item.text }}</span>
</button>
<MkA v-else :to="item.to" class="_button item" :class="{ danger: item.danger, active: item.active }">
<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
<span class="text">{{ item.text }}</span>
</MkA>
</template>
</div>
</div>
</div>
</template>
<template v-else>
<div v-for="item in searchResult">
<MkA :to="item.path + '#' + item.id" class="_button searchResultItem">
<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
<span class="text">{{ item.locationLabel.join(' > ') }}</span>
</MkA>
</div>
</template>
</div>
</template>
@ -58,10 +72,52 @@ export type SuperMenuDef = {
</script>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import MkInput from '@/components/MkInput.vue';
import { i18n } from '@/i18n.js';
defineProps<{
def: SuperMenuDef[];
grid?: boolean;
}>();
const search = ref('');
const searchResult = ref<any[]>([]);
const INDEX = [{
id: '727cc9e8-ad67-474a-9241-b5a9a6475e47',
locationLabel: [i18n.ts.profile, i18n.ts._profile.name],
icon: 'ti ti-user',
keywords: ['name'],
path: '/settings/profile',
}, {
id: '1a06c7f9-e85e-46cb-bf5f-b3efa8e71b93',
locationLabel: [i18n.ts.profile, i18n.ts._profile.description],
icon: 'ti ti-user',
keywords: ['bio'],
path: '/settings/profile',
}, {
id: 'acbfe8cb-c3c9-4d90-8c62-713025814b2e',
locationLabel: [i18n.ts.privacy, i18n.ts.makeFollowManuallyApprove],
icon: 'ti ti-lock-open',
keywords: ['follow', 'lock', i18n.ts.lockedAccountInfo],
path: '/settings/privacy',
}];
watch(search, (value) => {
if (value === '') {
searchResult.value = [];
return;
}
searchResult.value = INDEX.filter((item) => {
// TODO:
return item.locationLabel.some((x) => x.toLowerCase().includes(value.toLowerCase())) || item.keywords.some((x) => x.toLowerCase().includes(value.toLowerCase()));
});
});
function searchSubmit() {
}
</script>
<style lang="scss" scoped>
@ -184,5 +240,47 @@ defineProps<{
}
}
}
.searchResultItem {
display: flex;
align-items: center;
width: 100%;
box-sizing: border-box;
padding: 9px 16px 9px 8px;
border-radius: 9px;
font-size: 0.9em;
&:hover {
text-decoration: none;
background: var(--MI_THEME-panelHighlight);
}
&:focus-visible {
outline-offset: -2px;
}
&.active {
color: var(--MI_THEME-accent);
background: var(--MI_THEME-accentedBg);
}
&.danger {
color: var(--MI_THEME-error);
}
> .icon {
width: 32px;
margin-right: 2px;
flex-shrink: 0;
text-align: center;
opacity: 0.8;
}
> .text {
white-space: normal;
padding-right: 12px;
flex-shrink: 1;
}
}
}
</style>

View File

@ -5,7 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps_m">
<MkSwitch v-model="isLocked" @update:modelValue="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></MkSwitch>
<MkSearchMarker markerId="acbfe8cb-c3c9-4d90-8c62-713025814b2e">
<MkSwitch v-model="isLocked" @update:modelValue="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></MkSwitch>
</MkSearchMarker>
<MkSwitch v-if="isLocked" v-model="autoAcceptFollowed" @update:modelValue="save()">{{ i18n.ts.autoAcceptFollowed }}</MkSwitch>
<MkSwitch v-model="publicReactions" @update:modelValue="save()">
@ -174,6 +177,7 @@ import FormSlot from '@/components/form/slot.vue';
import { formatDateTimeString } from '@/scripts/format-time-string.js';
import MkInput from '@/components/MkInput.vue';
import * as os from '@/os.js';
import MkSearchMarker from '@/components/MkSearchMarker.vue';
const $i = signinRequired();

View File

@ -18,14 +18,18 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
<MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']">
<template #label>{{ i18n.ts._profile.name }}</template>
</MkInput>
<MkSearchMarker markerId="727cc9e8-ad67-474a-9241-b5a9a6475e47">
<MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']">
<template #label>{{ i18n.ts._profile.name }}</template>
</MkInput>
</MkSearchMarker>
<MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true">
<template #label>{{ i18n.ts._profile.description }}</template>
<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
</MkTextarea>
<MkSearchMarker markerId="1a06c7f9-e85e-46cb-bf5f-b3efa8e71b93">
<MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true">
<template #label>{{ i18n.ts._profile.description }}</template>
<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
</MkTextarea>
</MkSearchMarker>
<MkInput v-model="profile.location" manualSave>
<template #label>{{ i18n.ts.location }}</template>
@ -121,6 +125,7 @@ import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkSearchMarker from '@/components/MkSearchMarker.vue';
import FormSplit from '@/components/form/split.vue';
import MkFolder from '@/components/MkFolder.vue';
import FormSlot from '@/components/form/slot.vue';