diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-02-27 09:32:39 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-27 00:32:39 +0000 |
| commit | ec8381522789a7fc4329a627770d80a0cd7774a4 (patch) | |
| tree | a4bf4db4bae51fa4020def0fea19b08154eaf563 /packages/frontend/src/components/MkSelect.vue | |
| parent | Update CHANGELOG.md (diff) | |
| download | sharkey-ec8381522789a7fc4329a627770d80a0cd7774a4.tar.gz sharkey-ec8381522789a7fc4329a627770d80a0cd7774a4.tar.bz2 sharkey-ec8381522789a7fc4329a627770d80a0cd7774a4.zip | |
fix(frontend): MkSelectの初期値が表示されない場合がある (#15559)
* fix
* Update CHANGELOG.md
Diffstat (limited to 'packages/frontend/src/components/MkSelect.vue')
| -rw-r--r-- | packages/frontend/src/components/MkSelect.vue | 130 |
1 files changed, 100 insertions, 30 deletions
diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue index 83a79d0c9f..15717802ec 100644 --- a/packages/frontend/src/components/MkSelect.vue +++ b/packages/frontend/src/components/MkSelect.vue @@ -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!(); + + 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); + }, + }); + }; - scanOptions(options); + 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, |