From c4f1ca2fd9cb1c9b6035f4efb3fa9e46f7be9d64 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Mon, 21 Oct 2024 19:14:02 +0900
Subject: fix(frontend):
 MkSelectでmodelValueが更新されない限り値を更新しないように
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkSelect.vue | 80 +++++++++++++--------------
 1 file changed, 38 insertions(+), 42 deletions(-)

(limited to 'packages/frontend/src/components/MkSelect.vue')

diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue
index a2ec384ac5..8bd02000e6 100644
--- a/packages/frontend/src/components/MkSelect.vue
+++ b/packages/frontend/src/components/MkSelect.vue
@@ -16,9 +16,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 		@keydown.space.enter="show"
 	>
 		<div ref="prefixEl" :class="$style.prefix"><slot name="prefix"></slot></div>
-		<select
+		<div
 			ref="inputEl"
-			v-model="v"
 			v-adaptive-border
 			tabindex="-1"
 			:class="$style.inputCore"
@@ -26,27 +25,25 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:required="required"
 			:readonly="readonly"
 			:placeholder="placeholder"
-			@input="onInput"
 			@mousedown.prevent="() => {}"
 			@keydown.prevent="() => {}"
 		>
-			<slot></slot>
-		</select>
+			<div style="pointer-events: none;">{{ currentValueText ?? '' }}</div>
+			<div style="display: none;">
+				<slot></slot>
+			</div>
+		</div>
 		<div ref="suffixEl" :class="$style.suffix"><i class="ti ti-chevron-down" :class="[$style.chevron, { [$style.chevronOpening]: opening }]"></i></div>
 	</div>
 	<div :class="$style.caption"><slot name="caption"></slot></div>
-
-	<MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
 </div>
 </template>
 
 <script lang="ts" setup>
 import { onMounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots, VNodeChild } from 'vue';
-import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os.js';
 import { useInterval } from '@@/js/use-interval.js';
-import { i18n } from '@/i18n.js';
 import type { MenuItem } from '@/types/menu.js';
+import * as os from '@/os.js';
 
 const props = defineProps<{
 	modelValue: string | null;
@@ -56,25 +53,20 @@ const props = defineProps<{
 	placeholder?: string;
 	autofocus?: boolean;
 	inline?: boolean;
-	manualSave?: boolean;
 	small?: boolean;
 	large?: boolean;
 }>();
 
 const emit = defineEmits<{
-	(ev: 'changeByUser', value: string | null): void;
-	(ev: 'update:modelValue', value: string | null): void;
+	(ev: 'update:modelValue', value: string | number | null): void;
 }>();
 
 const slots = useSlots();
 
 const { modelValue, autofocus } = toRefs(props);
-const v = ref(modelValue.value);
 const focused = ref(false);
 const opening = ref(false);
-const changed = ref(false);
-const invalid = ref(false);
-const filled = computed(() => v.value !== '' && v.value != null);
+const currentValueText = ref<string | null>(null);
 const inputEl = ref<HTMLObjectElement | null>(null);
 const prefixEl = ref<HTMLElement | null>(null);
 const suffixEl = ref<HTMLElement | null>(null);
@@ -85,26 +77,6 @@ const height =
 	36;
 
 const focus = () => container.value?.focus();
-const onInput = (ev) => {
-	changed.value = true;
-};
-
-const updated = () => {
-	changed.value = false;
-	emit('update:modelValue', v.value);
-};
-
-watch(modelValue, newValue => {
-	v.value = newValue;
-});
-
-watch(v, () => {
-	if (!props.manualSave) {
-		updated();
-	}
-
-	invalid.value = inputEl.value?.validity.badInput ?? true;
-});
 
 // このコンポーネントが作成された時、非表示状態である場合がある
 // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
@@ -134,6 +106,31 @@ onMounted(() => {
 	});
 });
 
+watch(modelValue, () => {
+	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 (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;
+				if (option.props?.value === modelValue.value) {
+					currentValueText.value = option.children as string;
+					break;
+				}
+			}
+		}
+	};
+
+	scanOptions(slots.default!());
+}, { immediate: true });
+
 function show() {
 	if (opening.value) return;
 	focus();
@@ -146,11 +143,9 @@ function show() {
 	const pushOption = (option: VNode) => {
 		menu.push({
 			text: option.children as string,
-			active: computed(() => v.value === option.props?.value),
+			active: computed(() => modelValue.value === option.props?.value),
 			action: () => {
-				v.value = option.props?.value;
-				changed.value = true;
-				emit('changeByUser', v.value);
+				emit('update:modelValue', option.props?.value);
 			},
 		});
 	};
@@ -248,7 +243,8 @@ function show() {
 .inputCore {
 	appearance: none;
 	-webkit-appearance: none;
-	display: block;
+	display: flex;
+	align-items: center;
 	height: v-bind("height + 'px'");
 	width: 100%;
 	margin: 0;
-- 
cgit v1.2.3-freya

