Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | 94x 94x 94x 94x 3240x 1316x 1924x 98x 98x 98x 98x 55x 55x 29x 98x 1924x 94x 100x 100x 2x 98x 94x 94x | /**
* ## Specification for the HTML API for actions
*
* When using the HTML API, actions are always used with triggers. Please see
* {@link Triggers | the documentation on triggers} for the required syntax.
*
* @module Actions
*/
import * as MH from "@lisn/globals/minification-helpers";
import { splitOn } from "@lisn/utils/text";
import { WidgetConfigValidator, fetchWidgetConfig } from "@lisn/widgets/widget";
/**
* @interface
*/
export type Action = {
do: () => void;
undo: () => void;
toggle: () => void;
};
export type ActionCreateFn<Config extends Record<string, unknown>> = (
element: Element,
args: string[],
config?: Config,
) => Action | Promise<Action>;
/**
* Registers the given action so that it can be parsed by
* {@link Triggers.registerTrigger}.
*
* **IMPORTANT:** If an action by that name is already registered, the current
* call does nothing, even if the remaining arguments differ.
*
* @param name The name of the action. Should be in kebab-case.
* @param newAction Called for every action specification for a trigger
* parsed by {@link Triggers.registerTrigger}
*/
export const registerAction = <Config extends Record<string, unknown>>(
name: string,
newAction: ActionCreateFn<Config>,
configValidator?: null | WidgetConfigValidator<Config>,
) => {
if (registeredActions.has(name)) {
return;
}
const newActionFromSpec = async (
element: Element,
argsAndOptions: string,
) => {
const thisConfigValidator = MH.isFunction(configValidator)
? await configValidator(element)
: configValidator;
const args: string[] = [];
// In general, if an action accepts a boolean *option* (not argument), it
// may not be followed by a =value. So we pass the full string to the
// fetchWidgetConfig which will parse such boolean options if they are
// defined in the config validator.
const config = thisConfigValidator
? await fetchWidgetConfig(
argsAndOptions,
thisConfigValidator,
ARG_SEP_CHAR,
)
: undefined;
for (const entry of splitOn(argsAndOptions, ARG_SEP_CHAR, true)) {
if (entry) {
if (!MH.includes(entry, "=") && !(config && entry in config)) {
args.push(entry);
}
}
}
return newAction(element, args, config);
};
registeredActions.set(name, newActionFromSpec);
};
/**
* Returns an {@link Action} registered under the given name and instantiated
* with the given element and arguments and options parsed from the given string.
*
* @throws {@link Errors.LisnUsageError | LisnUsageError}
* If the given spec is not valid.
*/
export const fetchAction = async (
element: Element,
name: string,
argsAndOptions?: string,
): Promise<Action> => {
const newActionFromSpec = registeredActions.get(name);
if (!newActionFromSpec) {
throw MH.usageError(`Unknown action '${name}'`);
}
return await newActionFromSpec(element, argsAndOptions ?? "");
};
// --------------------
const ARG_SEP_CHAR = ",";
const registeredActions = MH.newMap<
string,
(element: Element, spec: string) => Action | Promise<Action>
>();
|