UNPKG

@ssv/ngx.command

Version:

Command pattern implementation for angular. Command used to encapsulate information which is needed to perform an action.

229 lines (219 loc) 11.5 kB
import * as _angular_core from '@angular/core'; import { Provider, InjectionToken, Signal, Injector, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { AbstractControl } from '@angular/forms'; interface CommandOptions { /** * Css Class which gets added/removed on the Command element's host while Command `isExecuting$`. */ executingCssClass: string; /** Determines whether the disabled will be handled by the directive or not. * Disable handled by directive's doesn't always play nice when used with other component/pipe/directive and they also handle disabled. * This disables the handling manually and need to pass explicitly `[disabled]="!saveCmd.canExecute"`. */ handleDisabled: boolean; } declare const COMMAND_OPTIONS: InjectionToken<CommandOptions>; declare function provideSsvCommandOptions(options: Partial<CommandOptions> | ((defaults: Readonly<CommandOptions>) => Partial<CommandOptions>)): Provider[]; /** Type that represents a sync or async value. */ type MaybeAsync<T> = T | Observable<T> | Promise<T>; type SignalLike<T> = (() => T); /** Converts Observable<T> to Promise<T>, leaves other types unchanged */ type ConvertObservableToPromise<T> = T extends Observable<infer U> ? Promise<U> : T; /** Return type of Execute function, converting `Observable` to `Promise` if needed */ type ExecuteReturnType<TExecute extends ExecuteFn> = ConvertObservableToPromise<ReturnType<TExecute>>; type ExecuteFn<TArgs extends any[] = any[], TReturn = unknown> = (...args: TArgs) => MaybeAsync<TReturn>; type CanExecute = SignalLike<boolean> | Signal<boolean> | Observable<boolean> | boolean; /** * Type for command parameters that allows: * - Single parameter passed directly (not in array) * - Multiple parameters as tuple array * - Single array parameter wrapped in array to prevent spreading */ type CommandParams<TExec extends ExecuteFn> = Parameters<TExec> extends [infer Single] ? Single | [Single] : Parameters<TExec> extends [] ? never : Parameters<TExec>; /** `command` input type convenience. * @example * For a command with a single parameter: * ```ts * readonly myCmd = input.required<CommandInput<MyType>>(); * ``` * For a command with multiple parameters: * ```ts * readonly myCmd = input.required<CommandInput<[param1: string, param2: number]>>(); * ``` */ type CommandInput<TArgs = unknown, R = unknown> = TArgs extends readonly [unknown, ...unknown[]] ? Command<(...args: TArgs) => R> : TArgs extends readonly [infer Single] ? Command<(arg: Single) => R> : Command<(arg: TArgs) => R>; interface ICommand<TExecute extends ExecuteFn = ExecuteFn> { /** Determines whether the command is currently executing, as a snapshot value. * @deprecated Use {@link $isExecuting} signal instead. */ readonly isExecuting: boolean; /** Determines whether the command is currently executing, as a signal. */ readonly $isExecuting: Signal<boolean>; /** Determines whether the command can execute or not, as a snapshot value. * @deprecated Use {@link $canExecute} signal instead. */ readonly canExecute: boolean; /** Determines whether the command can execute or not, as a signal. */ readonly $canExecute: Signal<boolean>; /** Execute function to invoke. Returns Promise if the execute function returns Observable, otherwise returns the original type. */ execute(...args: Parameters<TExecute>): ExecuteReturnType<TExecute>; } interface CommandCreator<TExecute extends ExecuteFn = ExecuteFn> { /** Execute function to invoke. */ execute: TExecute; /** Determines whether the command can execute or not. Can be a signal, observable, or function. */ canExecute?: CanExecute | ((...args: Parameters<TExecute>) => CanExecute); /** Parameters to pass to the execute function. */ params?: CommandParams<TExecute>; /** Host context for binding the execute function. */ host: unknown; } interface CommandCreateOptions { injector?: Injector; } /** Creates an async {@link Command}. Must be used within an injection context. * @deprecated Use {@link command} instead, as it handles both sync and async execute functions. */ declare const commandAsync: typeof command; /** Creates a {@link Command}. Must be used within an injection context (or the injector must be provided in the options). */ declare function command<TExecute extends ExecuteFn>(execute: TExecute, canExecute?: CanExecute, opts?: CommandCreateOptions): Command<TExecute>; /** * Command object used to encapsulate information which is needed to perform an action. */ declare class Command<TExecute extends ExecuteFn = ExecuteFn> implements ICommand<TExecute> { #private; private readonly _execute; get isExecuting(): boolean; get canExecute(): boolean; readonly $isExecuting: _angular_core.WritableSignal<boolean>; readonly $canExecute: Signal<boolean>; /** * Creates an instance of Command. * * @param execute Execute function to invoke. * @param canExecute Observable which determines whether it can execute or not. * @deprecated Use {@link command} or {@link commandAsync} instead for creating instances. */ constructor(_execute: TExecute, canExecute?: CanExecute, injector?: Injector); /** Execute function to invoke. Returns Promise if the execute function returns Observable, otherwise returns the original type. */ execute(...args: Parameters<TExecute>): ExecuteReturnType<TExecute>; } /** Helper type to extract ExecuteFn from ICommand/Command or use ExecuteFn directly */ type ExtractExecuteFn<T> = T extends Command<infer TExec> ? TExec : T extends ICommand<infer TExec> ? TExec : T extends ExecuteFn ? T : never; /** * Controls the state of a component in sync with `Command`. * * @example * ### Most common usage * ```html * <button [ssvCommand]="saveCmd">Save</button> * ``` * * * ### Usage with options * ```html * <button [ssvCommand]="saveCmd" [ssvCommandOptions]="{executingCssClass: 'in-progress'}">Save</button> * ``` * * * ### Usage with params * This is useful for collections (loops) or using multiple actions with different args. * *NOTE: This will share the `isExecuting` when used with multiple controls.* * * #### With single param (direct) * * ```html * <button [ssvCommand]="saveCmd" [ssvCommandParams]="hero">Save</button> * ``` * * #### With single param (array) * * ```html * <button [ssvCommand]="saveCmd" [ssvCommandParams]="[hero]">Save</button> * ``` * * *NOTE: if you have only 1 argument as an array, it should be enclosed within an array e.g. `[['apple', 'banana']]`, * else it will spread and you will get `arg1: "apple", arg2: "banana"`* * * #### With multi params * ```html * <button [ssvCommand]="saveCmd" [ssvCommandParams]="[{id: 1}, 'hello', hero]">Save</button> * ``` * * ### Usage with Command Creator * This is useful for collections (loops) or using multiple actions with different args, whilst not sharing `isExecuting`. * * * ```html * <button [ssvCommand]="{host: this, execute: removeHero$, canExecute: isValid$, params: [hero, 1337, 'xx']}">Save</button> * ``` * */ declare class SsvCommand<T extends ICommand | ExecuteFn = ExecuteFn> implements OnInit { #private; readonly commandOrCreator: _angular_core.InputSignal<T extends ICommand<ExecuteFn<any[], unknown>> ? T : ICommand<ExtractExecuteFn<T>> | CommandCreator<ExtractExecuteFn<T>>>; readonly ssvCommandOptions: _angular_core.InputSignal<Partial<CommandOptions>>; readonly commandOptions: _angular_core.Signal<CommandOptions>; readonly ssvCommandParams: _angular_core.InputSignal<(CommandParams<ExtractExecuteFn<T>> extends infer T_1 ? { [KeyType in keyof T_1]: T_1[KeyType]; } : never) | undefined>; readonly commandParams: _angular_core.Signal<Parameters<ExtractExecuteFn<T>> | undefined>; readonly _hostClasses: _angular_core.Signal<string[]>; readonly command: _angular_core.Signal<ICommand<ExtractExecuteFn<T>>>; constructor(); ngOnInit(): void; _handleClick(): void; private trySetDisabled; static ɵfac: _angular_core.ɵɵFactoryDeclaration<SsvCommand<any>, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SsvCommand<any>, "[ssvCommand]", ["ssvCommand"], { "commandOrCreator": { "alias": "ssvCommand"; "required": true; "isSignal": true; }; "ssvCommandOptions": { "alias": "ssvCommandOptions"; "required": false; "isSignal": true; }; "ssvCommandParams": { "alias": "ssvCommandParams"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>; } /** * Command creator ref, directive which allows creating Command in the template * and associate it to a command (in order to share executions). * @example * ### Most common usage * ```html * <div #actionCmd="ssvCommandRef" [ssvCommandRef]="{host: this, execute: removeHero$, canExecute: isValid$}"> * <button [ssvCommand]="actionCmd.command()" [ssvCommandParams]="[hero]"> * Remove * </button> * <button [ssvCommand]="actionCmd.command()" [ssvCommandParams]="[hero]"> * Remove * </button> * </div> * ``` * */ declare class SsvCommandRef<TExecute extends ExecuteFn = ExecuteFn> implements OnInit { #private; readonly commandCreator: _angular_core.InputSignal<CommandCreator<TExecute>>; readonly command: _angular_core.Signal<ICommand<TExecute>>; ngOnInit(): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration<SsvCommandRef<any>, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SsvCommandRef<any>, "[ssvCommandRef]", ["ssvCommandRef"], { "commandCreator": { "alias": "ssvCommandRef"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>; } /** @deprecated Use standalone instead. */ declare class SsvCommandModule { static ɵfac: _angular_core.ɵɵFactoryDeclaration<SsvCommandModule, never>; static ɵmod: _angular_core.ɵɵNgModuleDeclaration<SsvCommandModule, never, [typeof SsvCommand, typeof SsvCommandRef], [typeof SsvCommand, typeof SsvCommandRef]>; static ɵinj: _angular_core.ɵɵInjectorDeclaration<SsvCommandModule>; } /** Determines whether the arg object is of type `Command`. */ declare function isCommand<T extends ICommand>(arg: unknown | T): arg is T; /** Determines whether the arg object is of type `CommandCreator`. */ declare function isCommandCreator<T extends CommandCreator>(arg: unknown | T): arg is T; interface CanExecuteFormOptions { /** Determines whether to check for validity. (defaults: true) */ validity?: boolean; /** Determines whether to check whether UI has been touched. (defaults: true) */ dirty?: boolean; } /** Get can execute from form validity/pristine as an observable. */ declare function canExecuteFromNgForm(form: AbstractControl, options?: CanExecuteFormOptions): Observable<boolean>; /** Can executed based on valid/dirty signal inputs. */ declare function canExecuteFromSignals(signals: { valid: Signal<boolean>; dirty: Signal<boolean>; }, options?: CanExecuteFormOptions): Signal<boolean>; declare const VERSION = "5.1.0"; export { COMMAND_OPTIONS, Command, SsvCommand, SsvCommandModule, SsvCommandRef, VERSION, canExecuteFromNgForm, canExecuteFromSignals, command, commandAsync, isCommand, isCommandCreator, provideSsvCommandOptions }; export type { CanExecute, CanExecuteFormOptions, CommandCreateOptions, CommandCreator, CommandInput, CommandOptions, ExecuteFn, ICommand };