merge upstream

This commit is contained in:
Hazelnoot 2025-03-25 16:14:53 -04:00
commit d8908ef2d8
1065 changed files with 32953 additions and 20092 deletions

View file

@ -40,11 +40,29 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { onMounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots, VNodeChild } from 'vue';
import { onMounted, nextTick, ref, watch, computed, toRefs, useSlots } 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: itemsslotoption(props.items)
// see: https://github.com/misskey-dev/misskey/issues/15558
const props = defineProps<{
modelValue: string | number | null;
required?: boolean;
@ -55,6 +73,7 @@ const props = defineProps<{
inline?: boolean;
small?: boolean;
large?: boolean;
items?: MkSelectItem[];
}>();
const emit = defineEmits<{
@ -106,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;
@ -129,7 +171,7 @@ watch(modelValue, () => {
};
scanOptions(slots.default!());
}, { immediate: true });
}, { immediate: true, deep: true });
function show() {
if (opening.value) return;
@ -138,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,