Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] develop from misskey-dev:develop #93

Merged
merged 2 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@
- Enhance: リアクションする際に確認ダイアログを表示できるように
- Enhance: コントロールパネルのユーザ検索で入力された情報をページ遷移で損なわないように `#15437`
- Enhance: CWの注釈で入力済みの文字数を表示
- Enhance: ノート検索ページのデザイン調整
(Cherry-picked from https://github.com/taiyme/misskey/pull/273)
- Fix: ノートページで、クリップ一覧が表示されないことがある問題を修正
- Fix: コンディショナルロールを手動で割り当てできる導線を削除 `#13529`
- Fix: 埋め込みプレイヤーから外部ページに移動できない問題を修正
- Fix: Play の再読込時に UI が以前の状態を引き継いでしまう問題を修正 `#14378`
- Fix: カスタム絵文字管理画面(beta)にてisSensitive/localOnlyの絞り込みが上手くいかない問題の修正 ( #15445 )
- Fix: ユーザのサジェスト中に@を入力してもサジェスト結果が消えないように `#14385`
- Fix: CWの注釈が100文字を超えている場合、ノート投稿ボタンを非アクティブに
- Fix: テーマ選択で現在のテーマが初期表示されていない問題を修正
- 翻訳の更新

### Server
Expand Down
30 changes: 30 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11000,6 +11000,36 @@ export interface Locale extends ILocale {
*/
"otherOption3": string;
};
"_search": {
/**
* 全て
*/
"searchScopeAll": string;
/**
* ローカル
*/
"searchScopeLocal": string;
/**
* サーバー指定
*/
"searchScopeServer": string;
/**
* ユーザー指定
*/
"searchScopeUser": string;
/**
* サーバーのホストを入力してください
*/
"pleaseEnterServerHost": string;
/**
* ユーザーを選択してください
*/
"pleaseSelectUser": string;
/**
* 例: misskey.example.com
*/
"serverHostPlaceholder": string;
};
}
declare const locales: {
[lang: string]: Locale;
Expand Down
9 changes: 9 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2942,3 +2942,12 @@ _bootErrors:
otherOption1: "クライアント設定とキャッシュを削除"
otherOption2: "簡易クライアントを起動"
otherOption3: "修復ツールを起動"

_search:
searchScopeAll: "全て"
searchScopeLocal: "ローカル"
searchScopeServer: "サーバー指定"
searchScopeUser: "ユーザー指定"
pleaseEnterServerHost: "サーバーのホストを入力してください"
pleaseSelectUser: "ユーザーを選択してください"
serverHostPlaceholder: "例: misskey.example.com"
130 changes: 100 additions & 30 deletions packages/frontend/src/components/MkSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -56,6 +73,7 @@ const props = defineProps<{
inline?: boolean;
small?: boolean;
large?: boolean;
items?: MkSelectItem[];
}>();

const emit = defineEmits<{
Expand Down Expand Up @@ -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;
Expand All @@ -130,7 +171,7 @@ watch(modelValue, () => {
};

scanOptions(slots.default!());
}, { immediate: true });
}, { immediate: true, deep: true });

function show() {
if (opening.value) return;
Expand All @@ -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);
},
});
};

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);
scanOptions(options);
}

os.popupMenu(menu, container.value, {
width: container.value?.offsetWidth,
Expand Down
Loading
Loading