smol updates even mor
This commit is contained in:
parent
cde1e58a47
commit
b01f6444d0
51
modules/utils/createElementStatusModes.mjs
Normal file
51
modules/utils/createElementStatusModes.mjs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
//@ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Creates exclusive states for an HTML element. These states are added as classes
|
||||||
|
* and can be used to drive CSS changes.
|
||||||
|
* @param {string[]} allModes A string list of all possible modes. It's advised
|
||||||
|
* to prepend (`state-` or `mode-` to each for clarity)
|
||||||
|
* @param {Element} [element] the element to add the classes to. Defaults to the
|
||||||
|
* document's body
|
||||||
|
*/
|
||||||
|
export const createElementStatusModes = (allModes, element = document.body) => {
|
||||||
|
/**
|
||||||
|
* @param {any} mode
|
||||||
|
* @returns {mode is number}
|
||||||
|
*/
|
||||||
|
const isValidIndex = (mode) =>
|
||||||
|
typeof mode === "number" && mode >= 0 && mode < allModes.length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a status mode (class name) on the element.
|
||||||
|
* Pass a falsy value to clear all modes
|
||||||
|
* @param {number|null|undefined|false} mode
|
||||||
|
*/
|
||||||
|
const set = (mode = false) => {
|
||||||
|
mode = isValidIndex(mode) ? mode : -1;
|
||||||
|
const modeClass = allModes[mode];
|
||||||
|
if (modeClass && element.classList.contains(modeClass)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.classList.remove(...allModes);
|
||||||
|
element.classList.add(modeClass);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies which of the given classes is set.
|
||||||
|
* @returns {string|undefined} the class if there is one
|
||||||
|
*/
|
||||||
|
const get = () =>
|
||||||
|
allModes.find((className) => element.classList.contains(className));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} state
|
||||||
|
*/
|
||||||
|
const is = (state) =>
|
||||||
|
isValidIndex(state) && document.body.classList.contains(allModes[state]);
|
||||||
|
|
||||||
|
return { set, get, is };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createElementStatusModes;
|
@ -1,14 +0,0 @@
|
|||||||
//@ts-check
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a helper to add or remove global classes that begin with `is-`
|
|
||||||
* @param {string} name
|
|
||||||
*/
|
|
||||||
export const documentMode = (name) => ({
|
|
||||||
on: () => document.body.classList.add(`is-${name}`),
|
|
||||||
off: () => document.body.classList.remove(`is-${name}`),
|
|
||||||
toggle: () => document.body.classList.toggle(`is-${name}`),
|
|
||||||
has: () => document.body.classList.contains(`is-${name}`),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default documentMode;
|
|
@ -1,35 +1,16 @@
|
|||||||
//@ts-check
|
//@ts-check
|
||||||
|
import {createElementStatusModes} from "./createElementStatusModes.mjs"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a document state object that can toggle between exclusive states.
|
* Creates a document state object that can toggle between exclusive states.
|
||||||
* All passed states' css classnames will be prepended with `mode-`.
|
* All passed states' css classnames will be prepended with `mode-`.
|
||||||
|
* @see {createElementStatusModes}
|
||||||
* @param {string[]} states
|
* @param {string[]} states
|
||||||
*/
|
*/
|
||||||
export const documentState = (states) => {
|
export const documentState = (states) => {
|
||||||
const all = states.map((state) => `mode-${state}`);
|
const all = states.map((state) => `mode-${state}`);
|
||||||
|
|
||||||
/**
|
return createElementStatusModes(all, document.body)
|
||||||
* @param {any} state
|
|
||||||
* @returns {state is number}
|
|
||||||
*/
|
|
||||||
const isValidIndex = (state) =>
|
|
||||||
typeof state === "number" && state >= 0 && state < all.length;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} state
|
|
||||||
*/
|
|
||||||
const is = (state) =>
|
|
||||||
isValidIndex(state) && document.body.classList.contains(all[state]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number|undefined|null|false} state
|
|
||||||
*/
|
|
||||||
const set = (state = false) => {
|
|
||||||
document.body.classList.remove(...all);
|
|
||||||
isValidIndex(state) && document.body.classList.add(all[state]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return { set, is };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default documentState;
|
export default documentState;
|
||||||
|
15
modules/utils/elementMode.mjs
Normal file
15
modules/utils/elementMode.mjs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//@ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a helper to add or remove global classes that begin with `is-`
|
||||||
|
* @param {string} name name of the state
|
||||||
|
* @param {Element} [element] defaults to the document body
|
||||||
|
*/
|
||||||
|
export const elementMode = (name, element = document.body) => ({
|
||||||
|
on: () => element.classList.add(`is-${name}`),
|
||||||
|
off: () => element.classList.remove(`is-${name}`),
|
||||||
|
toggle: () => element.classList.toggle(`is-${name}`),
|
||||||
|
has: () => element.classList.contains(`is-${name}`),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default elementMode;
|
11
modules/utils/escapeRegExp.mjs
Normal file
11
modules/utils/escapeRegExp.mjs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
//@ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes a string so it can be used in a regular expression
|
||||||
|
* @param {string} text
|
||||||
|
*/
|
||||||
|
export function escapeRegExp(text) {
|
||||||
|
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||||
|
}
|
||||||
|
|
||||||
|
export default escapeRegExp
|
22
modules/utils/getAspectRatio.mjs
Normal file
22
modules/utils/getAspectRatio.mjs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//@ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{width: number;height: number}} Size
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates an ideal ratio
|
||||||
|
* @param {Size} initial the initial size
|
||||||
|
* @param {Size} current the current size
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getAspectRatio = (initial, current) => {
|
||||||
|
const ratioW = current.width / initial.width;
|
||||||
|
const ratioH = current.height / initial.height;
|
||||||
|
const ratio = Math.min(ratioW, ratioH);
|
||||||
|
const width = initial.width * ratio;
|
||||||
|
const height = initial.height * ratio;
|
||||||
|
return { width, height, ratio };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getAspectRatio
|
19
modules/utils/getElement.mjs
Normal file
19
modules/utils/getElement.mjs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//@ts-check
|
||||||
|
import {isElement} from "./isElement.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Little utility so people can pass css selectors or elements in initialization
|
||||||
|
* options.
|
||||||
|
* A minimal replacement to a more full fledged selector engine like jQuery
|
||||||
|
* @param {string|HTMLElement} elementOrString
|
||||||
|
* @returns {HTMLElement | null}
|
||||||
|
*/
|
||||||
|
export const getElement = (elementOrString) => {
|
||||||
|
const element =
|
||||||
|
elementOrString && typeof elementOrString === "string"
|
||||||
|
? /** @type {HTMLElement}*/ (document.querySelector(elementOrString))
|
||||||
|
: elementOrString;
|
||||||
|
return isElement(element) ? element : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getElement
|
16
modules/utils/getLocale.mjs
Normal file
16
modules/utils/getLocale.mjs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//@ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current locale. Only works if `navigator` is available.
|
||||||
|
* Otherwise, returns the `defaultLang` passed property
|
||||||
|
* @param {string} [defaultLang] defaults to an empty string
|
||||||
|
* @returns The browser locale, formatted as `xx_XX`
|
||||||
|
*/
|
||||||
|
export const getLocale = (defaultLang = "") =>
|
||||||
|
(typeof navigator !== "undefined" &&
|
||||||
|
(navigator.languages ? navigator.languages[0] : navigator.language)
|
||||||
|
.split(".")[0]
|
||||||
|
.replace("-", "_")) ||
|
||||||
|
defaultLang;
|
||||||
|
|
||||||
|
export default getLocale;
|
14
modules/utils/getRandomId.mjs
Normal file
14
modules/utils/getRandomId.mjs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//@ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
* short random string for ids - not guaranteed to be unique
|
||||||
|
* @see https://www.codemzy.com/blog/random-unique-id-javascript
|
||||||
|
* @param {number} length the length of the id
|
||||||
|
*/
|
||||||
|
export const getRandomId = function (length = 6) {
|
||||||
|
return Math.random()
|
||||||
|
.toString(36)
|
||||||
|
.substring(2, length + 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getRandomId
|
@ -2,27 +2,34 @@
|
|||||||
|
|
||||||
import { changeTitle } from "./changeTitle.mjs";
|
import { changeTitle } from "./changeTitle.mjs";
|
||||||
//import { createCustomElement } from "./createCustomElement.mjs";
|
//import { createCustomElement } from "./createCustomElement.mjs";
|
||||||
|
import { createElementStatusModes } from "./createElementStatusModes.mjs";
|
||||||
import { createTrackedResponse } from "./createTrackedResponse.mjs";
|
import { createTrackedResponse } from "./createTrackedResponse.mjs";
|
||||||
import { decodeContentLength } from "./decodeContentLength.mjs";
|
import { decodeContentLength } from "./decodeContentLength.mjs";
|
||||||
import { deferredPromise } from "./deferredPromise.mjs";
|
import { deferredPromise } from "./deferredPromise.mjs";
|
||||||
import { documentMode } from "./documentMode.mjs";
|
|
||||||
import { documentState } from "./documentState.mjs";
|
import { documentState } from "./documentState.mjs";
|
||||||
|
import { elementMode } from "./elementMode.mjs";
|
||||||
|
import { escapeRegExp } from "./escapeRegExp.mjs";
|
||||||
import { fetchContentLength } from "./fetchContentLength.mjs";
|
import { fetchContentLength } from "./fetchContentLength.mjs";
|
||||||
import { fetchHeaders } from "./fetchHeaders.mjs";
|
import { fetchHeaders } from "./fetchHeaders.mjs";
|
||||||
import { fetchMarkdown } from "./fetchMarkdown.mjs";
|
import { fetchMarkdown } from "./fetchMarkdown.mjs";
|
||||||
import { fetchText } from "./fetchText.mjs";
|
import { fetchText } from "./fetchText.mjs";
|
||||||
import { generateDomFromString } from "./generateDomFromString.mjs";
|
import { generateDomFromString } from "./generateDomFromString.mjs";
|
||||||
|
import { getAspectRatio } from "./getAspectRatio.mjs";
|
||||||
import {
|
import {
|
||||||
getCurrentHashUrl,
|
getCurrentHashUrl,
|
||||||
hasCurrentHashUrl,
|
hasCurrentHashUrl,
|
||||||
hasNoHashUrl,
|
hasNoHashUrl,
|
||||||
} from "./getCurrentHashUrl.mjs";
|
} from "./getCurrentHashUrl.mjs";
|
||||||
|
import { getElement } from "./getElement.mjs";
|
||||||
import { getElementByCSSSelector } from "./getElementByCSSSelector.mjs";
|
import { getElementByCSSSelector } from "./getElementByCSSSelector.mjs";
|
||||||
import { getElementById } from "./getElementById.mjs";
|
import { getElementById } from "./getElementById.mjs";
|
||||||
import { getFirstTitleContent } from "./getFirstTitleContent.mjs";
|
import { getFirstTitleContent } from "./getFirstTitleContent.mjs";
|
||||||
|
import { getLocale } from "./getLocale.mjs";
|
||||||
import { getPageUniqueId } from "./getPageUniqueId.mjs";
|
import { getPageUniqueId } from "./getPageUniqueId.mjs";
|
||||||
|
import { getRandomId } from "./getRandomId.mjs";
|
||||||
import { getReasonableUuid } from "./getReasonableUuid.mjs";
|
import { getReasonableUuid } from "./getReasonableUuid.mjs";
|
||||||
import { identity, awaitedIdentity } from "./identity.mjs";
|
import { identity, awaitedIdentity } from "./identity.mjs";
|
||||||
|
import { isElement } from "./isElement.mjs";
|
||||||
import { html } from "./html.mjs";
|
import { html } from "./html.mjs";
|
||||||
import { isExternalUrl } from "./isExternalUrl.mjs";
|
import { isExternalUrl } from "./isExternalUrl.mjs";
|
||||||
import { isLocalHost } from "./isLocalHost.mjs";
|
import { isLocalHost } from "./isLocalHost.mjs";
|
||||||
@ -59,6 +66,7 @@ import {
|
|||||||
} from "./querySelectorAll.mjs";
|
} from "./querySelectorAll.mjs";
|
||||||
import { retryPromise } from "./retryPromise.mjs";
|
import { retryPromise } from "./retryPromise.mjs";
|
||||||
import { rewriteLocalUrls } from "./rewriteLocalUrls.mjs";
|
import { rewriteLocalUrls } from "./rewriteLocalUrls.mjs";
|
||||||
|
import { throttle } from "./throttle.mjs";
|
||||||
import { today } from "./today.mjs";
|
import { today } from "./today.mjs";
|
||||||
import { UnreachableCaseError } from "./UnreachableCaseError.mjs";
|
import { UnreachableCaseError } from "./UnreachableCaseError.mjs";
|
||||||
import { wait } from "./wait.mjs";
|
import { wait } from "./wait.mjs";
|
||||||
@ -66,27 +74,34 @@ import { waitIfLocalHost } from "./waitIfLocalHost.mjs";
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
changeTitle,
|
changeTitle,
|
||||||
|
createElementStatusModes,
|
||||||
createTrackedResponse,
|
createTrackedResponse,
|
||||||
decodeContentLength,
|
decodeContentLength,
|
||||||
deferredPromise,
|
deferredPromise,
|
||||||
documentMode,
|
elementMode as documentMode,
|
||||||
documentState,
|
documentState,
|
||||||
|
escapeRegExp,
|
||||||
fetchContentLength,
|
fetchContentLength,
|
||||||
fetchHeaders,
|
fetchHeaders,
|
||||||
fetchMarkdown,
|
fetchMarkdown,
|
||||||
fetchText,
|
fetchText,
|
||||||
generateDomFromString,
|
generateDomFromString,
|
||||||
|
getAspectRatio,
|
||||||
getCurrentHashUrl,
|
getCurrentHashUrl,
|
||||||
hasCurrentHashUrl,
|
hasCurrentHashUrl,
|
||||||
hasNoHashUrl,
|
hasNoHashUrl,
|
||||||
|
getElement,
|
||||||
getElementByCSSSelector,
|
getElementByCSSSelector,
|
||||||
getElementById,
|
getElementById,
|
||||||
getFirstTitleContent,
|
getFirstTitleContent,
|
||||||
|
getLocale,
|
||||||
getPageUniqueId,
|
getPageUniqueId,
|
||||||
|
getRandomId,
|
||||||
getReasonableUuid,
|
getReasonableUuid,
|
||||||
html,
|
html,
|
||||||
identity,
|
identity,
|
||||||
awaitedIdentity,
|
awaitedIdentity,
|
||||||
|
isElement,
|
||||||
isExternalUrl,
|
isExternalUrl,
|
||||||
isLocalHost,
|
isLocalHost,
|
||||||
isNotNull,
|
isNotNull,
|
||||||
@ -118,6 +133,7 @@ export {
|
|||||||
querySelectorAll,
|
querySelectorAll,
|
||||||
retryPromise,
|
retryPromise,
|
||||||
rewriteLocalUrls,
|
rewriteLocalUrls,
|
||||||
|
throttle,
|
||||||
today,
|
today,
|
||||||
UnreachableCaseError,
|
UnreachableCaseError,
|
||||||
wait,
|
wait,
|
||||||
|
12
modules/utils/isElement.mjs
Normal file
12
modules/utils/isElement.mjs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//@ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies an element is actually an element.
|
||||||
|
* @param {any} element
|
||||||
|
* @returns {element is HTMLElement}
|
||||||
|
*/
|
||||||
|
export const isElement = (element) => {
|
||||||
|
return element instanceof Element || element instanceof Document;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isElement
|
52
modules/utils/throttle.mjs
Normal file
52
modules/utils/throttle.mjs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
//@ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Creates a throttled function that only invokes the provided function at most
|
||||||
|
* once per within a given number of milliseconds.
|
||||||
|
*
|
||||||
|
* @template {(...args: any) => any } F
|
||||||
|
* @template {ReturnType<F>} R
|
||||||
|
* @template {Parameters<F>} P
|
||||||
|
* @param {F} func
|
||||||
|
* @param {number} [firingRateMs] Firing rate (50ms by default)
|
||||||
|
*/
|
||||||
|
export const throttle = (func, firingRateMs = 50) => {
|
||||||
|
/** @type {R} */
|
||||||
|
let lastResult;
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
let last = 0;
|
||||||
|
|
||||||
|
/** @type {null|P} */
|
||||||
|
let funcArguments;
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
let timeoutID = 0;
|
||||||
|
|
||||||
|
const call = () => {
|
||||||
|
timeoutID = 0;
|
||||||
|
last = +new Date();
|
||||||
|
lastResult = func.apply(null, funcArguments);
|
||||||
|
funcArguments = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @param {P} args
|
||||||
|
*/
|
||||||
|
const throttled = (...args) => {
|
||||||
|
funcArguments = args;
|
||||||
|
const delta = new Date().getTime() - last;
|
||||||
|
if (!timeoutID)
|
||||||
|
if (delta >= firingRateMs) {
|
||||||
|
call();
|
||||||
|
} else {
|
||||||
|
timeoutID = setTimeout(call, firingRateMs - delta);
|
||||||
|
}
|
||||||
|
return lastResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
return throttled;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default throttle
|
Loading…
Reference in New Issue
Block a user