Actions API
Action
An Action is an encapsulation of a UI operation. In the ideal world an Action works on an injected context.
The context can be any object, but for pluggability context format is recommended
(See Context).
The action consumer is usually a view.
How to Export Action
Actions can be defined in the same way as views are defined in static plugins:
export const avid = {
actions: {
'av-my-plugin-action-first': actionFirstConfig,
'av-my-plugin-action-second': actionSecondConfig,
}
};
Action keys should be unique. Use namespacing for your action ids.
If you use Dependency API, to expose avid features:
export const avid = [
{
name: 'av-my-plugin-action-first',
provides: ['actions'],
create: () => actionFirstConfig,
},
{
name: 'av-my-plugin-action-second',
provides: ['actions'],
create: () => actionSecondConfig,
}];
Action Configuration
Minimum
A minimal action implementation must provide a text property. The text is a localized string which will be displayed by
ui presentation (Menu item name, toolbar tooltip). An action must also provide a “handler” function. The handler function implements the
actual operation which will be executed if the user presses the menu item or toolbar button.
{
text: "text",
handler: function() {
alert("HelloWorld);
}
}
Selection and Component Aware
The example above is a full functional action but it is not really useful. In most cases an action needs an execution context.
The execution context is mostly a selection (or collection of objects) provided by a ui component and component public API
(view publicScope).
{
getText({ selection, component }) {
return "text";
},
handler: function({ selection, component }) {
alert("Hello Selection: " + selection);
}
}
Note. View selection should have the same format as context (See Context).
Keyboard Shortcuts Binding to Action
When you bound keyboard shortcuts to actions, you will received event
data.
{
isEnabled({ event, selection, component }) {
return true;
},
handler: function({ event, selection, component }) {
alert("Hello Selection: " + selection);
}
}
Important. event
available in keyboard shortcuts only (see link).
Action Lifecycle
An action implementation like described before is just a template. This template will be used to create multiple instances
if needed (for every pane menu).
{
getText: function ({ selection, component }) {
//...
},
isEnabled: function({ selection, component }) {
return Ext.isArray(selection) && selection.length > 0;
},
isVisible: function({ selection, component }) {
//...
},
//only for type "checkitem"
isChecked: function ({ selection, component }) {
//...
},
handler: function({ selection, component }) {
alert("Hello Selection: " + selection['text/plain'] );
}
}
The actual lifecycle of an action instance is:
isEnabled -> getText -> isChecked -> isVisible -> handler (if user clicks item)
All methods except handler are optional.
If appropriate method is no defined, default values for action state are:
property | default value
---------------------------------
enabled | true
visible | true
text | Empty 'text' property
checked | false
Consumer Context
Each action instance is associated with a consumer component. The consumer component can be any object but is in the most
cases the View (precisely View publicScope).
Text
To define text for action item:
- property text for action (if text is known at the moment of action export)
- define method getText for action. This method can return string or a promise that fulfills with a string.
If getText promise is rejected then action item in menu will be disabled.
Note. It is recommended always define property text for action then if even getText
fail for some reason the item has predefined in property text.
If text is not defined then menu item has default text value Empty ‘text’ property.
Example. Define text in action method getText (allows access to localization)
{
text: "Create shortcut",
getText: function () {
return localize("shortcuts.action.create");
},
handler:function () {
...
}
});
Note. Text setting for action can be asynchronous.
The method getText can return a native promise that fulfills with a string.
Enable State
An Action might want to control if the given context is suitable for the implemented operations. The Action might be
disabled in case the context is not suitable. It can be defined immediately or in asynchronous way.
This behavior can be achieved by providing a “isEnabled” function.
Example
{
id: "org.mycompany.Helloworld",
text: "Hello World",
// action works with assets only
isEnabled: function({ selection } ) {
return selection["text/x.avid.asset-list"];
},
handler: function({ selection }) {
alert("Hello Selection: " + selection["text/x.avid.asset-list"] );
}
}
The action above will only be enabled in case the selection context conains assets.
The handler function must not validate the selection because it will only be called if “isEnabled” returns true.
Note: A context menu will filter out all disabled actions.
Note: isEnabled is called automatically before showing menu.
As noticed before it can be defined asynchronously if item is enabled.
For that purpose isEnabled should return native Promise.
Visibility
There 2 possibilities to control an action menu item visibility:
define method isVisible
define property keepIfEmpty (only for items with type menu )
Default value for visibility is equal true.
isVisible
An action might want to control if the menu item that corresponds to this action should be displayed.
It can depends on current selection, view state etc. It can be defined immediately or in asynchronous way.
Example. The Action should be hidden in case the context is not suitable.
This behavior can be achieved by providing method isVisible.
{
id: "org.mycompany.Helloworld",
text: "Hello World",
isVisible: function({ selection }) {
return !selection;
},
handler: function({ selection }) {
alert("Hello Selection: " + selection['text/plain'] );
}
}
The action above will only be visible in case the selection context is not empty.
Note: isVisible is called automatically before menu showing.
Note: isVisible can return a native promise if visibility should be defined asynchronously.
If isVisible promise is rejected then the action item will be visible but disabled.
See example for async isEnabled.
Menus
Usage
In order to group one more more items in a submenu, create an action with type: 'menu'
and the additional property createItems({ selection })
. The binding for the menu item follows the same rules as regular action items.
createItems( { selection } ) => [{actionDefinitionObject}]
This function should return an array of action definition objects. Can dynamically react to selection much like the handler
or isVisible
functions. The actions that are part of the menu don’t require separate bindings. If all actions are disabled or not visible, the menu will not be shown, see below.
keepIfEmpty
The property keepIfEmpty allows to show container items (items with submenus) even if they don’t have visible subitems.
In that case such item is shown in disabled state.
Example:
{
text: "Kept Empty Menu",
keepIfEmpty: true,
type: "menu",
createItems: [
actionDefinition, // an action that is not visible due to its own isVisible() check
]
}
Example. Action item with the property keepIfEmpty in view menu
Note. If the action has defined keepIfEmpty==true and isVisible the priority has isVisible method.
Action Binding
Use action bindings to associate action with appropriate consumer context (views).
Actions binding can be defined in the same way as actions are defined in static plugins:
export const avid = {
actionBindings: {
'av-my-plugin-action-binding': actionBindingConfig,
}
};
Action binding keys should be unique. Use namespacing for your action binding ids.
If you use Dependency API to expose avid features:
export const avid = [
{
name: 'av-my-plugin-action-binding',
provides: ['actionBindings'],
create: () => actionBindingConfig,
},
Action Binding Config
Config parameters that you need to pass is next:
places - constants which point type of menus where you can bind your action.
Possible places:
'contextMenu', 'viewMenu'
filter - is an Array or String (for single one) of view ids (viewTypes) where we want to bind menu
To bind menu for plugin with id static-plugin-example.example-view
filter: ["static-plugin-example.example-view"]
defaults
a position per item is of course a lot work and a maintenance hell (imagine to add a new action in the middle.
For that reason allows a action binding to specify a default index for all actions.defaults: {
index: 500,
},menuModelProvider - is a function that returns a menu model.
Menu model is an Array of items that contains Objects that represent menu item. In order to separate multiple items in the menu, add
{ type: 'separator' }
to add a horizontal line.If we want to bind actions with action ids named “com.avid.sent-to-email” and “com.avid.process-items” with a separator:
menuModelProvider: function() { return [ { actionId: "com.avid.sent-to-email", index: 500 }, { type: 'separator' }, { actionId: "com.avid.process-items", index: 600 } ]; }
Property index is used for action positioning in menu.
The model will be merged with all other plugins and the elements will be sorted by the given index.
Example
To summarize let’s get into a small example that shows how to binding action for tab menu may look like.
{
// places to bind action
places: [ 'contextMenu', 'viewMenu' ],
// filter by plugin id
filter: ["static-plugin-example.example-view", "av-ext4-example"],
// function that returns array that represents the menu model
menuModelProvider: function (viewPublic) {
// viewPublic method that are publicly provided by view
// depends on view
var text = viewPublic.name()
return [
{ type: 'separator' },
{
// Text that will be in menu. Your action will
// be named in menu by getting this property
text: text
//You need to know your Action id (*actionId*) that you want to bind
actionId: "com.avid.example.example-action-enabled",
},
binder.model.SEPARATOR
];
}
};