From 7199e6f4e0b3a2c2bc198e689c3e0cd0d0f0354a Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 17 Oct 2020 20:12:00 +0900
Subject: Migrate to Vue3 (#6587)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Update reaction.vue

* fix  bug

* wip

* wip

* wjio

* wip

* Revert "wip"

This reverts commit e427f2160adf4e8a4147006e25a89854edab0033.

* wip

* wip

* wip

* Update init.ts

* Update drive-window.vue

* wip

* wip

* Use PascalCase for components

* Use PascalCase for components

* update dep

* wip

* wip

* wip

* Update init.ts

* wip

* Update paging.ts

* Update test.vue

* watch deep

* wip

* lint

* wip

* wip

* wip

* wip

* wiop

* wip

* Update webpack.config.ts

* alllow null poll

* wip

* wip

* wip

* wiop

* UI redesign & refactor (#6714)

* wip

* wip

* wip

* wip

* wip

* Update drive.vue

* Update word-mute.vue

* wip

* wip

* wip

* clean up

* wip

* Update default.vue

* wip

* Update notes.vue

* Update mfm.ts

* Update index.home.vue

* Update post-form.vue

* Update post-form-attaches.vue

* wip

* Update post-form.vue

* Update sidebar.vue

* wip

* wip

* Update index.vue

* wip

* Update default.vue

* Update index.vue

* Update index.vue

* wip

* Update post-form-attaches.vue

* Update note.vue

* wip

* clean up

* Update notes.vue

* wip

* wip

* Update ja-JP.yml

* wip

* wip

* Update index.vue

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update default.vue

* wip

* Update _dark.json5

* wip

* wip

* wip

* clean up

* wip

* wip

* Update index.vue

* Update test.vue

* wip

* wip

* fix

* wip

* wip

* wip

* wip

* clena yop

* wip

* wip

* Update store.ts

* Update messaging-room.vue

* Update default.widgets.vue

* fix

* wip

* wip

* Update modal.vue

* wip

* Update os.ts

* Update os.ts

* Update deck.vue

* Update init.ts

* wip

* Update ja-JP.yml

* v-sizeは単にwindowのresizeを監視するだけで良いかもしれない

* Update modal.vue

* wip

* Update tooltip.ts

* wip

* wip

* wip

* wip

* wip

* Update image-viewer.vue

* wip

* wip

* Update style.scss

* Update style.scss

* Update visitor.vue

* wip

* Update init.ts

* Update init.ts

* wip

* wip

* Update visitor.vue

* Update visitor.vue

* Update visitor.vue

* Update visitor.vue

* wip

* wip

* Update modal.vue

* Update header.vue

* Update menu.vue

* Update about.vue

* Update about-misskey.vue

* wip

* wip

* Update visitor.vue

* Update tooltip.ts

* wip

* Update drive.vue

* wip

* Update style.scss

* Update header.vue

* wip

* wip

* Update users.user.vue

* Update announcements.vue

* wip

* wip

* wip

* Update emojis.vue

* wip

* Update emojis.vue

* Update style.scss

* Update users.vue

* wip

* Update style.scss

* wip

* Update welcome.entrance.vue

* Update radio.vue

* Update size.ts

* Update emoji-edit-dialog.vue

* wip

* Update emojis.vue

* wip

* Update emojis.vue

* Update emojis.vue

* Update emojis.vue

* wip

* wip

* wip

* wip

* Update file-dialog.vue

* wip

* wip

* Update token-generate-window.vue

* Update notification-setting-window.vue

* wip

* wip

* Update _error_.vue

* Update ja-JP.yml

* wip

* wip

* Update store.ts

* Update emojis.vue

* Update emojis.vue

* Update emojis.vue

* Update announcements.vue

* Update store.ts

* wip

* Update page-editor.vue

* wip

* wip

* Update modal.vue

* wip

* Update select-file.ts

* Update timeline.vue

* Update emojis.vue

* Update os.ts

* wip

* Update user-select.vue

* Update mfm.ts

* Update get-file-info.ts

* Update drive.vue

* Update init.ts

* Update mfm.ts

* wip

* wip

* Update window.vue

* Update note.vue

* wip

* wip

* Update user-info.vue

* wip

* wip

* wip

* wip

* wip

* Update header.vue

* Update header.vue

* wip

* Update explore.vue

* wip

* wip

* wip

* Update webpack.config.ts

* wip

* wip

* wip

* wip

* wip

* wip

* Update autocomplete.ts

* wip

* wip

* wip

* Update toast.vue

* wip

* Update post-form-dialog.vue

* wip

* wip

* wip

* wip

* wip

* Update users.vue

* wip

* Update explore.vue

* wip

* wip

* wip

* Update package.json

* wip

* Update icon-dialog.vue

* wip

* wip

* Update user-preview.ts

* wip

* wip

* wip

* wip

* wip

* Update instance.vue

* Update user-name.vue

* Update federation.vue

* Update instance.vue

* wip

* wip

* Update tag.vue

* wip

* wip

* wip

* wip

* wip

* Update instance.vue

* wip

* Update os.ts

* Update os.ts

* wip

* wip

* wip

* Update router.ts

* wip

* Update init.ts

* Update note.vue

* Update messages.vue

* wip

* wip

* wip

* wip

* wip

* google

* wip

* wip

* wip

* wip

* Update theme-editor.vue

* wip

* wip

* Update room.vue

* Update channel-editor.vue

* wip

* Update window.vue

* Update window.vue

* wip

* Update window.vue

* Update window.vue

* wip

* Update menu.vue

* wip

* wip

* wip

* wip

* Update messaging-room.vue

* wip

* Update post-form.vue

* Update default.widgets.vue

* Update window.vue

* wip
---
 src/client/components/ui/input.vue | 262 +++++++++++++++++--------------------
 1 file changed, 117 insertions(+), 145 deletions(-)

(limited to 'src/client/components/ui/input.vue')

diff --git a/src/client/components/ui/input.vue b/src/client/components/ui/input.vue
index f9c2d9a43a..dec4a08712 100644
--- a/src/client/components/ui/input.vue
+++ b/src/client/components/ui/input.vue
@@ -2,66 +2,51 @@
 <div class="juejbjww" :class="{ focused, filled, inline, disabled }">
 	<div class="icon" ref="icon"><slot name="icon"></slot></div>
 	<div class="input">
-		<span class="label" ref="label"><slot></slot></span>
+		<span class="label" ref="labelEl"><slot></slot></span>
 		<span class="title" ref="title">
 			<slot name="title"></slot>
-			<span class="warning" v-if="invalid"><fa :icon="faExclamationCircle"/>{{ $refs.input.validationMessage }}</span>
+			<span class="warning" v-if="invalid"><Fa :icon="faExclamationCircle"/>{{ $refs.input.validationMessage }}</span>
 		</span>
-		<div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
-		<template v-if="type != 'file'">
-			<input v-if="debounce" ref="input"
-				v-debounce="500"
-				:type="type"
-				v-model.lazy="v"
-				:disabled="disabled"
-				:required="required"
-				:readonly="readonly"
-				:placeholder="placeholder"
-				:pattern="pattern"
-				:autocomplete="autocomplete"
-				:spellcheck="spellcheck"
-				:step="step"
-				@focus="focused = true"
-				@blur="focused = false"
-				@keydown="$emit('keydown', $event)"
-				@input="onInput"
-				:list="id"
-			>
-			<input v-else ref="input"
-				:type="type"
-				v-model="v"
-				:disabled="disabled"
-				:required="required"
-				:readonly="readonly"
-				:placeholder="placeholder"
-				:pattern="pattern"
-				:autocomplete="autocomplete"
-				:spellcheck="spellcheck"
-				:step="step"
-				@focus="focused = true"
-				@blur="focused = false"
-				@keydown="$emit('keydown', $event)"
-				@input="onInput"
-				:list="id"
-			>
-			<datalist :id="id" v-if="datalist">
-				<option v-for="data in datalist" :value="data"/>
-			</datalist>
-		</template>
-		<template v-else>
-			<input ref="input"
-				type="text"
-				:value="filePlaceholder"
-				readonly
-				@click="chooseFile"
-			>
-			<input ref="file"
-				type="file"
-				:value="value"
-				@change="onChangeFile"
-			>
-		</template>
-		<div class="suffix" ref="suffix"><slot name="suffix"></slot></div>
+		<div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div>
+		<input v-if="debounce" ref="inputEl"
+			v-debounce="500"
+			:type="type"
+			v-model.lazy="v"
+			:disabled="disabled"
+			:required="required"
+			:readonly="readonly"
+			:placeholder="placeholder"
+			:pattern="pattern"
+			:autocomplete="autocomplete"
+			:spellcheck="spellcheck"
+			:step="step"
+			@focus="focused = true"
+			@blur="focused = false"
+			@keydown="onKeydown($event)"
+			@input="onInput"
+			:list="id"
+		>
+		<input v-else ref="inputEl"
+			:type="type"
+			v-model="v"
+			:disabled="disabled"
+			:required="required"
+			:readonly="readonly"
+			:placeholder="placeholder"
+			:pattern="pattern"
+			:autocomplete="autocomplete"
+			:spellcheck="spellcheck"
+			:step="step"
+			@focus="focused = true"
+			@blur="focused = false"
+			@keydown="onKeydown($event)"
+			@input="onInput"
+			:list="id"
+		>
+		<datalist :id="id" v-if="datalist">
+			<option v-for="data in datalist" :value="data"/>
+		</datalist>
+		<div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div>
 	</div>
 	<button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $t('save') }}</button>
 	<div class="desc _caption"><slot name="desc"></slot></div>
