@kamen/create-webapp
Version:
npm init @kamen/webapp
395 lines (343 loc) • 10.2 kB
JavaScript
const primitiveTypeRe = /^string|number|bigint|boolean|symbol$/;
const objectTypeRe = /^object|function$/;
export {default as temporal} from './temporal.js';
/**
* @param {*} value
* @returns {boolean}
*/
export function isPrimitive(value) {
return primitiveTypeRe.test(typeof value);
}
/**
* @param {*} value
* @returns {boolean}
*/
export function isObject(value) {
return value !== null && objectTypeRe.test(typeof value);
}
/**
* @param {number} offset
* @param {number} length
* @returns {number}
*/
export function modulo(offset, length) {
return ((offset % length) + length) % length;
}
/**
* @param {number} [min=0]
* @param {number} [max=255]
* @param {number} [step=1]
* @returns {number[]}
*/
export function range(min = 0, max = 255, step = 1) {
return Array.from({length: (max - min) / step + 1}, (_, index) => index * step + min);
}
/**
* @param {number} value
* @param {number} [min=0]
* @param {number} [max=255]
* @returns {number}
*/
export function clamp(value, min = 0, max = 255) {
return Math.min(Math.max(value, min), max);
}
/**
* @returns {string}
*/
export function randomSlug() {
return Math.random().toString(36).slice(2);
}
/**
* @param {number} [min=0]
* @param {number} [max=255]
* @returns {number}
*/
export function randomFromRange(min = 0, max = 255) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
/**
* @template T
* @param {T[]} [list=[]]
* @returns {T}
*/
export function randomFromList(list = []) {
return list[randomFromRange(0, list.length - 1)];
}
/**
* @param {number} [saturation=80]
* @param {number} [lightness=80]
* @param {number} [alpha=1]
* @returns {string}
*/
export function randomColorFromSaturationLightnessAlpha(saturation = 80, lightness = 80, alpha = 1) {
const hue = randomFromRange(0, 359);
return `hsla(${hue}, ${saturation}%, ${lightness}%, ${alpha})`;
}
/**
* @param {number} [min=0]
* @param {number} [max=255]
* @returns {Function<number>}
*/
export function createRandomFromRange(min = 0, max = 255) {
return function () {
return randomFromRange(min, max);
}
}
/**
* @template T
* @param {T[]} [list=[]]
* @returns {Function<T>}
*/
export function createRandomFromList(list = []) {
return function () {
return randomFromList(list);
}
}
/**
* @param {number} [saturation=80]
* @param {number} [lightness=80]
* @param {number} [alpha=1]
* @returns {Function<string>}
*/
export function createRandomColorFromSaturationLightnessAlpha(saturation = 80, lightness = 80, alpha = 1) {
return function () {
return randomColorFromSaturationLightnessAlpha(saturation, lightness, alpha);
}
}
/**
* @param {string} [text='']
* @param {number} [saturation=80]
* @param {number} [lightness=80]
* @param {number} [alpha=1]
* @returns {string}
*/
export function textToHsla(text = '', saturation = 80, lightness = 80, alpha = 1) {
let hash = 0;
for (let i = 0, {length} = text; i < length; i++)
hash = text.charCodeAt(i) + ((hash << 5) - hash);
return `hsla(${hash % 360}, ${saturation}%, ${lightness}%, ${alpha})`;
}
/**
* @param {number} [max=1<<24]
* @returns {Set<number>}
*/
export function sieveOfEratosthenes(max = 1 << 24) {
const primes = new Set;
for (let i = 2; i < max; i++) primes.add(i);
for (let p = 2; p * p <= max; p++)
if (primes.has(p))
for (let i = p * p; i <= max; i += p) primes.delete(i);
return primes;
}
/**
* @param {string|Function} code
* @param {string} [type='text/javascript']
* @returns {string}
*/
export function createResource(code, type = 'text/javascript') {
return URL.createObjectURL(new Blob([typeof code === 'function' ? code.toString() : code], {type}));
}
/**
* @param {string|Function} code
* @param {string} [tag='iframe']
* @param {string} [attribute='src']
* @param {string} [type='text/html']
* @param {Node} [container=document.body]
* @returns {Element}
*/
export function createResourceTag(code, tag = 'iframe', attribute = 'src', type = 'text/html', container = document.body) {
const element = document.createElement(tag);
element.setAttribute(attribute, createResource(code, type));
return container.appendChild(element);
}
/**
* @param {string|Function} code
* @returns {Worker}
*/
export function createResourceClassicWorker(code) {
return new Worker(createResource(code), {type: 'classic'});
}
/**
* @param {string|Function} code
* @returns {Worker}
*/
export function createResourceModuleWorker(code) {
return new Worker(createResource(code), {type: 'module'});
}
/**
* @param {string|Function} code
* @param {Node} [container=document.head]
* @returns {HTMLScriptElement}
*/
export function createResourceClassicScript(code, container = document.head) {
return createResourceTag(code, 'script', 'type', 'text/javascript', container);
}
/**
* @param {string|Function} code
* @param {Node} [container=document.head]
* @returns {HTMLScriptElement}
*/
export function createResourceModuleScript(code, container = document.head) {
return createResourceTag(code, 'script', 'type', 'module', container);
}
/**
* @param {string|Function} code
* @param {Node} [container=document.head]
* @returns {HTMLStyleElement}
*/
export function createResourceStyle(code, container = document.head) {
return createResourceTag(code, 'style', 'type', 'text/css', container);
}
/**
* @param {string|Function} code
* @param {Node} [container=document.body]
* @returns {HTMLIFrameElement}
*/
export function createResourceIframe(code, container = document.body) {
return createResourceTag(code, 'iframe', 'src', 'text/html', container);
}
/**
* @param {string} [module='axios']
* @returns {Promise}
*/
export function npmModule(module = 'axios') {
const cdns = [
`https://esm.sh/${module}`,
`https://esm.run/${module}`,
`https://jspm.dev/${module}`,
`https://unpkg.com/${module}?module`,
`https://cdn.skypack.dev/${module}`
];
return Promise.any(cdns.map(cdn => import(cdn)));
}
/**
* @param {string[]} [modules=[]]
* @returns {Promise}
*/
export function npmModules(modules = []) {
return Promise.allSettled(modules.map(npmModule));
}
/**
* @param {number} [version=2]
* @param {Node} [root=document]
* @param {string} [hook='__VUE_DEVTOOLS_GLOBAL_HOOK__']
* @returns {Element|string}
*/
export function findVue(version = 2, root = document, hook = '__VUE_DEVTOOLS_GLOBAL_HOOK__') {
let element;
const property = version === 2 ? '__vue__' : '__vue_app__';
const treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
while (element = treeWalker.nextNode())
if (property in element) {
const {[property]: app} = element;
const {config} = version === 2 ? app.constructor : app;
config.devtools = true;
if (hook in window) window[hook].Vue = app.constructor;
return app;
}
return 'Vue mountpoint not found';
}
export class DOMUtils {
static namespaces = class NameSpaces {
static HTML = 'http://www.w3.org/1999/xhtml';
static MathML = 'http://www.w3.org/1998/Math/MathML';
static SVG = 'http://www.w3.org/2000/svg';
};
static createHTMLElement(tag = '') {
return document.createElementNS(this.namespaces.HTML, tag);
}
static createMathMLElement(tag = '') {
return document.createElementNS(this.namespaces.MathML, tag);
}
static createSVGElement(tag = '') {
return document.createElementNS(this.namespaces.SVG, tag);
}
}
/**
* URL wrapper implementing builder pattern
*/
export class URLBuilder {
static create(url, base) {
return new this(url, base);
}
constructor(url, base) {
this.url = new URL(url, base);
}
appendParams(name, value) {
this.url.searchParams.append(name, value);
return this;
}
deleteParams(name, value) {
this.url.searchParams.delete(name, value);
return this;
}
setParams(name, value) {
this.url.searchParams.set(name, value);
return this;
}
createParams(options) {
this.url.searchParams = new URLSearchParams(options);
return this;
}
get hash() {
return this.url.hash;
}
toString() {
return this.url.toString();
}
}
export class WrappedListIndexManager extends EventTarget {
#counter = 0;
#length = 16;
#step = 1;
static create() {
return new this;
}
#dispatchEvent(detail = this.getIndex()) {
return this.dispatchEvent(new CustomEvent('change', {detail}));
}
setCounter(counter = 0) {
this.#counter = counter;
this.#dispatchEvent();
return this;
}
setLength(length = 16) {
this.#length = length;
this.#dispatchEvent();
return this;
}
setStep(step = 1) {
this.#step = step;
return this;
}
increment(step = this.#step) {
this.#counter += step;
this.#dispatchEvent();
return this;
}
decrement(step = this.#step) {
this.#counter -= step;
this.#dispatchEvent();
return this;
}
getIndex() {
return modulo(this.#counter, this.#length);
}
}
export const introspection = {
isPrimitive,
isObject
};
export const numbering = {
modulo,
range,
clamp
};
export const randomness = {
randomFromRange,
createRandomFromRange,
randomFromList,
createRandomFromList,
randomColorFromSaturationLightnessAlpha,
createRandomColorFromSaturationLightnessAlpha
};