cms icon indicating copy to clipboard operation
cms copied to clipboard

[5.x] Add field actions feature

Open jacksleight opened this issue 1 year ago • 1 comments

Summary

This PR adds a new feature called field actions.

Field actions provide a way to extend the functionality of existing fieldtypes without having to extend the whole class/component. They can be accessed via a new menu that appears above the field, which also provides a standard UI for common field features such as expand/collapse all and toggle fullscreen (these are referred to as internal actions).

Any action can be flagged as a quick action, which will make it appear in a simple popup menu on hover (exact UI still TBC).

Field actions are implemented entirely on the frontend in JS and should not be confused with entry/term etc. actions that run on the server. The implementation is currently intentionally very lightweight, but convenience helpers could be added in future (eg. to open a modal and prompt for user input).

Here's how they look:

actions

As well as fields this feature has also been incorporated into replicator and bard sets. While the focus of this PR is adding actions to fields, it's been implemented in a generic way so that the same functionality could be added to other component types in the future.

The actions are also available in fullscreen modes, which are now using a universal header component:

CleanShot 2024-09-19 at 14 46 44 CleanShot 2024-09-19 at 14 46 54 CleanShot 2024-09-19 at 14 47 00

Registering Actions

Field actions can be registered as follows. As a minimum you need to specify a display value and run callback. Quick actions also require an icon:

Statamic.$actions.add('text-fieldtype', {
    title: 'Reverse Text',
    run: ({ value, update }) => {
        update(value.split('').reverse().join(''));
    },
});

Statamic.$actions.add('table-fieldtype', {
    title: 'Import CSV',
    icon: 'add-table',
    quick: true,
    visible: () => {
        // check if visible
    },
    run: () => {
        // do something
    },
});

It's entirely up to you what you do inside the run callback. The callbacks currently receive a payload object as follows:

  • vm: The fieldtype Vue component
  • fieldPathPrefix: The field path prefix
  • handle: The field handle
  • value: The field value
  • config: The field config
  • meta: The field meta
  • update(value): The field update function
  • updateMeta(value): The field updateMeta function

And for Bard/Replicator sets:

  • vm: The set Vue component
  • fieldVm: The fieldtype Vue component
  • fieldPathPrefix: The field path prefix
  • index: The set index
  • values: The set values
  • config: The set config
  • meta: The set meta
  • update(handle, value): The set field update function
  • updateMeta(handle, value): The set field updateMeta function

Internal Actions

Fieldtype components themselves can add internal actions to the new dropdown/quick menus. This is currently used for existing expand/collapse all and toggle fullscreen features. These can be defined by adding an internalActions computed property to a fieldtype component:

computed: {
    internalActions() {
        return [
            {
                title: __('Expand All'),
                icon: 'arrows-horizontal-expand',
                quick: true,
                run: this.expandAll,
            },
            // ....
        ];
    },
},

Notes

  • There are currently three "types" of action: Actions are the ones registered through Statamic.$actions, these could also be considered Custom Actions. Internal Actions are the ones registered by fieldtypes themselves. Quick Actions are a subset of both Actions + Internal Actions and are the actions that appear in the hover-menu.
  • On the publish form the quick actions appear on hover, and they also appear in the dropdown, which makes sense since the dropdown covers up the hover-menu, and this ensures they're always accessible. In the fullscreen header there's space to always display the quick actions, but they also appear in the dropdown. This makes less sense, but I couldn't decide if it was worth filtering them out?
    • Worth pointing out that right now all internal actions are also quick actions, but they don't have to be, and it's possible to register custom quick actions as well.
  • At the moment the fullscreen mode toggle is just another action, but maybe it should have special status? It should really have a different "collapse" icon in fullscreen mode, but currently actions can only have one icon.

jacksleight avatar Jun 21 '24 15:06 jacksleight

This is now ready for review. There are a few implementation details and UI things that I'm still unsure about, but I'm hoping you can help figure out the final details.

jacksleight avatar Sep 20 '24 15:09 jacksleight

You're right about the Fullscreen icon though, we definitely need to solve the icon switching issue. Maybe we can have a FullscreenToggleAction that works more like an opt-in feature or mixin on the Fieldtype level, and fields can just say "has fullscreen" or something?

As a possible solution I've added the ability for the icon to be resolved via a callback that can check the state of the field. I've updated all the fullscreen toggles to use that (the close icon probably needs to be tweaked). There might be a better approach, but this is an option.

I'm also now wondering if these actions should have handles? Pretty much everything else in Statamic has handles, and giving each action a unique handle could be useful down the road... not sure though. Anyway you and Jason can review and decide.

jacksleight avatar Nov 06 '24 09:11 jacksleight

Heck yeah, thank you! That's a great solution IMO. The close icon is what we've been using already, so we'll leave it for now. UI refresh coming later :)

jackmcdade avatar Nov 06 '24 15:11 jackmcdade

~How were you adding actions to replicator/bard sets?~

Nevermind, I got it.

It wasStatamic.$actions.add('bard-fieldtype-set', {

jasonvarga avatar Nov 08 '24 18:11 jasonvarga

Ah whoops, left that out. Like this:

Statamic.$actions.add('replicator-fieldtype-set', {
    title: 'Translate Content',
    run: ({ update }) => {
        //
    },
});

Statamic.$actions.add('bard-fieldtype-set', {
    title: 'Generate Image',
    run: ({ update }) => {
        //
    },
});

The names come from the registered component names (this.$options.name).

jacksleight avatar Nov 08 '24 18:11 jacksleight

This is so cool Jack - Could the buttons be made reactive, i.e. a small icon above title turns red if some rules are triggered (i.e. a background check against an AI model). Obviously there would also need to be some logic that controls how often the check is triggered (field states, field content, time based, and so on).

sorentv2reg avatar Nov 23 '24 06:11 sorentv2reg

@sorentv2reg In theory yes, the icons can already be reactive based on the field state. But they only appear when you hover the menu so may not be that useful as an indicator.

jacksleight avatar Nov 23 '24 08:11 jacksleight

@jacksleight there seems to be a small bug when using a replicator inside a group. See screenshot below. The one in the group (above) shows the 3 dots below the description instead of to the right.

Screenshot 2024-11-26 at 16 07 03

geert-salamander avatar Nov 26 '24 15:11 geert-salamander

@geert-salamander That's been fixed in https://github.com/statamic/cms/pull/11165, not released yet.

jacksleight avatar Nov 26 '24 15:11 jacksleight