@@ -69,11 +54,12 @@
 </template>
 
 <script lang="ts">
-import Vue from 'vue';
+import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
 import debounce from 'v-debounce';
 import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
+import * as os from '@/os';
 
-export default Vue.extend({
+export default defineComponent({
 	directives: {
 		debounce
 	},
@@ -136,106 +122,92 @@ export default Vue.extend({
 			required: false,
 		},
 	},
-	data() {
-		return {
-			v: this.value,
-			focused: false,
-			invalid: false,
-			changed: false,
-			id: Math.random().toString(),
-			faExclamationCircle
+	emits: ['change', 'keydown', 'enter'],
+	setup(props, context) {
+		const { value, type, autofocus } = toRefs(props);
+		const v = ref(value.value);
+		const id = Math.random().toString(); // TODO: uuid?
+		const focused = ref(false);
+		const changed = ref(false);
+		const invalid = ref(false);
+		const filled = computed(() => v.value !== '' && v.value != null);
+		const inputEl = ref(null);
+		const prefixEl = ref(null);
+		const suffixEl = ref(null);
+		const labelEl = ref(null);
+
+		const focus = () => inputEl.value.focus();
+		const onInput = (ev) => {
+			changed.value = true;
+			context.emit('change', ev);
 		};
-	},
-	computed: {
-		filled(): boolean {
-			return this.v !== '' && this.v != null;
-		},
-		filePlaceholder(): string | null {
-			if (this.type != 'file') return null;
-			if (this.v == null) return null;
+		const onKeydown = (ev: KeyboardEvent) => {
+			context.emit('keydown', ev);
 
-			if (typeof this.v == 'string') return this.v;
-
-			if (Array.isArray(this.v)) {
-				return this.v.map(file => file.name).join(', ');
-			} else {
-				return this.v.name;
+			if (ev.code === 'Enter') {
+				context.emit('enter');
 			}
-		}
-	},
-	watch: {
-		value(v) {
-			this.v = v;
-		},
-		v(v) {
-			if (this.type === 'number') {
-				this.$emit('input', parseFloat(v));
+		};
+
+		watch(value, newValue => {
+			v.value = newValue;
+		});
+
+		watch(v, newValue => {
+			if (type?.value === 'number') {
+				context.emit('update:value', parseFloat(newValue));
 			} else {
-				this.$emit('input', v);
+				context.emit('update:value', newValue);
 			}
 
-			this.invalid = this.$refs.input.validity.badInput;
-		}
-	},
-	mounted() {
-		if (this.autofocus) {
-			this.$nextTick(() => {
-				this.$refs.input.focus();
-			});
-		}
+			invalid.value = inputEl.value.validity.badInput;
+		});
 
-		this.$nextTick(() => {
-			// このコンポーネントが作成された時、非表示状態である場合がある
-			// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
-			const clock = setInterval(() => {
-				if (this.$refs.prefix) {
-					this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
-					if (this.$refs.prefix.offsetWidth) {
-						this.$refs.input.style.paddingLeft = this.$refs.prefix.offsetWidth + 'px';
-					}
+		onMounted(() => {
+			nextTick(() => {
+				if (autofocus.value) {
+					focus();
 				}
-				if (this.$refs.suffix) {
-					if (this.$refs.suffix.offsetWidth) {
-						this.$refs.input.style.paddingRight = this.$refs.suffix.offsetWidth + 'px';
+
+				// このコンポーネントが作成された時、非表示状態である場合がある
+				// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
+				const clock = setInterval(() => {
+					if (prefixEl.value) {
+						labelEl.value.style.left = (prefixEl.value.offsetLeft + prefixEl.value.offsetWidth) + 'px';
+						if (prefixEl.value.offsetWidth) {
+							inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px';
+						}
 					}
-				}
-			}, 100);
+					if (suffixEl.value) {
+						if (suffixEl.value.offsetWidth) {
+							inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px';
+						}
+					}
+				}, 100);
 
-			this.$once('hook:beforeDestroy', () => {
-				clearInterval(clock);
+				onUnmounted(() => {
+					clearInterval(clock);
+				});
 			});
 		});
 
-		this.$on('keydown', (e: KeyboardEvent) => {
-			if (e.code == 'Enter') {
-				this.$emit('enter');
-			}
-		});
+		return {
+			id,
+			v,
+			focused,
+			invalid,
+			changed,
+			filled,
+			inputEl,
+			prefixEl,
+			suffixEl,
+			labelEl,
+			focus,
+			onInput,
+			onKeydown,
+			faExclamationCircle,
+		};
 	},
-	methods: {
-		focus() {
-			this.$refs.input.focus();
-		},
-		togglePassword() {
-			if (this.type == 'password') {
-				this.type = 'text'
-			} else {
-				this.type = 'password'
-			}
-		},
-		chooseFile() {
-			this.$refs.file.click();
-		},
-		onChangeFile() {
-			this.v = Array.from((this.$refs.file as any).files);
-			this.$emit('input', this.v);
-			this.$emit('change', this.v);
-		},
-		onInput(ev) {
-			this.changed = true;
-			this.$emit('change', ev);
-		}
-	}
 });
 </script>
 
-- 
cgit v1.2.3-freya

