1
0
mirror of https://github.com/locomotivemtl/locomotive-boilerplate.git synced 2026-01-15 00:55:08 +08:00

5 Commits

Author SHA1 Message Date
Jérémy Minié
4ff4f2fb9c Declare REGISTERED_CUSTOM_EVENTS as a const to avoid altering window global object 2022-06-22 17:07:39 -04:00
Jérémy Minié
cfa899639e Fix non-existing variables/functions calls 2022-06-22 17:06:39 -04:00
Lucas Vallenet
d916ad030d Update comments and functions / Blends scrollup and scrolldown to one function 2022-06-02 16:42:19 +02:00
Lucas Vallenet
b346b043a6 Improve tickers comments 2022-06-02 16:42:19 +02:00
Lucas Vallenet
99291b17f5 Create tickers and events utils 2022-06-02 16:26:29 +02:00
5 changed files with 265 additions and 3711 deletions

View File

@@ -1,15 +0,0 @@
export default function(func, wait, immediate) {
let timeout;
return function() {
const context = this;
const args = arguments;
const later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}

View File

@@ -0,0 +1,180 @@
import { debounce } from './tickers'
/**
* @typedef {object} RegisteredCustomEvent
*
* @property {EventTarget} target - The event target ({@see Window} or {@see Node}).
* @property {string} type - The custom event name.
*/
/** @type {RegisteredCustomEvent[]} */
const REGISTERED_CUSTOM_EVENTS = []
/**
* Determines if the given object is the {@see Window}.
*
* @param {object} obj
* @return {boolean}
*/
const isWindow = obj => obj === window
/**
* Determines if the given object implements {@see EventTarget}.
*
* @param {object} obj
* @return {boolean}
*/
const isEventTarget = obj => (obj instanceof EventTarget)
/**
* Determines if the target already has the event attached.
*
* @param {EventTarget} target
* @param {string} type - The custom event name.
* @return {boolean}
*/
const isCustomEventRegistered = (target, type) => REGISTERED_CUSTOM_EVENTS.some(e => e.target === target && e.type === type)
/**
* Registers the custom event with the given target, if not already registered.
*
* @param {EventTarget} target
* @param {string} type - The custom event name.
* @return {void}
*/
const addCustomEvent = (target, type) => {
if (!isCustomEventRegistered(target, type)) {
REGISTERED_CUSTOM_EVENTS.push({
target,
type
})
}
}
/**
* Adds a custom "start" event for the given target.
*
* Internally, this function adds a debounced event listener on
* the given `event` to trigger the custom `<event>start` event.
*
* @param {EventTarget} target
* @param {string} type - The base event name.
* @param {number} [delay] - The number of milliseconds to wait
* before dispatching the custom event.
* @throws Error If the target is invalid.
* @throws Error If the custom event is already defined.
* @return {void}
*/
const addStartEvent = (target, type, delay = 200) => {
const customType = `${type}start`
if (!isEventTarget(target)) {
throw new Error(`addStartEvent: target parameter must be an instance of EventTarget`)
}
if (isCustomEventRegistered(target, customType)) {
throw new Error(`addStartEvent: '${customType}' already exists for target parameter`)
}
addCustomEvent(target, customType)
const startEvent = new CustomEvent(customType)
target.addEventListener(type, debounce(() => {
target.dispatchEvent(startEvent)
}, delay, true))
}
/**
* Adds a custom "end" event for the given target.
*
* Internally, this function adds a debounced event listener on
* the given `event` to trigger the custom `<event>end` event.
*
* @param {EventTarget} target
* @param {string} type - The base event name.
* @param {number} [delay] - The number of milliseconds to wait
* before dispatching the custom event.
* @throws Error If the target is invalid.
* @throws Error If the custom event is already defined.
* @return {void}
*/
const addEndEvent = (target, type, delay = 200) => {
const customType = `${type}end`
if (!isEventTarget(target)) {
throw new Error(`addEndEvent: target parameter must be an instance of EventTarget`)
}
if (isCustomEventRegistered(target, customType)) {
throw new Error(`addEndEvent: '${customType}' already exists for target parameter`)
}
addCustomEvent(target, customType)
const endEvent = new CustomEvent(customType)
target.addEventListener(type, debounce(() => {
target.dispatchEvent(endEvent)
}, delay))
}
/**
* Adds custom scroll "up" and "down" events for the given target.
*
* Internally, this function adds an event listener on
* the scroll event to detect the direction and trigger
* the custom `scrollup` and `scrolldown` events.
*
* @param {EventTarget} [target] - If omitted, the custom event
* if attached to the Window.
* @throws Error If the target is invalid.
* @return {void}
*/
const addScrollDirectionEvents = (target = window) => {
if (!isEventTarget(target)) {
throw new Error(`addScrollDirectionEvents: target parameter must be an instance of EventTarget`)
}
let scrollTop = target.scrollTop
let previousScrollTop = scrollTop
let direction = 0
const scrollUp = new CustomEvent('scrollup')
const scrollDown = new CustomEvent('scrolldown')
const scrollProperty = isWindow(target) ? 'scrollY' : 'scrollTop'
target.addEventListener('scroll', () => {
scrollTop = target[scrollProperty]
// Scroll up
if (scrollTop < previousScrollTop && direction > -1) {
target.dispatchEvent(scrollUp)
direction = -1
// Scroll down
} else if (scrollTop > previousScrollTop && direction < 1) {
target.dispatchEvent(scrollDown)
direction = 1
}
previousScrollTop = scrollTop
})
}
export {
addStartEvent,
addEndEvent,
addScrollDirectionEvents,
}

View File

@@ -0,0 +1,78 @@
/**
* Creates a debounced function.
*
* A debounced function delays invoking `callback` until after
* `delay` milliseconds have elapsed since the last time the
* debounced function was invoked.
*
* Useful for behaviour that should only happen _before_ or
* _after_ an event has stopped occurring.
*
* @template {function} T
*
* @param {T} callback - The function to debounce.
* @param {number} delay - The number of milliseconds to wait.
* @param {boolean} [immediate] -
* If `true`, `callback` is invoked before `delay`.
* If `false`, `callback` is invoked after `delay`.
* @return {function<T>} The new debounced function.
*/
const debounce = (callback, delay, immediate = false) => {
let timeout = null
return (...args) => {
clearTimeout(timeout)
const later = () => {
timeout = null
if (!immediate) {
callback(...args)
}
}
if (immediate && !timeout) {
callback(...args)
}
timeout = setTimeout(later, delay)
}
}
/**
* Creates a throttled function.
*
* A throttled function invokes `callback` at most once per every
* `delay` milliseconds.
*
* Useful for rate-limiting an event that occurs in quick succession.
*
* @template {function} T
*
* @param {T} callback - The function to throttle.
* @param {number} delay - The number of milliseconds to wait.
* @return {function<T>} The new throttled function.
*/
const throttle = (callback, delay) => {
let timeout = false
return (...args) => {
if (!timeout) {
timeout = true
callback(...args)
setTimeout(() => {
timeout = false
}, delay)
}
}
}
export {
debounce,
throttle
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long