

Vuetify3のダイアログがボタンをクリックしても開かない。ただし、その後にダイアログ(EditImgBtn.vue)のHTMLを変更するとダイアログが開く。（適当なHTMLタグのインデントを深くする・HTMLのコードを入力する）

調べても原因が分からず、お知恵をお借りしたいです。

環境  
Vue3  
Vuetify3

TweetForm.vue（問題のダイアログの親コンポーネント）

[code]

    <script>
    import { MESSAGE_MAX_COUNT, IMAGE_MAX_COUNT } from '../const'
    import { postTweet } from '../api'
    import EditImgBtn from './EditImgBtn.vue'
    
    export default {
      components: {
        EditImgBtn,
      },
      data() {
        return {
          debug: 0,
          tweet: {
            message: '',
            images: [],
          },
          textContent: '',
          previousRevisions: [''],
          index: 0,
          afterInput: false,
          showWarning: false,
        }
      },
      computed: {
        previousRevision(){
          return this.previousRevisions[this.index]
        },
        stopUpload(){
          return this.tweet.images.length === IMAGE_MAX_COUNT
        },
      },
      methods: {
        async onClick(){
          const res = await postTweet(this.tweet)
          console.log(`POST ${res.data.tweet.message}`)
          this.$store.commit('setTweet', { tweet: res.data.tweet })
          // if(/\s#.+/.test(res.data.tweet.message)) this.dispatch('createHashTags', { tweet: res.data.tweet })
        },
        onInput(e){
          this.tweet.message = e.target.textContent
          if(e.target.textContent.length > 0 && e.target.textContent.length <= MESSAGE_MAX_COUNT){
            this.$refs.form.innerHTML = `<span id="under-message">${e.target.textContent}</span>`
            this.setCaretPosition(document.getElementById('under-message'), e.target.textContent.length)
          }else if(e.target.textContent.length > MESSAGE_MAX_COUNT){
            this.$refs.form.innerHTML = `<span id="under-message">${e.target.textContent.substring(0, MESSAGE_MAX_COUNT)}</span>` + `<span id="over-message" style="background-color: pink;">${e.target.textContent.substring(MESSAGE_MAX_COUNT)}</span>`
            this.setCaretPosition(document.getElementById('over-message'), e.target.textContent.length - MESSAGE_MAX_COUNT)
          }
          switch(e.inputType){
            case 'insertFromPaste':
            case 'deleteByCut':
              this.saveState(e)
              this.afterInput = false
              break
            case 'insertText':
            case 'deleteContentBackward':
              this.afterInput = true
              break
          }
        },
        onKeyDown(e){
          if(this.index < this.previousRevisions.length - 1 && e.metaKey && e.shiftKey && e.key === 'z'){
            this.redo()
            this.afterInput = false
            return
          }
          if(this.index > 0 && e.metaKey && e.key === 'z'){
            this.undo()
            this.afterInput = false
            return
          }
        },
        onKeyUp(e){
          if(this.afterInput){
            this.saveState(e)
            this.afterInput = false
          }
        },
        setCaretPosition(el, pos){
          el.focus();
          var range = document.createRange();
          var sel = window.getSelection();
          range.setStart(el.firstChild, pos);
          range.collapse(true);
          sel.deleteAllRanges();
          sel.addRange(range); 
        },
        setTextContent(e){
          if(e.target.textContent === undefined){
            this.textContent = ''
          }else{
            this.textContent = e.target.textContent
          }
        },
        saveState(e){
          this.setTextContent(e)
          this.index += 1
          this.previousRevisions[this.index] = this.textContent
        },
        undo(){
          this.index -= 1
          this.edit()
        },
        redo(){
          this.index += 1
          this.edit()
        },
        edit(){
          if(this.previousRevisions.length === 1){
            this.$refs.form.innerHTML = ''
          }else if(this.previousRevisions.length > 1 && this.previousRevision.length <= MESSAGE_MAX_COUNT){
            this.$refs.form.innerHTML = `<span id="under-message">${this.previousRevision}</span>`
          }else if(this.previousRevisions.length > 1 && this.previousRevision.length > MESSAGE_MAX_COUNT){
            this.$refs.form.innerHTML = `<span id="under-message">${this.previousRevision.substring(0, MESSAGE_MAX_COUNT)}</span>` + `<span id="over-message" style="background-color: pink;">${this.previousRevision.substring(MESSAGE_MAX_COUNT)}</span>`
          }
        },
        onChangeImage(e){
          var images = Array.from(e.target.files)
          if(this.tweet.images.length + images.length > 4){
            this.setShowWarning()
            return
          }
          var i = 0
          while(i < images.length){
            var image = images[i]
            this.tweet.images.push(image)
            i++
          }
        },
        async postTweet(){
          const res = await postTweet({ tweet: this.tweet })
          console.log(res.data.tweet.images)
        },
        setShowWarning(){
          this.showWarning = true
          setTimeout(()=>this.showWarning = false, 5000)
        },
        imgClasses(length, index){
          var classes = []
          classes.push('img')
          if(length === 1){
            classes.push('all')
          }else if(length === 2){
            classes.push('half')
          }else if(length === 3){
            if(index === 0){
              classes.push('half')
            }else{
              classes.push('quarter')
            }
          }else{
            classes.push('quarter')
          }
          return classes
        },
        deleteImg(index){
          this.tweet.images.splice(index, 1)
        },
        editImg(index){
    
        },
        getImageUrl(img){
          return URL.createObjectURL(img)
        }
      },
    }
    </script>
    
    <template>
      <div contenteditable="true" ref="form" class="form" @input="onInput" @keyup="onKeyUp" @keydown="onKeyDown">
        <div v-if="tweet.images" class="img-box">
          <div v-for="(image, index) in tweet.images" :key="index" :class="imgClasses(tweet.images.length, index)">
            <img :src="getImageUrl(image)" class="image">
            <button class="delete-img-btn" @click="deleteImg(index)">
              <svg width="18" height="18">
                <path d="M 3 3, L 15 15, M 15 3, L 3 15" stroke="white" fill="transparent" stroke-width="2"></path>
              </svg>
            </button>
            <EditImgBtn :url="getImageUrl(image)"></EditImgBtn>
          </div>
        </div>
      </div>
      <v-btn @click="postTweet" class="post-btn" color="rgb(29, 155, 240)" density="comfortable" rounded="xl" size="large">ツイートする</v-btn>
      <div class="support-functions">
        <div tabindex="0" class="function">
          <svg width="24" height="24">
            <path d="M 3 5.5, C 3 4.119, 4.119 3, 5.5 3 h 13, C 19.881 3, 21 4.119, 21 5.5 v 13 c 0 1.381, -1.119 2.5, -2.5 2.5 h -13 c -1.381 0, -2.5 -1.119, -2.5 -2.5 v -13 z
              M 10 7, c -2 0, -2 2, 0 2, M 9 7, c 2 0, 2 2, 0 2 M 21 16 l -5 -5, -5 5, -3 -3, -5 5" stroke="rgb(29, 155, 240)" fill="transparent" stroke-width="2"></path>
          </svg>
          <input @change="onChangeImage" :disabled="stopUpload" type="file" accept="image/jpeg, image/png, image/webp, image/gif, video/mp4, video/quicktime" multiple tabindex="-1" class="hidden">
        </div>
      </div>
      <div v-if="showWarning" class="warning">
        GIF画像１点か画像を４点まで選択してください。
      </div>
    </template>
    
    <style scoped>
    
    .form {
      height: 400px;
    }
    .length-over{
      background-color: pink;
    }
    .post-btn {
      color: white; 
      font-weight: bold; 
      letter-spacing: 0px; 
      padding:0 16px;
    }
    .support-functions{
      display: flex;
    }
    .function {
      position: relative;
      width: 36px;
      height: 36px;
    }
    .hidden {
      width: 24px;
      height: 24px;
      opacity: 0;
      position: absolute;
      top: 0px;
      left: 0px;
    }
    .warning {
      z-index: 100;
      position: absolute;
      bottom: 0px;
      left: 200px;
      background-color: rgb(29, 155, 240);
      color: white;
    }
    .img-box {
      width: 600px;
      height: 300px;
      display: flex;
    }
    .img {
      position: relative;
      border-radius: 5%;
    }
    .all {
      max-width: 100%;
      max-height: 100%;
    }
    .half {
      width: 50%;
      height: 100%;
    }
    .quarter {
      width: 50%;
      height: 50%;
    }
    .image {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    .delete-img-btn {
      display: inline-block;
      position: absolute;
      top: 10px;
      left: 10px;
      width: 36px;
      height: 36px;
      text-align: center;
      padding: 9px 0;
      border-radius: 50%;
      background-color: rgba(0, 0, 0, 0.7);
    }
    </style>
    
[/code]

EditImgBtn.vue

[code]

    <template>
      <v-dialog
      v-model="dialog"
      width="auto"
      >
        <template v-slot:activator="{ props }">
          <button
            v-bind="props"
            class="edit-img-btn"
          >
            <svg width="18" height="18">
              <path d="M 3 15, v -3, c 2 -6, 9 1, 3 3 z M 6 9, l 5 -5, c 2 -2, 4 0, 2 2 L 9 11" stroke="white" fill="transparent" stroke-width="2"></path>
            </svg>
          </button>
        </template>
        <v-card>
          <div class="header">
            <button @click="dialog = false" class="prev-btn">
              <svg width="24" height="24" view-box="0, 0, 24, 24">
                <path d="M 12 3, l -9, 9, l 9, 9, M 3 12 h 18" stroke="black" fill="transparent" stroke-width="2"></path>
              </svg>
            </button>
            <h2>メディアをトリミング</h2>
            <button>
              
            </button>
          </div>
          <div class="nav">
          
          </div>
          <div class="main">
          
          </div>
        </v-card>
      </v-dialog>
    </template>
    
    <script>
    import Cropper from 'cropperjs'
    
    export default {
     
        props: ['url'],
        methods: {
        
        }
      }
    </script>
    
    <style scoped>
    .container {
      width: 800px;
      height: 900px;
      border-radius: 5px;
    }
    .edit-img-btn {
      display: inline-block;
      position: absolute;
      bottom: 10px;
      right: 10px;
      width: 24px;
      height: 24px;
      padding: 3px;
      border-radius: 50%;
      background-color: rgba(0, 0, 0, 0.7);
    }
    .header {
      display: flex;
      justify-content: center;
      height: 36px;
      align-items: center;
    }
    .prev-btn {
      display: flex;
      align-items: center;
    }
    .main {
      width: 600px;
      height: 800px;
      padding: 10px;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .img {
      display: block;
      max-width: 100%;
      max-height: 100%;
    }
    </style>
    
[/code]


CC BY-SA 4.0
2023-05-07T04:34:03.123
94777
2023-05-07T05:45:20.740
2023-05-07T05:45:20.740
3060
49334
question
vue.js
Vuetify3のダイアログがボタンをクリックしても開かない