@barguide/react-hooks
Version:
TypeScript | React Hooks
204 lines (190 loc) • 7.2 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('events'), require('lodash/throttle')) :
typeof define === 'function' && define.amd ? define(['exports', 'react', 'events', 'lodash/throttle'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Component = {}, global.React, global.events, global.throttle));
})(this, (function (exports, react, events, throttle) { 'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var throttle__default = /*#__PURE__*/_interopDefaultLegacy(throttle);
/**
* @external https://nodejs.org/api/events.html
* @description Much of the Node.js core API is built around an idiomatic
* asynchronous event-driven architecture in which certain kinds of objects
* (called "emitters") emit named events that cause Function objects
* ("listeners") to be called.
*/
const emitter = new events.EventEmitter();
/**
* @name useCheckoutLoader
* @description We use the "effect" hook to add/remove an "emitter" instance
* to our Modal component. By passing the "setter" from our React State we
* can get a live subscription to the state state
*/
const useEmitter = (event, callback, initialValue = false) => {
const [state, setState] = react.useState(initialValue);
const onEventCallback = (value = !state) => {
setState(value);
if (typeof callback === 'function')
callback(value);
};
react.useEffect(() => {
emitter.addListener(event, onEventCallback);
return () => {
emitter.removeListener(event, onEventCallback);
};
});
return [state, setState];
};
/**
* @name useFormCheckbox
* @description A convenience hook for working with text inputs of type checkbox
*/
const useFormCheckbox = (initialValue = false) => {
// Hooks
const [value, setValue] = react.useState(initialValue);
// Handlers
const onChange = (event) => {
setValue(event.target.checked);
};
return { onChange, setValue, value };
};
/**
* @name useFormInput
* @description A convenience hook for working with text inputs
*/
const useFormInput = (initialValue = '') => {
// Hooks
const [value, setValue] = react.useState(initialValue);
// Handlers
const onChange = (event) => {
setValue(event.target.value);
};
return { onChange, setValue, value };
};
/**
* @name useFormSelect
* @description A convenience hook for working with text inputs of type checkbox
*/
const useFormSelect = (values) => {
// Setup
const initialValue = values[0];
// Hooks
const [value, setValue] = react.useState(initialValue);
// Handlers
const onChange = (event) => {
setValue(event.target.value);
};
return { onChange, setValue, value };
};
/**
* @name useFormValidation
* @description Simple hook that takes a form element as a ref and returns
* the validity using HTML validation and the rules on the form inputs
*/
const useFormValidation = (formRef, dependencies) => {
// Hooks
const [valid, setValid] = react.useState(false);
// Setup
const { current } = formRef;
// Life Cycle
react.useEffect(() => {
if (!current)
return;
setValid(current.checkValidity());
}, dependencies);
return valid;
};
/**
* @name useScroll
* @description Pass this hook a callback to be fired in a
* throttled callback/handler
*/
const useScroll = (callback) => {
// Setup
const throttledCallback = throttle__default["default"](callback, 100, {
leading: true,
trailing: true
});
// Life Cycle
react.useEffect(() => {
window.addEventListener('scroll', throttledCallback);
callback();
return () => {
window.removeEventListener('scroll', throttledCallback);
};
}, []);
};
/**
* @name useCheckoutLoader
* @description We use the "effect" hook to add/remove an "emitter" instance
* to our Modal component. By passing the "setter" from our React State we
* can get a live subscription to the state
*/
const useScrollDirection = () => {
const threshhold = 30;
const triggerBottom = 100;
const triggerTop = 100;
const triggerDistance = 150;
const [bottom, setBottom] = react.useState(false);
const [direction, setDirection] = react.useState(false);
const [position, setPosition] = react.useState(0);
const [top, setTop] = react.useState(false);
const onScroll = (_event) => {
const root = document.documentElement;
const { clientHeight, scrollTop, scrollHeight } = root;
const remainder = scrollHeight - clientHeight - scrollTop;
const isBottom = remainder <= triggerBottom;
const isTop = scrollTop <= threshhold;
const isSignificant = Math.abs(scrollTop - position) > triggerDistance;
const nearBottom = remainder <= triggerBottom;
const nearTop = scrollTop <= triggerTop;
const isException = nearBottom || nearTop;
if (isException) {
if (nearTop && !top) {
setBottom(false);
setTop(true);
}
else if (nearBottom && !bottom) {
setBottom(true);
setTop(false);
}
return;
}
// Required travel distance
if (isSignificant) {
const isDownward = scrollTop > position;
// Edge-cases
if (top !== isTop)
setTop(isTop);
else if (bottom !== isBottom)
setBottom(isBottom);
// Account for negative scrolling (mobile)
setPosition(scrollTop <= 0 ? 0 : scrollTop);
setDirection(isDownward);
}
};
const throttledResize = throttle__default["default"](onScroll, 100, {
leading: true,
trailing: true
});
react.useEffect(() => {
window.addEventListener('scroll', throttledResize);
return () => {
window.removeEventListener('scroll', throttledResize);
};
});
return {
bottom,
direction,
position,
top
};
};
exports.useEmitter = useEmitter;
exports.useFormCheckbox = useFormCheckbox;
exports.useFormInput = useFormInput;
exports.useFormSelect = useFormSelect;
exports.useFormValidation = useFormValidation;
exports.useScroll = useScroll;
exports.useScrollDirection = useScrollDirection;
Object.defineProperty(exports, '__esModule', { value: true });
}));