UNPKG

lisn.js

Version:

Simply handle user gestures and actions. Includes widgets.

246 lines (240 loc) 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ViewTrigger = void 0; var MC = _interopRequireWildcard(require("../globals/minification-constants.cjs")); var MH = _interopRequireWildcard(require("../globals/minification-helpers.cjs")); var _cssAlter = require("../utils/css-alter.cjs"); var _domAlter = require("../utils/dom-alter.cjs"); var _domSearch = require("../utils/dom-search.cjs"); var _text = require("../utils/text.cjs"); var _validation = require("../utils/validation.cjs"); var _views = require("../utils/views.cjs"); var _animate = require("../actions/animate.cjs"); var _animatePlay = require("../actions/animate-play.cjs"); var _viewWatcher = require("../watchers/view-watcher.cjs"); var _trigger = require("./trigger.cjs"); var _debug = _interopRequireDefault(require("../debug/debug.cjs")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /** * @module Triggers * * @categoryDescription View * {@link ViewTrigger} allows you to run actions when the viewport's scroll * position relative to a given target or offset from top/bottom/left/right is * one of the matching "views" (at/above/below/left/right), and undo those * actions when the viewport's "view" is not matching. */ /** * {@link ViewTrigger} allows you to run actions when the viewport's scroll * position relative to a given target or offset from top/bottom/left/right is * one of the matching "views" (at/above/below/left/right), and undo those * actions when the viewport's "view" is not matching. * * ------- * * To use with auto-widgets (HTML API), see {@link registerTrigger} for the * specification. * * - Arguments (optional): One or more (comma-separated) {@link View}s. * Default is "at". * - Additional trigger options: * - `target`: A string element specification for an element (see * {@link Utils.getReferenceElement | getReferenceElement}) or a * {@link Types.ScrollOffset | ScrollOffset}. * - `root`: A string element specification. See * {@link Utils.getReferenceElement | getReferenceElement}. * - `rootMargin`: A string. * - `threshold`: A number or list (comma-separated) of numbers. * * @example * Show the element when it's in the viewport, hide otherwise. * * ```html * <div data-lisn-on-view="at @show"></div> * ``` * * @example * Same as above. "views" is optional and defaults to "at". * * ```html * <div data-lisn-on-view="@show"></div> * ``` * * @example * As above but using a CSS class instead of data attribute: * * ```html * <div class="lisn-on-view--@show"></div> * ``` * * @example * Show the element 1000ms after the first time it enters the viewport. * * ```html * <div data-lisn-on-view="@show +once +delay=1000"></div> * ``` * * @example * Add class `seen` the first time the element enters the viewport, and play * the animations defined on it 1000ms after each time it enters the viewport, * reverse the animations as soon as it goes out of view. * * ```html * <div data-lisn-on-view="@add-class=seen +once ; * @animate +do-delay=1000" * ></div> * ``` * * @example * Add class `seen` when the viewport is at or below the element (element * above viewport), remove it when the viewport is above the element. * Element going to the left or right of the viewport will not trigger the * action. See {@link getOppositeViews}: * * ```html * <div data-lisn-on-view="at,below @add-class=seen"></div> * ``` * * @example * Add class `cls` when the viewport is above or to the left the element * (element below or to the right of the viewport), remove it when the * viewport is either at, below or to the right of the element. * * ```html * <div data-lisn-on-view="above,left @add-class=cls"></div> * ``` * * @example * Hide the element when the viewport is above the next element with class * `section`, show it when the viewport is below or at the target element. * * ```html * <div data-lisn-on-view="above @hide +target=next.section"></div> * <div class="section"></div> * ``` * * @example * As above, but using `data-lisn-ref` attribute instead of class selector. * * ```html * <div data-lisn-on-view="above @hide +target=next-section"></div> * <div data-lisn-ref="section"></div> * ``` * * @example * Open the {@link Widgets.Openable | Openable} widget configured for this * element when the viewport is 75% down from the top of the page. * * ```html * <div data-lisn-on-view="@open +target=top:75%"></div> * ``` * * @category View */ class ViewTrigger extends _trigger.Trigger { static register() { (0, _trigger.registerTrigger)("view", (element, args, actions, config) => { return new ViewTrigger(element, actions, MH.assign(config, { views: (0, _validation.validateStrList)("views", args, _views.isValidView) })); }, newConfigValidator); } /** * If no actions are supplied, nothing is done. * * @throws {@link Errors.LisnUsageError | LisnUsageError} * If the config is invalid. */ constructor(element, actions, config) { var _config$rootMargin, _config$target; super(element, actions, config); _defineProperty(this, "getConfig", void 0); const logger = _debug.default ? new _debug.default.Logger({ name: `ViewTrigger-${(0, _text.formatAsString)(element)}` }) : null; this.getConfig = () => MH.copyObject(config); if (!MH.lengthOf(actions)) { return; } const watcher = _viewWatcher.ViewWatcher.reuse({ root: config === null || config === void 0 ? void 0 : config.root, rootMargin: config === null || config === void 0 || (_config$rootMargin = config.rootMargin) === null || _config$rootMargin === void 0 ? void 0 : _config$rootMargin.replace(/,/g, " "), threshold: config === null || config === void 0 ? void 0 : config.threshold }); const target = (_config$target = config === null || config === void 0 ? void 0 : config.target) !== null && _config$target !== void 0 ? _config$target : element; const views = (config === null || config === void 0 ? void 0 : config.views) || MC.S_AT; const oppositeViews = (0, _views.getOppositeViews)(views); const setupWatcher = target => { if (!MH.lengthOf(oppositeViews)) { debug: logger === null || logger === void 0 || logger.debug6("Trigger can never be reversed, running now"); // The action is never undone this.run(); } else { debug: logger === null || logger === void 0 || logger.debug6("Setting up trigger", views, oppositeViews); watcher.onView(target, this.run, { views }); watcher.onView(target, this.reverse, { views: oppositeViews }); } }; // See comment in globals/settings under contentWrappingAllowed let willAnimate = false; for (const action of actions) { if (MH.isInstanceOf(action, _animate.Animate) || MH.isInstanceOf(action, _animatePlay.AnimatePlay)) { willAnimate = true; break; } } if (willAnimate) { setupRepresentative(element).then(setupWatcher); } else { setupWatcher(target); } } } /** * @category View * @interface */ exports.ViewTrigger = ViewTrigger; // ---------- const newConfigValidator = element => { return { target: (key, value) => { var _ref; return MH.isLiteralString(value) && (0, _views.isValidScrollOffset)(value) ? value : (_ref = MH.isLiteralString(value) ? (0, _domSearch.waitForReferenceElement)(value, element) : null) !== null && _ref !== void 0 ? _ref : undefined; }, root: (key, value) => { var _ref2; return (_ref2 = MH.isLiteralString(value) ? (0, _domSearch.waitForReferenceElement)(value, element) : null) !== null && _ref2 !== void 0 ? _ref2 : undefined; }, rootMargin: _validation.validateString, threshold: (key, value) => (0, _validation.validateNumList)(key, value) }; }; const setupRepresentative = async element => { let target = await (0, _domAlter.tryWrap)(element); if (!target) { // Not allowed to wrap. Create a dummy hidden clone that's not animated and // position it absolutely in a wrapper of size 0 that's inserted just // before the actual element, so that the hidden clone overlaps the actual // element's regular (pre-transformed) position. const prev = element.previousElementSibling; const prevChild = MH.childrenOf(prev)[0]; if (prev && (0, _cssAlter.hasClass)(prev, MC.PREFIX_WRAPPER) && prevChild && (0, _cssAlter.hasClass)(prevChild, MC.PREFIX_GHOST)) { // Already cloned by a previous animate action? target = prevChild; } else { target = (await (0, _domAlter.insertGhostClone)(element))._clone; } } return target; }; //# sourceMappingURL=view-trigger.cjs.map