All files / widgets track-scroll.ts

95% Statements 19/20
100% Branches 2/2
100% Functions 5/5
94.73% Lines 18/19

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        94x   94x   94x   94x                                                                     94x   8x 8x 4x   4x       94x     3x 3x                 5x   5x                   5x                                                     94x     94x     94x        
/**
 * @module Widgets
 */
 
import * as MH from "@lisn/globals/minification-helpers";
 
import { validateNumber } from "@lisn/utils/validation";
 
import { ScrollWatcher } from "@lisn/watchers/scroll-watcher";
 
import {
  Widget,
  WidgetConfigValidatorObject,
  registerWidget,
} from "@lisn/widgets/widget";
 
/**
 * This is a simple wrapper around the {@link ScrollWatcher}. If you are using
 * the JavaScript API, you should use the {@link ScrollWatcher} directly. The
 * purpose of this widget is to expose the watcher's ability to track scroll
 * and set relevant CSS properties via the HTML API. See
 * {@link ScrollWatcher.trackScroll}.
 *
 * -----
 *
 * To use with auto-widgets (HTML API) (see
 * {@link Settings.settings.autoWidgets | settings.autoWidgets}), the following
 * CSS classes or data attributes are recognized:
 * - `lisn-track-scroll` class or `data-lisn-track-scroll` attribute set on
 *   the element that constitutes the widget.
 *
 * @example
 * This will track scroll on this element and set the relevant CSS properties.
 *
 * ```html
 * <div class="lisn-track-scroll"></div>
 * ```
 *
 * @example
 * As above but with custom options
 *
 * ```html
 * <div data-lisn-track-scroll="threshold=0 | debounce-window=0"></div>
 * ```
 */
export class TrackScroll extends Widget {
  static get(element: Element): TrackScroll | null {
    const instance = super.get(element, DUMMY_ID);
    if (MH.isInstanceOf(instance, TrackScroll)) {
      return instance;
    }
    return null;
  }
 
  static register() {
    registerWidget(
      WIDGET_NAME,
      (element, config) => {
        if (!TrackScroll.get(element)) {
          return new TrackScroll(element, config);
        }
        return null;
      },
      configValidator,
    );
  }
 
  constructor(element: Element, config?: TrackScrollConfig) {
    super(element, { id: DUMMY_ID });
 
    ScrollWatcher.reuse().trackScroll(
      null,
      MH.assign(
        {
          scrollable: element,
        },
        config,
      ),
    );
 
    this.onDestroy(() => ScrollWatcher.reuse().noTrackScroll(null, element));
  }
}
 
/**
 * @interface
 */
export type TrackScrollConfig = {
  /**
   * Corresponds to
   * {@link Watchers/ScrollWatcher.OnScrollOptions.threshold | OnScrollOptions.threshold}.
   *
   * @defaultValue undefined // ScrollWatcher default
   */
  threshold?: number;
 
  /**
   * Corresponds to
   * {@link Watchers/ScrollWatcher.OnScrollOptions.debounceWindow | OnScrollOptions.debounceWindow}.
   *
   * @defaultValue undefined // ScrollWatcher default
   */
  debounceWindow?: number;
};
 
// --------------------
 
const WIDGET_NAME = "track-scroll";
// Only one TrackScroll widget per element is allowed, but Widget requires a
// non-blank ID.
const DUMMY_ID = WIDGET_NAME;
 
// For HTML API only
const configValidator: WidgetConfigValidatorObject<TrackScrollConfig> = {
  threshold: validateNumber,
  debounceWindow: validateNumber,
};