Kirby 4 file upload - How to display an input for a required field in uploader?

The new image uploader in Kirby 4 is great, but is there any way to have it display inputs for required fields?

An input for alt text is a good example here (to enforce/guard that all uploaded images have alt text).

In Kirby 3 I had to create a custom plugin to achieve this. It extended the built-in k-upload component that is now deprecated. Hoping there’s a built-in (or at least simpler) way to achieve this with Kirby 4.

Seems there’s still no simple way to achieve this with Kirby 4 :frowning:

The workable-enough approach I’ve settled on for now (which only duplicates the template markup, no upload handling or anything which should keep this relatively maintainable):

// File upload handling.
// Adds a required input for alt text for image uploads.

window.panel.plugin('my-plugin-namespace/upload', {
   components: {
      'k-upload-dialog': {
         extends: 'k-upload-dialog', // https://github.com/getkirby/kirby/blob/main/panel/src/components/Dialogs/UploadDialog.vue
         data() {
            return {
               imageAltsByFilename: {},
            }
         },
         created() {
            /* 

            Here we send PATCH requests to each new image entry to set their alt text.

            This gets called when kirby calls `this.emit("done" [...]` (here: https://github.com/getkirby/kirby/blob/ba8e9e511d797c0c2bc9278d5484020c4aa8cfce/panel/src/panel/upload.js#L71)

            This seemed to be the simplest approach (much simpler than duplicating the upload logic), though it does
            seem crazy that Kirby doesn't offer a simpler approach for something as simple as requiring alt text be entered with new images.

            Note that this gets called "in the background" after the modal closes (which might not be the best approach, though within the
            limitations it's not clear how to get around this).
            
            */

            window.panel.upload.on.done = (files) => {
               const imageFiles = files.filter((file) => file.type === 'image')
               Promise.all(
                  imageFiles.map((file) => {
                     return this.$api.patch(file.link, {alt: this.imageAltsByFilename[file.filename]})
                  })
               )
            }
         },
         destroyed() {
            // Remove our custom `done` callback to prevent possible memory leaks.
            delete window.panel.upload.on.done
         },
         methods: {
            customSubmitHandler() {
               // Update our local `imageAltsByFilename` in order to set each image's alt text in the `done` callback (above)
               window.panel.upload.files.forEach((file) => {
                  // Non-image files can also be uploaded so guard here for alt text (the alt text input is only on images)
                  if (file.alt) {
                     this.imageAltsByFilename[file.filename] = file.alt.trim()
                  }
               })

               // Call Kirby's submit handler
               this.$emit('submit')
            },
         },
         template: /*html*/ `
            <k-dialog
               ref="dialog"
               class="k-upload-dialog"
               v-bind="$props"
               :disabled="disabled || $panel.upload.files.length === 0"
               @cancel="$emit('cancel')"
               @submit="customSubmitHandler()"
            >
               <k-dropzone @drop="$panel.upload.select($event)">
                  <template v-if="$panel.upload.files.length === 0">
                     <k-empty icon="upload" layout="cards" @click="$panel.upload.pick()">
                        {{ $t("files.empty") }}
                     </k-empty>
                  </template>
                  <template v-else>
                     <ul class="k-upload-items">
                        <li
                           v-for="file in $panel.upload.files"
                           :key="file.id"
                           :data-completed="file.completed"
                           class="k-upload-item"
                        >
                           <a :href="file.url" class="k-upload-item-preview" target="_blank">
                              <k-image-frame
                                 v-if="isPreviewable(file.type)"
                                 :cover="true"
                                 :src="file.url"
                                 back="pattern"
                              />
                              <k-icon-frame
                                 v-else
                                 back="black"
                                 color="white"
                                 ratio="1/1"
                                 icon="file"
                              />
                           </a>
                           <div>
                              <div>
                                 <div>
                                    <k-input
                                       v-model="file.name"
                                       :disabled="file.completed"
                                       :after="'.' + file.extension"
                                       :novalidate="true"
                                       :required="true"
                                       class="k-upload-item-input"
                                       type="slug"
                                    />
                                 </div>
                                 <div v-if="isPreviewable(file.type)">
                                    <k-input
                                       v-model="file.alt"
                                       :disabled="file.completed"
                                       :novalidate="true"
                                       :required="true"
                                       class="k-upload-item-input"
                                       placeholder="image alt text for SEO, required"
                                       type="text"
                                    />
                                 </div>
                              </div>
                              <div class="k-upload-item-body">
                                 <p v-if="file.error" class="k-upload-item-error">
                                    {{ file.error }}
                                 </p>
                                 <k-progress
                                    v-else-if="file.progress"
                                    :value="file.progress"
                                    class="k-upload-item-progress"
                                 />
                              </div>
                           </div>
                           <div class="k-upload-item-toggle">
                              <k-button
                                 v-if="!file.completed && !file.progress"
                                 icon="remove"
                                 @click="$panel.upload.remove(file.id)"
                              />
         
                              <div v-else-if="!file.completed">
                                 <k-icon type="loader" />
                              </div>
         
                              <k-button
                                 v-else
                                 icon="check"
                                 theme="positive"
                                 @click="$panel.upload.remove(file.id)"
                              />
                           </div>
                        </li>
                     </ul>
                  </template>
               </k-dropzone>
            </k-dialog>
         `,
      },
   },
})