Skip to content

Commit

Permalink
Merge pull request #4139 from traPtitech/feat/focus_on_field_on_mount
Browse files Browse the repository at this point in the history
入力欄のあるモーダルが表示されたときに自動でフォーカスするように
  • Loading branch information
mehm8128 authored Nov 14, 2023
2 parents 52674dc + 7f79768 commit 4cee220
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 10 deletions.
14 changes: 13 additions & 1 deletion src/components/GroupManager/LineEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<div :class="$style.label">{{ label }}</div>
<div v-if="isEditing" :class="$style.inputWrapper">
<form-input
ref="inputRef"
v-model="localValue"
:class="$style.input"
:max-length="maxLength"
Expand Down Expand Up @@ -37,6 +38,7 @@ import AIcon from '/@/components/UI/AIcon.vue'
import { useModelValueSyncer } from '/@/composables/useModelSyncer'
import useLocalInput from '/@/composables/utils/useLocalInput'
import useToggle from '/@/composables/utils/useToggle'
import { ref, nextTick } from 'vue'
const props = defineProps<{
label: string
Expand All @@ -48,6 +50,8 @@ const emit = defineEmits<{
(e: 'update:modelValue', _value: string): void
}>()
const inputRef = ref<InstanceType<typeof FormInput> | null>(null)
const remoteValue = useModelValueSyncer(props, emit)
const { localValue, isEditing } = useLocalInput(
remoteValue,
Expand All @@ -57,7 +61,15 @@ const { localValue, isEditing } = useLocalInput(
},
true // キャンセルするUIがないため
)
const { open: startEditing, close: endEditing } = useToggle(isEditing)
const { open, close: endEditing } = useToggle(isEditing)
const startEditing = async () => {
open()
await nextTick()
if (isEditing.value) {
inputRef.value?.focus()
}
}
</script>

<style lang="scss" module>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

<script lang="ts">
import type { Ref } from 'vue'
import { computed, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import apis, { buildFilePathForPost, formatResizeError } from '/@/lib/apis'
import useModifierKey from '/@/components/Main/MainView/MessageInput/composables/useModifierKey'
import useTextStampPickerInvoker from '../composables/useTextStampPickerInvoker'
Expand Down Expand Up @@ -174,6 +174,10 @@ const {
addAttachment,
onAddAttachments
} = useAttachmentsEditor(props, text)
onMounted(() => {
textareaComponentRef.value?.textareaAutosizeRef.$el?.focus()
})
</script>

<style lang="scss" module>
Expand Down
16 changes: 13 additions & 3 deletions src/components/Main/MainView/PrimaryViewSidebar/ContentEditor.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<template>
<div :class="$style.container">
<div v-if="isEditingValue && value !== undefined">
<textarea-autosize v-model="value" :class="$style.editor" />
<textarea-autosize
ref="textareaRef"
v-model="value"
:class="$style.editor"
/>
<length-count :val="value" :max-length="maxLength" />
</div>
<div v-else :class="$style.content" :data-is-empty="$boolAttr(isEmpty)">
Expand All @@ -24,7 +28,7 @@
import AIcon from '/@/components/UI/AIcon.vue'
import LengthCount from '/@/components/UI/LengthCount.vue'
import TextareaAutosize from '/@/components/UI/TextareaAutosize.vue'
import { computed } from 'vue'
import { computed, nextTick, ref } from 'vue'
import { countLength } from '/@/lib/basic/string'
import {
useModelSyncer,
Expand All @@ -51,14 +55,20 @@ const emit = defineEmits<{
const value = useModelValueSyncer(props, emit)
const isEditingValue = useModelSyncer(props, emit, 'isEditing')
const textareaRef = ref<InstanceType<typeof TextareaAutosize> | null>(null)
const content = computed(() => {
if (value.value === '') return props.fallbackValue
if (value.value === undefined) return 'ロード中'
return value.value
})
const isEmpty = computed(() => value.value === '' || value.value === undefined)
const onButtonClick = () => {
const onButtonClick = async () => {
isEditingValue.value = !isEditingValue.value
await nextTick()
if (isEditingValue.value) {
textareaRef.value?.focus()
}
}
const isExceeded = computed(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div :class="$style.container">
<div :class="$style.input">
<filter-input v-model="value" on-secondary disable-ime />
<filter-input v-model="value" on-secondary disable-ime focus-on-mount />
</div>
</div>
</template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<navigation-content-container subtitle="ユーザーリスト">
<filter-input v-model="query" on-secondary />
<filter-input v-model="query" on-secondary focus-on-mount />
<div v-if="query.length > 0" :class="$style.list">
<users-element
v-for="user in filteredItems"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
label="チャンネル名"
:class="$style.input"
:max-length="20"
focus-on-mount
/>
<p :class="$style.desc">
実行すると
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
v-model="manageState.name"
label="チャンネル名"
:max-length="20"
focus-on-mount
/>
<form-selector
v-model="manageState.parent"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
label="名前"
:class="$style.input"
:max-length="30"
focus-on-mount
/>
<form-text-area
v-model="description.val"
Expand Down
2 changes: 1 addition & 1 deletion src/components/Modal/Common/UsersSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
@update:model-value="toggleAll"
/>
</label>
<filter-input v-model="query" :class="$style.search" />
<filter-input v-model="query" :class="$style.search" focus-on-mount />
</div>
<div :class="$style.list">
<label v-for="user in filteredUsers" :key="user.id" :class="$style.user">
Expand Down
1 change: 1 addition & 0 deletions src/components/Modal/GroupCreateModal/GroupCreateModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
:class="$style.item"
label="グループ名"
:max-length="30"
focus-on-mount
/>
<form-input
v-model="desc"
Expand Down
14 changes: 12 additions & 2 deletions src/components/UI/FormInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@
<script lang="ts" setup>
import AIcon from '/@/components/UI/AIcon.vue'
import LengthCount from '/@/components/UI/LengthCount.vue'
import { shallowRef } from 'vue'
import { onMounted, shallowRef } from 'vue'
import { randomString } from '/@/lib/basic/randomString'
import useInput from '/@/composables/useInput'
import useShowPassword from '/@/composables/dom/useShowPassword'
import { isTouchDevice } from '/@/lib/dom/browser'
const props = withDefaults(
defineProps<{
Expand All @@ -74,13 +75,15 @@ const props = withDefaults(
step?: string
maxLength?: number
useChangeEvent?: boolean
focusOnMount?: boolean
}>(),
{
type: 'text',
modelValue: '',
onSecondary: false,
placeholder: '',
useChangeEvent: false
useChangeEvent: false,
focusOnMount: false
}
)
Expand Down Expand Up @@ -108,6 +111,13 @@ const id = randomString()
const { isPasswordShown, togglePassword, typeWithShown } =
useShowPassword(props)
onMounted(() => {
if (!props.focusOnMount || isTouchDevice()) return
focus()
})
defineExpose({ focus })
</script>

<style lang="scss" module>
Expand Down
9 changes: 9 additions & 0 deletions src/components/UI/TextareaAutosize.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ const emit = defineEmits<{
const { value, onInput } = useTextModelSyncer(props, emit)
const textareaEle = ref<HTMLTextAreaElement | null>(null)
const focus = () => {
textareaEle.value?.focus()
}
onMounted(() => {
if (textareaEle.value) {
autosize(textareaEle.value)
Expand All @@ -48,6 +53,10 @@ onBeforeUnmount(() => {
autosize.destroy(textareaEle.value)
}
})
defineExpose({
focus
})
</script>

<style lang="scss" module>
Expand Down

0 comments on commit 4cee220

Please sign in to comment.