All files / triggers check-trigger.ts

96% Statements 24/25
83.33% Branches 10/12
100% Functions 9/9
95.65% Lines 22/23

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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171                94x   94x 94x       94x                                                                                                                                                                     94x       188x     4x                               7x 7x 7x   7x       7x   7x 3x     6x   4x   4x 1x                                       94x     4x   4x 1x        
/**
 * @module Triggers
 *
 * @categoryDescription Input
 * {@link CheckTrigger} allows you to run actions when the user checks a target
 * checkbox input element, and undo those actions when they uncheck the checkbox.
 */
 
import * as MH from "@lisn/globals/minification-helpers";
 
import { waitForReferenceElement } from "@lisn/utils/dom-search";
import { addEventListenerTo, removeEventListenerFrom } from "@lisn/utils/event";
 
import { Action } from "@lisn/actions/action";
 
import {
  registerTrigger,
  Trigger,
  TriggerConfig,
} from "@lisn/triggers/trigger";
 
import { WidgetConfigValidatorFunc } from "@lisn/widgets/widget";
 
/**
 * {@link CheckTrigger} allows you to run actions when the user checks a target
 * checkbox input element, and undo those actions when they uncheck the checkbox.
 *
 * -------
 *
 * To use with auto-widgets (HTML API), see {@link registerTrigger} for the
 * specification.
 *
 * - Arguments: none
 * - Additional trigger options (see {@link CheckTriggerConfig}:
 *   - `target`: A string element specification.
 *     See {@link Utils.getReferenceElement | getReferenceElement}.
 *
 * @example
 * Add classes `active` and `checked` when the user checks the checkbox,
 * remove them when unchecked.
 *
 * ```html
 * <input type="checkbox" data-lisn-on-check="@add-class: active,checked"/>
 * ```
 *
 * @example
 * As above, but using a CSS class instead of data attribute:
 *
 * ```html
 * <input type="checkbox" class="lisn-on-check--@add-class: active,checked"/>
 * ```
 *
 * @example
 * Play the animations on the element each time the user checks the next
 * element with class `checkbox` (do nothing when it's unchecked).
 *
 * ```html
 * <div data-lisn-on-check="@animate +one-way +target=next.checkbox"></div>
 * <input type="checkbox" class="checkbox"/>
 * ```
 *
 * @example
 * Add class `used` the first time the user checks the next element with class
 * `checkbox`, and play or reverse the animations 200ms after each time the
 * user toggles the reference checkbox.
 *
 * ```html
 * <div data-lisn-on-check="@add-class: used +once ;
 *                          @animate +delay=200 +target=next.checkbox"
 * ></div>
 * <input type="checkbox" class="checkbox"/>
 * ```
 *
 * @example
 * When the user checks the next element with class `checkbox` then add classes `c1`
 * and `c2` to the element (that the trigger is defined on) and enable trigger
 * `my-trigger` defined on this same element; undo all of that when the user unchecks
 * the reference checkbox.
 *
 * ```html
 * <div data-lisn-on-check="@add-class: c1,c2 @enable: my-trigger +target=next.checkbox"
 *      data-lisn-on-run="@show +id=my-trigger"
 * ></div>
 * <input type="checkbox" class="checkbox"/>
 * ```
 *
 * @example
 * As above, but using `data-lisn-ref` attribute instead of class selector.
 *
 * ```html
 * <div data-lisn-on-check="@add-class: c1,c2 @enable: my-trigger +target=next-checkbox"
 *      data-lisn-on-run="@show +id=my-trigger"
 * ></div>
 * <input type="checkbox" data-lisn-ref="checkbox"/>
 * ```
 *
 * @category Input
 */
export class CheckTrigger extends Trigger {
  readonly getConfig: () => CheckTriggerConfig;
 
  static register() {
    registerTrigger(
      "check",
      (element, args, actions, config) =>
        new CheckTrigger(element, actions, config),
      newConfigValidator,
    );
  }
 
  /**
   * If no actions are supplied, nothing is done.
   *
   * @throws {@link Errors.LisnUsageError | LisnUsageError}
   *                If the config is invalid.
   */
  constructor(
    element: Element,
    actions: Action[],
    config?: CheckTriggerConfig,
  ) {
    config ??= {};
    super(element, actions, config);
    this.getConfig = () => MH.copyObject(config);
 
    Iif (!MH.lengthOf(actions)) {
      return;
    }
 
    const target = MH.targetOf(config) ?? element;
 
    if (!MH.isInstanceOf(target, HTMLInputElement)) {
      return;
    }
 
    const onToggle = () => (target.checked ? this.run() : this.reverse());
 
    addEventListenerTo(target, "change", onToggle);
 
    this.onDestroy(() => {
      removeEventListenerFrom(target, "change", onToggle);
    });
  }
}
 
/**
 * @category Input
 * @interface
 */
export type CheckTriggerConfig = TriggerConfig & {
  /**
   * The target to use for the hover action.
   *
   * @defaultValue The element on which the {@link Trigger} is defined
   */
  target?: Element;
};
 
// --------------------
 
const newConfigValidator: WidgetConfigValidatorFunc<CheckTriggerConfig> = (
  element,
) => {
  return {
    target: (key, value) =>
      MH.isLiteralString(value)
        ? waitForReferenceElement(value, element).then((v) => v ?? undefined) // ugh, typescript...
        : undefined,
  };
};