fix(frontend): MkSelectの初期値が表示されない場合がある (#15559)
* fix * Update CHANGELOG.md
This commit is contained in:
@@ -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,
|
||||
|
Reference in New Issue
Block a user