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>
         `,
      },
   },
})

Is it still not possible to ask for alt text on upload (without your plugin)?

Unfortunately not :frowning:

(would love to be proven wrong here)

Hey,
I’ve tested some stuff with the create dialog options, but it doesn’t seem possible either…

No, these are the docs for page blueprints, not file blueprints. The option currently only exists for creating pages.

There is an existing feature request for it File blueprint: `create` option like for pages · Kirby Feedback - I encourage everyone to vote on it.

As mentioned on Discord, the UI is one tricky part as it’s not as easily to integrate a dynamic amount of fields into the upload dialog (whereas the page create dialog was a lot more forgiving)

(@paubou I know we’re already talking on Discord about it but wanted to leave it here as well for others that might find this thread)

I’d be fine with the UI being not ideal in the case of multiple inputs being added (as a first-pass “better than nothing” type of thing).

Trying to handle this via custom code is a nightmare. And even once working right, it’s quite prone to breaking on Kirby updates.

Having first-class support for this would be a huge improvement.

We will probably get there, but it will take a while (not anymore in v5.0).

1 Like