@coldwired/actions
Why?
Initial inspiration was turbo-stream, which allows
for the application of incremental changes to the page. The problem we faced was that applying
changes wiped out client changes and it was not always practical to propagate the client state
necessary for rendering to the server. We wanted to be able to preserve some state, such as open
dialogs an menus or input values.
How?
Actions
will create a MutationObserver
and a WeakMap
of some of the DOM state, such as class
names, aria attributes, and input values. This allows it to preserve state across morph changes. You
always have the possibility to force a state update through the attribute data-turbo-force
.
Usage
Action
An action is an object describing a DOM operation. Actions can be fully serialized to carry them
over the wire (turbo-stream).
type Action = {
action: 'after' | 'before' | 'append' | 'prepend' | 'replace' | 'update' | 'remove' | 'focus' | 'enable' | 'disable' | 'hide' | 'show';
targets: Element[] | string;
fragment?: DocumentFragment | string;
delay?: number;
pin?: boolean;
}
Setup
Before you start working with actions, you need to create and register an instance of Actions
.
After that, actions can be applied through the Actions
instance or dispatched as events. We also
provide an implementation of turbo-stream on top of
Actions
through the @coldwired/turbo-stream
package.
import { Actions } from '@coldwired/actions';
const actions = new Actions({ element: document.body });
actions.observe();
DOM manipulation
actions.after({ targets: '.item', fragment: '<p>Hello World</p>' });
actions.before({ targets: '.item', fragment: '<p>Hello World</p>' });
actions.append({ targets: '.item', fragment: '<p>Hello World</p>' });
actions.prepend({ targets: '.item', fragment: '<p>Hello World</p>' });
actions.replace({ targets: '.item', fragment: '<p>Hello World</p>' });
actions.update({ targets: '.item', fragment: '<p>Hello World</p>' });
actions.remove({ targets: '.item' });
actions.focus({ targets: '.item' });
actions.disable({ targets: '.item' });
actions.enable({ targets: '.item' });
actions.hide({ targets: '.item' });
actions.show({ targets: '.item' });
actions.applyActions([
{
action: 'update',
targets: '.item-to-update',
fragment: '<p>Hello World</p>'
},
{
action: 'remove',
targets: '.item-to-remove',
},
])
Dispatch from anywhere
If you want to dispatch actions from places where you don't have access to the Actions
instance,
you can use global API.
import * as Actions from '@coldwired/actions';
Actions.after({ targets: '.item', fragment: '<p>Hello World</p>' });
Actions.dispatchActions([
{
action: 'update',
targets: '.item-to-update',
fragment: '<p>Hello World</p>'
},
{
action: 'remove',
targets: '.item-to-remove',
},
]);
Delayed actions
You can add a delay to any action, which is useful for hiding flash messages after a short period of
time, for example.
actions.hide({ targets: '.item', delay: 2000 });
Pinned actions
An action can be pinned — this is mostly useful in combination with full-page morph.
actions.append({ targets: '.warnings', fragment: '<p>Warning !</p>', pin: true });
actions.morph(document, newDocument);
actions.applyPinnedActions();
actions.reset();