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: ユーザのサジェスト中に@を入力してもサジェスト結果が消えないように `#14385`
|
||||
- Fix: CWの注釈が100文字を超えている場合、ノート投稿ボタンを非アクティブに
|
||||
- Fix: テーマ選択で現在のテーマが初期表示されていない問題を修正
|
||||
- 翻訳の更新
|
||||
|
||||
### Server
|
||||
|
@ -41,11 +41,28 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, ref, watch, computed, toRefs, useSlots } from 'vue';
|
||||
import type { VNode, VNodeChild } from 'vue';
|
||||
import { useInterval } from '@@/js/use-interval.js';
|
||||
import type { VNode, VNodeChild } from 'vue';
|
||||
import type { MenuItem } from '@/types/menu.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<{
|
||||
modelValue: string | number | null;
|
||||
required?: boolean;
|
||||
@ -56,6 +73,7 @@ const props = defineProps<{
|
||||
inline?: boolean;
|
||||
small?: boolean;
|
||||
large?: boolean;
|
||||
items?: MkSelectItem[];
|
||||
}>();
|
||||
|
||||
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[]) => {
|
||||
for (const vnode of options) {
|
||||
if (typeof vnode !== 'object' || vnode === null || Array.isArray(vnode)) continue;
|
||||
@ -130,7 +171,7 @@ watch(modelValue, () => {
|
||||
};
|
||||
|
||||
scanOptions(slots.default!());
|
||||
}, { immediate: true });
|
||||
}, { immediate: true, deep: true });
|
||||
|
||||
function show() {
|
||||
if (opening.value) return;
|
||||
@ -139,41 +180,70 @@ function show() {
|
||||
opening.value = true;
|
||||
|
||||
const menu: MenuItem[] = [];
|
||||
let options = slots.default!();
|
||||
|
||||
const pushOption = (option: VNode) => {
|
||||
menu.push({
|
||||
text: option.children as string,
|
||||
active: computed(() => modelValue.value === option.props?.value),
|
||||
action: () => {
|
||||
emit('update:modelValue', option.props?.value);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const scanOptions = (options: VNodeChild[]) => {
|
||||
for (const vnode of options) {
|
||||
if (typeof vnode !== 'object' || vnode === null || Array.isArray(vnode)) continue;
|
||||
if (vnode.type === 'optgroup') {
|
||||
const optgroup = vnode;
|
||||
if (props.items) {
|
||||
for (const item of props.items) {
|
||||
if (item.type === 'group') {
|
||||
menu.push({
|
||||
type: 'label',
|
||||
text: optgroup.props?.label,
|
||||
text: item.label,
|
||||
});
|
||||
if (Array.isArray(optgroup.children)) scanOptions(optgroup.children);
|
||||
} else if (Array.isArray(vnode.children)) { // 何故かフラグメントになってくることがある
|
||||
const fragment = vnode;
|
||||
if (Array.isArray(fragment.children)) scanOptions(fragment.children);
|
||||
} else if (vnode.props == null) { // v-if で条件が false のときにこうなる
|
||||
// nop?
|
||||
for (const option of item.items) {
|
||||
menu.push({
|
||||
text: option.label,
|
||||
active: computed(() => modelValue.value === option.value),
|
||||
action: () => {
|
||||
emit('update:modelValue', option.value);
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const option = vnode;
|
||||
pushOption(option);
|
||||
menu.push({
|
||||
text: item.label,
|
||||
active: computed(() => modelValue.value === item.value),
|
||||
action: () => {
|
||||
emit('update:modelValue', item.value);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
let options = slots.default!();
|
||||
|
||||
scanOptions(options);
|
||||
const pushOption = (option: VNode) => {
|
||||
menu.push({
|
||||
text: option.children as string,
|
||||
active: computed(() => modelValue.value === option.props?.value),
|
||||
action: () => {
|
||||
emit('update:modelValue', option.props?.value);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const scanOptions = (options: VNodeChild[]) => {
|
||||
for (const vnode of options) {
|
||||
if (typeof vnode !== 'object' || vnode === null || Array.isArray(vnode)) continue;
|
||||
if (vnode.type === 'optgroup') {
|
||||
const optgroup = vnode;
|
||||
menu.push({
|
||||
type: 'label',
|
||||
text: optgroup.props?.label,
|
||||
});
|
||||
if (Array.isArray(optgroup.children)) scanOptions(optgroup.children);
|
||||
} else if (Array.isArray(vnode.children)) { // 何故かフラグメントになってくることがある
|
||||
const fragment = vnode;
|
||||
if (Array.isArray(fragment.children)) scanOptions(fragment.children);
|
||||
} else if (vnode.props == null) { // v-if で条件が false のときにこうなる
|
||||
// nop?
|
||||
} else {
|
||||
const option = vnode;
|
||||
pushOption(option);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scanOptions(options);
|
||||
}
|
||||
|
||||
os.popupMenu(menu, container.value, {
|
||||
width: container.value?.offsetWidth,
|
||||
|
@ -32,27 +32,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
|
||||
<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 #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 v-model="darkThemeId" large class="select">
|
||||
<MkSelect v-model="darkThemeId" large class="select" :items="darkThemeSelectorItems">
|
||||
<template #label>{{ i18n.ts.themeForDarkMode }}</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>
|
||||
</div>
|
||||
|
||||
@ -73,6 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
import { computed, onActivated, ref, watch } from 'vue';
|
||||
import JSON5 from 'json5';
|
||||
import type { MkSelectItem } from '@/components/MkSelect.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkSelect from '@/components/MkSelect.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 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 darkThemeId = computed({
|
||||
get() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user