Skip to content

[v6] feat: New filelist field - #8337

Merged
distantnative merged 3 commits into
v6/section-to-fieldsfrom
v6/feat/filelist-field
Aug 13, 2026
Merged

[v6] feat: New filelist field#8337
distantnative merged 3 commits into
v6/section-to-fieldsfrom
v6/feat/filelist-field

Conversation

@afbora

@afbora afbora commented Jul 29, 2026

Copy link
Copy Markdown
Member

Description

This is part of merging sections into fields (#7859). @distantnative suggested to start by rebuilding one section as a real field class, so this PR does the files section.

The name filelist is only a working name for now — files is already taken by the file picker, and we still have to decide how to solve that.

What is in here:

  • ModelListField, an abstract base class with everything a list of models needs. pagelist builds on it in the follow-up PR.
  • FileListField on top of it, registered as type: filelist.
  • The Panel component and some lab examples.

I tried to keep the behaviour exactly like the current files section, no redesign. Same props, same defaults, same error messages. The blueprint option is still called search, only the internal name is searchable, as @distantnative suggested.

There is also one small fix in BaseField: isSubmittable() was missing, so a field without a value crashed on save. It is a separate commit, since it also affects SectionField and InfoField.

@bastianallgeier reported that an invalid list was not marked as such. k-field can now pass an invalid state down to its label, so this works for every field, not just this one.

Changelog

🎉 Features

  • New filelist field which lists the files of a page, like the files section does

✨ Enhancements

  • Invalid fields without an input of their own now get the red label as well

🐛 Bug fixes

  • Fields without a value no longer break saving

Docs

The field works like the files section, but as a field:

fields:
  gallery:
    type: filelist
    layout: cards
    template: image
    search: true
    batch: true

Options: batch, columns, create, empty, flip, image, info, layout, limit, max, min, page, parent, query, search, size, sortable, sortBy, template, text — plus when and width, which sections cannot do.

For review team

@afbora afbora self-assigned this Jul 29, 2026
@afbora

afbora commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

I've already added lab examples but you can also test via standablone following blueprint:

title: Filelist field

tabs:
  basics:
    label: Basics
    icon: box
    fields:
      plain:
        label: 1 – Plain
        type: filelist

      withHelp:
        label: 2 – Help text
        type: filelist
        help: Every file of this page, nothing else configured

      noImages:
        label: 3 – image false
        type: filelist
        image: false

      customText:
        label: 4 – Custom text and info
        type: filelist
        text: "{{ file.alt }}"
        info: "{{ file.template.or('no template') }}"

      emptyState:
        label: 5 – Empty state
        type: filelist
        template: does-not-exist
        empty: Nothing here, this is the custom empty text

  layouts:
    label: Layouts
    icon: dashboard
    fields:
      list:
        label: 6 – layout list
        type: filelist
        layout: list

      cardlets:
        label: 7 – layout cardlets
        type: filelist
        layout: cardlets

      cards:
        label: 8 – layout cards
        type: filelist
        layout: cards

      cardsSmall:
        label: 9 – layout cards, size small
        type: filelist
        layout: cards
        size: small

      table:
        label: 10 – layout table
        type: filelist
        layout: table

      tableColumns:
        label: 11 – layout table with columns
        type: filelist
        layout: table
        columns:
          alt:
            label: Alt text
          template:
            label: Template
            value: "{{ file.template.or('–') }}"

  data:
    label: Data
    icon: filter
    fields:
      parent:
        label: 12 – parent query
        type: filelist
        parent: site.find("photography").children.first
        help: Files of the first album, with a link to it

      query:
        label: 13 – query
        type: filelist
        query: page.images.filterBy("template", "image")

      template:
        label: 14 – template image
        type: filelist
        template: image
        help: 5 of 7 files

      pagination:
        label: 15 – limit 3
        type: filelist
        limit: 3

      sortBy:
        label: 16 – sortBy filename desc
        type: filelist
        sortBy: filename desc
        help: Manual sorting must be off

      flip:
        label: 17 – flip
        type: filelist
        flip: true
        help: Manual sorting must be off

  options:
    label: Options
    icon: cog
    fields:
      min:
        label: 18 – min 10
        type: filelist
        min: 10
        help: Invalid, must block publishing

      max:
        label: 19 – max 3
        type: filelist
        max: 3
        help: Invalid, upload disabled

      maxExact:
        label: 20 – max 7
        type: filelist
        max: 7
        help: Valid but full, upload disabled

      noCreate:
        label: 21 – create false
        type: filelist
        create: false

      search:
        label: 22 – search
        type: filelist
        search: true

      batch:
        label: 23 – batch
        type: filelist
        batch: true

      uploadTemplate:
        label: 24 – upload template image
        type: filelist
        template: image
        help: Uploads should use the image template

@distantnative distantnative left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start! Some first thoughts and questions.

Comment thread panel/src/components/Forms/Field/FileListField.vue Outdated
Comment thread panel/src/components/Forms/Field/FileListField.vue Outdated
Comment thread panel/src/components/Forms/Field/FileListField.vue
Comment thread panel/src/components/Forms/Field/FileListField.vue
Comment thread panel/src/components/Forms/Field/FileListField.vue Outdated
Comment thread src/Form/Field/FileListField.php Outdated
Comment thread src/Form/Field/ModelListField.php
Comment thread src/Form/Field/ModelListField.php Outdated
Comment thread src/Form/Field/ModelListField.php Outdated
Comment thread src/Form/Field/ModelListField.php
@bastianallgeier

Copy link
Copy Markdown
Member

This is already looking great!! I love the general direction

Comment thread src/Form/Field/FileListField.php Outdated
@afbora
afbora force-pushed the v6/feat/filelist-field branch 2 times, most recently from 825a1f5 to 46b1ce5 Compare August 5, 2026 12:59
@bastianallgeier
bastianallgeier force-pushed the v6/feat/filelist-field branch from 46b1ce5 to 148aad1 Compare August 8, 2026 07:22
@bastianallgeier

bastianallgeier commented Aug 8, 2026

Copy link
Copy Markdown
Member

I've created a new sandbox branch with an example setup for the file list field that's indentical to the files section https://github.com/getkirby/sandbox/tree/feat/filelist-field It all works perfectly, except validation. (invalid tab) The invalid state is not showing up correctly and the form can be submitted.

But other than that I feel like this is pretty much complete.

I was wondering if we should maybe start a new v6/feat/section-migration branch and merge all those migration tasks into that first. Then we could catch last minute inconsistencies between migrated classes there before we merge everything into v6/develop. What do you think @afbora & @distantnative?

Side note: @distantnative I feel like we broke something in the file dropdowns (not related to this PR) They are super slow for me in the sandbox at the moment. Not just for the filelist field but also in files sections.

@distantnative

Copy link
Copy Markdown
Member

Makes sense to me with the extra branch to collect all of these changes first.

And with the drop-down you mean the options dropdown?

@bastianallgeier

Copy link
Copy Markdown
Member

@distantnative yes, the options dropdown

@bastianallgeier bastianallgeier added this to the 6.0.0-alpha.4 milestone Aug 9, 2026
@distantnative
distantnative changed the base branch from v6/develop to v6/section-to-fields August 9, 2026 15:40
@afbora

afbora commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@bastianallgeier Thanks for testing it!

Validation: the publishing part actually works — with min: 2 and no files the status change is blocked. Only the red invalid state is missing. Fields show that state through a native input inside them, and my field does not have one. Could you check what you did when the form could be submitted? Maybe you mean saving the changes, which also works for sections?

Small heads-up: your blueprint uses search: true, but I renamed the option to searchable after @distantnative suggestion. Unknown options are dropped silently, so those examples never show the search button.

About the extra branch: sounds good to me, that makes it much easier to spot differences between the migrated classes.

@distantnative

distantnative commented Aug 11, 2026

Copy link
Copy Markdown
Member

@afbora I think the blueprint option should stay search - I would not introduce this breaking change. My suggestion was just about the internal names to align them.

And it would be great to have the red invalid state as well. If we now all migrate to fields, we even have more need that all fields behave the same.

@afbora

afbora commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@distantnative Both done.

search is the blueprint option again, only the internal name and the Vue prop stay searchable.

For the invalid state: fields only got it through a native input inside them, which a list does not have. I gave k-field the same invalid prop that k-section already has, so any field without an input can show it now — not just this one.

@distantnative

Copy link
Copy Markdown
Member

I am wondering if we should use k-input-validator here too. We could split it into k-input-validator (Actually receives input) and k-validator (jut receives a count and min/max). That way the fields would be a bit more unified maybe. But maybe also overkill. @bastianallgeier, what do you think?

@bastianallgeier

Copy link
Copy Markdown
Member

I like the idea to reuse our validators. @afbora could you try how much work this is? If it's too complicated or too "dirty" I would skip it for now. Otherwise we could add it to the PR.

@afbora

afbora commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

@bastianallgeier @distantnative I tried it and pushed it. Just a try, I can revert it.

Split it like @distantnative said: k-validator takes a count with min/max, k-input-validator extends it and counts its entries. Mostly moving code, the validation only used entries.length anyway.

Good part: it removed more than it added. The invalid prop on k-field and the extra CSS in Label.vue are gone again, .k-field:has(:invalid) already catches it. Doesn't feel dirty to me.

One thing I found on the way: ModelsSection validates this.data.length, which is only the current page. So a section with limit: 3 and min: 5 looks invalid even with 10 files. The field uses the total instead. Separate issue?

@distantnative
distantnative force-pushed the v6/feat/filelist-field branch 2 times, most recently from 8c6fe96 to e9d9d25 Compare August 13, 2026 20:29
@distantnative
distantnative force-pushed the v6/feat/filelist-field branch from e9d9d25 to f810f5c Compare August 13, 2026 20:30
@distantnative
distantnative merged commit 3584437 into v6/section-to-fields Aug 13, 2026
12 of 14 checks passed
@distantnative
distantnative deleted the v6/feat/filelist-field branch August 13, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants