fix(frontend): MkSelectの初期値が表示されない場合がある (#15559)
* fix * Update CHANGELOG.md
This commit is contained in:
parent
6199139307
commit
ec83815227
@ -22,6 +22,7 @@
|
|||||||
- Fix: カスタム絵文字管理画面(beta)にてisSensitive/localOnlyの絞り込みが上手くいかない問題の修正 ( #15445 )
|
- Fix: カスタム絵文字管理画面(beta)にてisSensitive/localOnlyの絞り込みが上手くいかない問題の修正 ( #15445 )
|
||||||
- Fix: ユーザのサジェスト中に@を入力してもサジェスト結果が消えないように `#14385`
|
- Fix: ユーザのサジェスト中に@を入力してもサジェスト結果が消えないように `#14385`
|
||||||
- Fix: CWの注釈が100文字を超えている場合、ノート投稿ボタンを非アクティブに
|
- Fix: CWの注釈が100文字を超えている場合、ノート投稿ボタンを非アクティブに
|
||||||
|
- Fix: テーマ選択で現在のテーマが初期表示されていない問題を修正
|
||||||
- 翻訳の更新
|
- 翻訳の更新
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
@ -41,11 +41,28 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, nextTick, ref, watch, computed, toRefs, useSlots } from 'vue';
|
import { onMounted, nextTick, ref, watch, computed, toRefs, useSlots } from 'vue';
|
||||||
import type { VNode, VNodeChild } from 'vue';
|
|
||||||
import { useInterval } from '@@/js/use-interval.js';
|
import { useInterval } from '@@/js/use-interval.js';
|
||||||
|
import type { VNode, VNodeChild } from 'vue';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
|
||||||
|
type ItemOption = {
|
||||||
|
type?: 'option';
|
||||||
|
value: string | number | null;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ItemGroup = {
|
||||||
|
type: 'group';
|
||||||
|
label: string;
|
||||||
|
items: ItemOption[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MkSelectItem = ItemOption | ItemGroup;
|
||||||
|
|
||||||
|
// TODO: itemsをslot内のoptionで指定する用法は廃止する(props.itemsを必須化する)
|
||||||
|
// see: https://github.com/misskey-dev/misskey/issues/15558
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: string | number | null;
|
modelValue: string | number | null;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
@ -56,6 +73,7 @@ const props = defineProps<{
|
|||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
small?: boolean;
|
small?: boolean;
|
||||||
large?: boolean;
|
large?: boolean;
|
||||||
|
items?: MkSelectItem[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@ -107,7 +125,30 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(modelValue, () => {
|
watch([modelValue, () => props.items], () => {
|
||||||
|
if (props.items) {
|
||||||
|
let found: ItemOption | null = null;
|
||||||
|
for (const item of props.items) {
|
||||||
|
if (item.type === 'group') {
|
||||||
|
for (const option of item.items) {
|
||||||
|
if (option.value === modelValue.value) {
|
||||||
|
found = option;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (item.value === modelValue.value) {
|
||||||
|
found = item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
currentValueText.value = found.label;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const scanOptions = (options: VNodeChild[]) => {
|
const scanOptions = (options: VNodeChild[]) => {
|
||||||
for (const vnode of options) {
|
for (const vnode of options) {
|
||||||
if (typeof vnode !== 'object' || vnode === null || Array.isArray(vnode)) continue;
|
if (typeof vnode !== 'object' || vnode === null || Array.isArray(vnode)) continue;
|
||||||
@ -130,7 +171,7 @@ watch(modelValue, () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
scanOptions(slots.default!());
|
scanOptions(slots.default!());
|
||||||
}, { immediate: true });
|
}, { immediate: true, deep: true });
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
if (opening.value) return;
|
if (opening.value) return;
|
||||||
@ -139,6 +180,34 @@ function show() {
|
|||||||
opening.value = true;
|
opening.value = true;
|
||||||
|
|
||||||
const menu: MenuItem[] = [];
|
const menu: MenuItem[] = [];
|
||||||
|
|
||||||
|
if (props.items) {
|
||||||
|
for (const item of props.items) {
|
||||||
|
if (item.type === 'group') {
|
||||||
|
menu.push({
|
||||||
|
type: 'label',
|
||||||
|
text: item.label,
|
||||||
|
});
|
||||||
|
for (const option of item.items) {
|
||||||
|
menu.push({
|
||||||
|
text: option.label,
|
||||||
|
active: computed(() => modelValue.value === option.value),
|
||||||
|
action: () => {
|
||||||
|
emit('update:modelValue', option.value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
menu.push({
|
||||||
|
text: item.label,
|
||||||
|
active: computed(() => modelValue.value === item.value),
|
||||||
|
action: () => {
|
||||||
|
emit('update:modelValue', item.value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let options = slots.default!();
|
let options = slots.default!();
|
||||||
|
|
||||||
const pushOption = (option: VNode) => {
|
const pushOption = (option: VNode) => {
|
||||||
@ -174,6 +243,7 @@ function show() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
scanOptions(options);
|
scanOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
os.popupMenu(menu, container.value, {
|
os.popupMenu(menu, container.value, {
|
||||||
width: container.value?.offsetWidth,
|
width: container.value?.offsetWidth,
|
||||||
|
@ -32,27 +32,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="selects">
|
<div class="selects">
|
||||||
<MkSelect v-model="lightThemeId" large class="select">
|
<MkSelect v-model="lightThemeId" large class="select" :items="lightThemeSelectorItems">
|
||||||
<template #label>{{ i18n.ts.themeForLightMode }}</template>
|
<template #label>{{ i18n.ts.themeForLightMode }}</template>
|
||||||
<template #prefix><i class="ti ti-sun"></i></template>
|
<template #prefix><i class="ti ti-sun"></i></template>
|
||||||
<option v-if="instanceLightTheme" :key="'instance:' + instanceLightTheme.id" :value="instanceLightTheme.id">{{ instanceLightTheme.name }}</option>
|
|
||||||
<optgroup v-if="installedLightThemes.length > 0" :label="i18n.ts._theme.installedThemes">
|
|
||||||
<option v-for="x in installedLightThemes" :key="'installed:' + x.id" :value="x.id">{{ x.name }}</option>
|
|
||||||
</optgroup>
|
|
||||||
<optgroup :label="i18n.ts._theme.builtinThemes">
|
|
||||||
<option v-for="x in builtinLightThemes" :key="'builtin:' + x.id" :value="x.id">{{ x.name }}</option>
|
|
||||||
</optgroup>
|
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
<MkSelect v-model="darkThemeId" large class="select">
|
<MkSelect v-model="darkThemeId" large class="select" :items="darkThemeSelectorItems">
|
||||||
<template #label>{{ i18n.ts.themeForDarkMode }}</template>
|
<template #label>{{ i18n.ts.themeForDarkMode }}</template>
|
||||||
<template #prefix><i class="ti ti-moon"></i></template>
|
<template #prefix><i class="ti ti-moon"></i></template>
|
||||||
<option v-if="instanceDarkTheme" :key="'instance:' + instanceDarkTheme.id" :value="instanceDarkTheme.id">{{ instanceDarkTheme.name }}</option>
|
|
||||||
<optgroup v-if="installedDarkThemes.length > 0" :label="i18n.ts._theme.installedThemes">
|
|
||||||
<option v-for="x in installedDarkThemes" :key="'installed:' + x.id" :value="x.id">{{ x.name }}</option>
|
|
||||||
</optgroup>
|
|
||||||
<optgroup :label="i18n.ts._theme.builtinThemes">
|
|
||||||
<option v-for="x in builtinDarkThemes" :key="'builtin:' + x.id" :value="x.id">{{ x.name }}</option>
|
|
||||||
</optgroup>
|
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -73,6 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onActivated, ref, watch } from 'vue';
|
import { computed, onActivated, ref, watch } from 'vue';
|
||||||
import JSON5 from 'json5';
|
import JSON5 from 'json5';
|
||||||
|
import type { MkSelectItem } from '@/components/MkSelect.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
@ -102,6 +89,70 @@ const installedLightThemes = computed(() => installedThemes.value.filter(t => t.
|
|||||||
const builtinLightThemes = computed(() => builtinThemes.value.filter(t => t.base === 'light' || t.kind === 'light'));
|
const builtinLightThemes = computed(() => builtinThemes.value.filter(t => t.base === 'light' || t.kind === 'light'));
|
||||||
const themes = computed(() => uniqueBy([instanceDarkTheme.value, instanceLightTheme.value, ...builtinThemes.value, ...installedThemes.value].filter(x => x != null), theme => theme.id));
|
const themes = computed(() => uniqueBy([instanceDarkTheme.value, instanceLightTheme.value, ...builtinThemes.value, ...installedThemes.value].filter(x => x != null), theme => theme.id));
|
||||||
|
|
||||||
|
const lightThemeSelectorItems = computed(() => {
|
||||||
|
const items = [] as MkSelectItem[];
|
||||||
|
if (instanceLightTheme.value) {
|
||||||
|
items.push({
|
||||||
|
type: 'option',
|
||||||
|
value: instanceLightTheme.value.id,
|
||||||
|
label: instanceLightTheme.value.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (installedLightThemes.value.length > 0) {
|
||||||
|
items.push({
|
||||||
|
type: 'group',
|
||||||
|
label: i18n.ts._theme.installedThemes,
|
||||||
|
items: installedLightThemes.value.map(x => ({
|
||||||
|
type: 'option',
|
||||||
|
value: x.id,
|
||||||
|
label: x.name,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
items.push({
|
||||||
|
type: 'group',
|
||||||
|
label: i18n.ts._theme.builtinThemes,
|
||||||
|
items: builtinLightThemes.value.map(x => ({
|
||||||
|
type: 'option',
|
||||||
|
value: x.id,
|
||||||
|
label: x.name,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
return items;
|
||||||
|
});
|
||||||
|
|
||||||
|
const darkThemeSelectorItems = computed(() => {
|
||||||
|
const items = [] as MkSelectItem[];
|
||||||
|
if (instanceDarkTheme.value) {
|
||||||
|
items.push({
|
||||||
|
type: 'option',
|
||||||
|
value: instanceDarkTheme.value.id,
|
||||||
|
label: instanceDarkTheme.value.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (installedDarkThemes.value.length > 0) {
|
||||||
|
items.push({
|
||||||
|
type: 'group',
|
||||||
|
label: i18n.ts._theme.installedThemes,
|
||||||
|
items: installedDarkThemes.value.map(x => ({
|
||||||
|
type: 'option',
|
||||||
|
value: x.id,
|
||||||
|
label: x.name,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
items.push({
|
||||||
|
type: 'group',
|
||||||
|
label: i18n.ts._theme.builtinThemes,
|
||||||
|
items: builtinDarkThemes.value.map(x => ({
|
||||||
|
type: 'option',
|
||||||
|
value: x.id,
|
||||||
|
label: x.name,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
return items;
|
||||||
|
});
|
||||||
|
|
||||||
const darkTheme = ColdDeviceStorage.ref('darkTheme');
|
const darkTheme = ColdDeviceStorage.ref('darkTheme');
|
||||||
const darkThemeId = computed({
|
const darkThemeId = computed({
|
||||||
get() {
|
get() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user