cms icon indicating copy to clipboard operation
cms copied to clipboard

[4.x] HTML Fieldtype Magic 🪄 Helper

Open jacksleight opened this issue 1 year ago • 11 comments

This PR adds Alpine magic helpers that can be used inside an HTML fieldtype’s code to access the publish store and field component APIs. These allows you to really easily add dynamic elements to publish forms right in the blueprint/fieldset without the need to build a custom fieldtype.

Here’s a quick video with some examples:

https://github.com/statamic/cms/assets/126740/915f2d39-42fc-4b43-b6f2-68be5f174e22

This is the HTML for each example, which is set in the HTML fieldtype’s config:

<div
  class="border border-gray-200 p-4 rounded flex items-center"
  style="height: 114px"
  x-data="{ store: $useStore() }">
  <img
    class="rounded w-40 object-cover mr-4 shrink-0" 
    x-bind:src="store.meta.image.data[0].thumbnail">
  <div>
    <h3
      class="font-semibold"
      x-text="store.values.title"></h3>
    <p
      class="text-sm text-gray-700"
      x-text="store.values.description"></p>
  </div>
</div>
<button
  class="btn w-full"
  x-data="{ store: $useStore() }"
  x-on:click="null;
    const best = store.values.best_thing;
    const second_best = store.values.second_best_thing;
    store.setFieldValue(`best_thing`, second_best);
    store.setFieldValue(`second_best_thing`, best);
  ">
  🤔 … Swap Them!
</button>
<!-- Ignore the `null;`, Alpine doesn't like expressions that start with a line break -->
<input
  type="text"
  class="input-text text-center h-auto"
  style="font-size: 10rem"
  x-data="{ field: $useField() }"
  x-model="field.value"
  x-on:input="field.update(field.value)" />

I think previews and small utilities are a great use-case for this. Turning an HTML fieldtype into an actual input will be very limited (no processing/augmentation), but it does work and could still be useful for simple things.

There are two helpers available which return the following props/methods:

  • $useStore()
    • values (reactive)
    • meta (reactive)
    • errors (reactive)
    • setFieldValue(handle, value)
    • setFieldMeta(handle, value)
  • $useField()
    • value (reactive)
    • fieldPathPrefix (reactive)
    • update(value)
    • updateDebounced(value)
    • updateMeta(meta)

Closes https://github.com/statamic/ideas/issues/799

jacksleight avatar Nov 10 '23 17:11 jacksleight

Very magical. Love this.

ryanmitchell avatar Nov 10 '23 18:11 ryanmitchell

Made draft because I want to do a bit more testing and possibly refactoring, particularly around usage in sets. But thoughts welcome!

jacksleight avatar Nov 10 '23 19:11 jacksleight

It's a really great approach to improving the UX of publish form! Thank you ! Looking at your PR, I'm thinking that this would probably also be the best approach for improving the visibility of replicator sets. As soon as you go beyond 4-5 sets, it becomes unreadable for customers and even for us, so I regularly think about how to implement something more visual for the editor. The only ideas that came to me were to create a completely custom publish form. But with what you're proposing, I'm thinking that if we could add a default display mode to a replicator set with a config to handle a html field like your PR on the fieldset, this could take Statamic to an other level !

For example from this : image

To this : image

jeremyvienney avatar Nov 11 '23 13:11 jeremyvienney

Oh yeah that’s a cool idea, basically a way to use the same approach to specify custom set preview HTML. I'd need to make this a bit more general purpose, so it could also be used outside of a field, but that should be possible. I’ll give it some thought!

jacksleight avatar Nov 11 '23 15:11 jacksleight

I think so too, probably the simpliest approach to enhance the builder while keeping total freedom from user side directly in production without needed to rebuild custom vue components. Not so much time from my side to test it in the next days but I'd love to give it a try from your PR next week.

jeremyvienney avatar Nov 11 '23 16:11 jeremyvienney

This is looks really cool. Might be a nice way to build out a search engine preview snippet.

robdekort avatar Nov 11 '23 20:11 robdekort

@robdekort As in custom previews in the global search?

Screenshot 2023-11-13 at 09 16 27

jacksleight avatar Nov 13 '23 09:11 jacksleight

@robdekort Oh yeah, as custom previews in the global search?

Screenshot 2023-11-13 at 09 16 27

I was thinking about a preview field referencing the favicon, page title and meta description. Without the need for a custom fieldtype. Screenshot 2023-11-13 at 10 18 28

robdekort avatar Nov 13 '23 09:11 robdekort

@robdekort Ah gotcha, yeah that would be a perfect use-case for this đź‘Ť .

jacksleight avatar Nov 13 '23 09:11 jacksleight

@jacksleight Revisiting this because of a mention in Discord. Is this PR meant to still be a draft? It looks so polished in your video at the top.

andjsch avatar Mar 21 '24 16:03 andjsch

@AndreasSchantl It is, because I wanted to review how it works with fields in sets. At the moment you have to do a lot of extra work to read sibling set fields, and since that's probably a common use-case I wanted to simplify the process. I did start on a solution but got sidetracked, I will revisit this soon.

jacksleight avatar Mar 25 '24 11:03 jacksleight

Closing this for now.

I think it's a cool idea, but it needs more thought and there might be better ways to achieve the same things.

If you would like the helpers from this PR you can just drop this into your custom cp.js.

jacksleight avatar Jun 25 '24 11:06 jacksleight