Comparing version 2.2.1 to 2.3.0
2250
dist/index.js
@@ -9,7 +9,7 @@ var __defProp = Object.defineProperty; | ||
}; | ||
var __copyProps = (to, from, except, desc2) => { | ||
var __copyProps = (to, from, except, desc) => { | ||
if (from && typeof from === "object" || typeof from === "function") { | ||
for (let key of __getOwnPropNames(from)) | ||
if (!__hasOwnProp.call(to, key) && key !== except) | ||
__defProp(to, key, { get: () => from[key], enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable }); | ||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
} | ||
@@ -25,5 +25,6 @@ return to; | ||
CENTURY: () => CENTURY, | ||
ColourTools: () => ColourTools_exports, | ||
ColourTools: () => ColourTools, | ||
DAY: () => DAY, | ||
DECADE: () => DECADE, | ||
ErrorTools: () => ErrorTools, | ||
HOUR: () => HOUR, | ||
@@ -34,4 +35,3 @@ MILLENNIUM: () => MILLENNIUM, | ||
MONTH: () => MONTH, | ||
MathTools: () => MathTools, | ||
MathsTools: () => MathsTools_exports, | ||
MathsTools: () => MathsTools, | ||
ObjectTools: () => ObjectTools, | ||
@@ -41,3 +41,3 @@ PromiseTools: () => PromiseTools, | ||
SECOND: () => SECOND, | ||
StringTools: () => StringTools_exports, | ||
StringTools: () => StringTools, | ||
TimeTools: () => TimeTools, | ||
@@ -57,5 +57,6 @@ WEEK: () => WEEK, | ||
entries: () => entries, | ||
everys: () => everys2, | ||
filters: () => filters2, | ||
fn: () => fn_exports, | ||
everys: () => everys, | ||
ff: () => ff, | ||
filters: () => filters, | ||
fn: () => fn, | ||
getDeferred: () => getDeferred, | ||
@@ -70,3 +71,3 @@ getProgressBar: () => getProgressBar, | ||
mapLimit: () => mapLimit, | ||
maps: () => maps2, | ||
maps: () => maps, | ||
millenniums: () => millenniums, | ||
@@ -78,7 +79,7 @@ milliseconds: () => milliseconds, | ||
printLn: () => printLn, | ||
progressBar: () => progressBar_exports, | ||
progressBar: () => progressBar, | ||
queue: () => queue, | ||
randomise: () => randomise, | ||
range: () => range, | ||
reduces: () => reduces2, | ||
reduces: () => reduces, | ||
repeat: () => repeat, | ||
@@ -92,3 +93,3 @@ retry: () => retry, | ||
sortNumberedText: () => sortNumberedText, | ||
sorts: () => sorts2, | ||
sorts: () => sorts, | ||
stopInterval: () => stopInterval, | ||
@@ -98,3 +99,3 @@ superscript: () => superscript, | ||
timer: () => timer, | ||
times: () => times_exports, | ||
times: () => times, | ||
tryOr: () => tryOr, | ||
@@ -105,3 +106,3 @@ wait: () => wait, | ||
waitUntil: () => waitUntil, | ||
waiters: () => waiters_exports, | ||
waiters: () => waiters, | ||
weeks: () => weeks, | ||
@@ -115,94 +116,349 @@ years: () => years, | ||
// src/tools/times.ts | ||
var times_exports = {}; | ||
__export(times_exports, { | ||
CENTURY: () => CENTURY, | ||
DAY: () => DAY, | ||
DECADE: () => DECADE, | ||
HOUR: () => HOUR, | ||
MILLENNIUM: () => MILLENNIUM, | ||
MILLISECOND: () => MILLISECOND, | ||
MINUTE: () => MINUTE, | ||
MONTH: () => MONTH, | ||
SECOND: () => SECOND, | ||
WEEK: () => WEEK, | ||
YEAR: () => YEAR, | ||
centuries: () => centuries, | ||
days: () => days, | ||
decades: () => decades, | ||
hours: () => hours, | ||
millenniums: () => millenniums, | ||
milliseconds: () => milliseconds, | ||
minutes: () => minutes, | ||
months: () => months, | ||
seconds: () => seconds, | ||
weeks: () => weeks, | ||
years: () => years | ||
}); | ||
var MILLISECOND = 1; | ||
var SECOND = 1e3 * MILLISECOND; | ||
var MINUTE = 60 * SECOND; | ||
var HOUR = 60 * MINUTE; | ||
var DAY = 24 * HOUR; | ||
var WEEK = 7 * DAY; | ||
var MONTH = 30 * DAY; | ||
var YEAR = 365.25 * DAY; | ||
var DECADE = 10 * YEAR; | ||
var CENTURY = 100 * YEAR; | ||
var MILLENNIUM = 1e3 * YEAR; | ||
var milliseconds = (x = 1) => x; | ||
var seconds = (x = 1) => x * SECOND; | ||
var minutes = (x = 1) => x * MINUTE; | ||
var hours = (x = 1) => x * HOUR; | ||
var days = (x = 1) => x * DAY; | ||
var weeks = (x = 1) => x * WEEK; | ||
var months = (x = 1) => x * MONTH; | ||
var years = (x = 1) => x * YEAR; | ||
var decades = (x = 1) => x * DECADE; | ||
var centuries = (x = 1) => x * CENTURY; | ||
var millenniums = (x = 1) => x * MILLENNIUM; | ||
var times; | ||
((times2) => { | ||
times2.MILLISECOND = 1; | ||
times2.SECOND = 1e3 * times2.MILLISECOND; | ||
times2.MINUTE = 60 * times2.SECOND; | ||
times2.HOUR = 60 * times2.MINUTE; | ||
times2.DAY = 24 * times2.HOUR; | ||
times2.WEEK = 7 * times2.DAY; | ||
times2.MONTH = 30 * times2.DAY; | ||
times2.YEAR = 365.25 * times2.DAY; | ||
times2.DECADE = 10 * times2.YEAR; | ||
times2.CENTURY = 100 * times2.YEAR; | ||
times2.MILLENNIUM = 1e3 * times2.YEAR; | ||
times2.milliseconds = (x = 1) => x; | ||
times2.seconds = (x = 1) => x * times2.SECOND; | ||
times2.minutes = (x = 1) => x * times2.MINUTE; | ||
times2.hours = (x = 1) => x * times2.HOUR; | ||
times2.days = (x = 1) => x * times2.DAY; | ||
times2.weeks = (x = 1) => x * times2.WEEK; | ||
times2.months = (x = 1) => x * times2.MONTH; | ||
times2.years = (x = 1) => x * times2.YEAR; | ||
times2.decades = (x = 1) => x * times2.DECADE; | ||
times2.centuries = (x = 1) => x * times2.CENTURY; | ||
times2.millenniums = (x = 1) => x * times2.MILLENNIUM; | ||
})(times || (times = {})); | ||
var MILLISECOND = times.MILLISECOND; | ||
var SECOND = times.SECOND; | ||
var MINUTE = times.MINUTE; | ||
var HOUR = times.HOUR; | ||
var DAY = times.DAY; | ||
var WEEK = times.WEEK; | ||
var MONTH = times.MONTH; | ||
var YEAR = times.YEAR; | ||
var DECADE = times.DECADE; | ||
var CENTURY = times.CENTURY; | ||
var MILLENNIUM = times.MILLENNIUM; | ||
var milliseconds = times.milliseconds; | ||
var seconds = times.seconds; | ||
var minutes = times.minutes; | ||
var hours = times.hours; | ||
var days = times.days; | ||
var weeks = times.weeks; | ||
var months = times.months; | ||
var years = times.years; | ||
var decades = times.decades; | ||
var centuries = times.centuries; | ||
var millenniums = times.millenniums; | ||
// src/tools/waiters.ts | ||
var waiters_exports = {}; | ||
__export(waiters_exports, { | ||
interval: () => interval, | ||
stopInterval: () => stopInterval, | ||
wait: () => wait, | ||
waitEvery: () => waitEvery, | ||
waitFor: () => waitFor, | ||
waitUntil: () => waitUntil | ||
}); | ||
var wait = (time) => new Promise((resolve2) => setTimeout(resolve2, time)); | ||
var PING_RATIO = 0.75; | ||
var ROUND_AMOUNT = 1.5; | ||
var getPingDuration = (time, now = Date.now()) => Math.ceil((time - now) * PING_RATIO / ROUND_AMOUNT) * ROUND_AMOUNT; | ||
var waitUntil = async (time) => { | ||
while (Date.now() < time) { | ||
await wait(getPingDuration(time)); | ||
} | ||
return null; | ||
}; | ||
var waitFor = async (time) => waitUntil(Date.now() + time); | ||
var getNextEvery = (timing, offset = 0) => { | ||
const now = Date.now(); | ||
const result2 = timing - (now - offset) % timing; | ||
return result2 <= 10 ? timing : result2; | ||
}; | ||
var waitEvery = (timing, offset) => waitFor(getNextEvery(timing, offset)); | ||
var stopped = []; | ||
var stopInterval = (intID) => stopped.push(intID); | ||
var interval = (action, timing) => { | ||
const intID = Math.floor(Math.random() * Math.pow(10, 10)); | ||
let count = 0; | ||
const run = async () => { | ||
await waitEvery(timing); | ||
if (stopped.includes(intID)) { | ||
return; | ||
var waiters; | ||
((waiters2) => { | ||
waiters2.wait = (time) => new Promise((resolve) => setTimeout(resolve, time)); | ||
const PING_RATIO = 0.75; | ||
const ROUND_AMOUNT = 1.5; | ||
const getPingDuration = (time, now = Date.now()) => Math.ceil((time - now) * PING_RATIO / ROUND_AMOUNT) * ROUND_AMOUNT; | ||
waiters2.waitUntil = async (time) => { | ||
while (Date.now() < time) { | ||
await waiters2.wait(getPingDuration(time)); | ||
} | ||
action(intID, ++count); | ||
return null; | ||
}; | ||
waiters2.waitFor = async (time) => waiters2.waitUntil(Date.now() + time); | ||
const getNextEvery = (timing, offset = 0) => { | ||
const now = Date.now(); | ||
const result = timing - (now - offset) % timing; | ||
return result <= 10 ? timing : result; | ||
}; | ||
waiters2.waitEvery = (timing, offset) => waiters2.waitFor(getNextEvery(timing, offset)); | ||
const stopped = []; | ||
waiters2.stopInterval = (intID) => stopped.push(intID); | ||
waiters2.interval = (action, timing) => { | ||
const intID = Math.floor(Math.random() * Math.pow(10, 10)); | ||
let count = 0; | ||
const run = async () => { | ||
await waiters2.waitEvery(timing); | ||
if (stopped.includes(intID)) { | ||
return; | ||
} | ||
action(intID, ++count); | ||
run(); | ||
}; | ||
run(); | ||
return intID; | ||
}; | ||
run(); | ||
return intID; | ||
}; | ||
})(waiters || (waiters = {})); | ||
var wait = waiters.wait; | ||
var waitUntil = waiters.waitUntil; | ||
var waitFor = waiters.waitFor; | ||
var waitEvery = waiters.waitEvery; | ||
var stopInterval = waiters.stopInterval; | ||
var interval = waiters.interval; | ||
// src/tools/ArrayTools.ts | ||
var ArrayTools; | ||
((ArrayTools2) => { | ||
let utils; | ||
((utils2) => { | ||
utils2.isNumString = (text) => Boolean(text.match(/^[0-9-.]+$/)); | ||
utils2.partitionNums = (ignoreCase) => (name) => (ignoreCase ? name.toLowerCase() : name).split(/([0-9]+)/).map((s) => utils2.isNumString(s) ? Number(s) : s); | ||
})(utils = ArrayTools2.utils || (ArrayTools2.utils = {})); | ||
ArrayTools2.range = (length = 1, multiplier = 1, offset = 0) => new Array(Math.floor(length)).fill(1).map((v, i) => MathsTools.fixFloat(i * multiplier) + offset); | ||
const zipFn = (length, arrs) => ArrayTools2.range(length).map((i) => arrs.map((arr) => (arr || [])[i])); | ||
ArrayTools2.zip = (...arrs) => zipFn(Math.min(...(arrs.length ? arrs : [[]]).map((arr) => (arr || []).length)), arrs); | ||
ArrayTools2.zipMax = (...arrs) => zipFn(Math.max(...(arrs.length ? arrs : [[]]).map((arr) => (arr || []).length)), arrs); | ||
ArrayTools2.sortByMapped = (arr, mapFn, sortFn = fn.asc) => ArrayTools2.zip(arr, arr.map(mapFn)).sort((a, b) => sortFn(a[1], b[1])).map(([v]) => v); | ||
ArrayTools2.randomise = (arr) => ArrayTools2.sortByMapped(arr, () => Math.random()); | ||
ArrayTools2.reverse = (arr) => [...arr].reverse(); | ||
ArrayTools2.entries = (arr) => ArrayTools2.zip(ArrayTools2.range(arr.length), arr); | ||
ArrayTools2.repeat = (maxLength, ...items) => { | ||
const simple = new Array(maxLength).fill(items[0]); | ||
return items.length === 1 ? simple : simple.map((v, i) => items[i % items.length]); | ||
}; | ||
ArrayTools2.roll = (distance, arr) => [ | ||
...arr.slice(distance % arr.length), | ||
...arr.slice(0, distance % arr.length) | ||
]; | ||
ArrayTools2.sortNumberedText = (texts, ignoreCase = true) => { | ||
return ArrayTools2.sortByMapped(texts, utils.partitionNums(ignoreCase), (a, b) => { | ||
for (let i in a) { | ||
const result = fn.asc(a[i], b[i]); | ||
if (result !== 0) | ||
return result; | ||
} | ||
return 0; | ||
}); | ||
}; | ||
ArrayTools2.partition = (array, partitionSize = Math.ceil(array.length / 2)) => { | ||
const numParts = Math.ceil(array.length / partitionSize); | ||
const result = []; | ||
for (let i = 0; i < numParts; i++) { | ||
result.push(array.slice(i * partitionSize, (i + 1) * partitionSize)); | ||
} | ||
return result; | ||
}; | ||
ArrayTools2.groupObj = (array, mapFn) => { | ||
const result = {}; | ||
array.forEach((item, index) => { | ||
const key = mapFn(item, index, array); | ||
if (key === void 0) | ||
return; | ||
if (!result[key]) | ||
result[key] = []; | ||
result[key].push(item); | ||
}); | ||
return result; | ||
}; | ||
ArrayTools2.group = (array, mapFn) => { | ||
const obj = ArrayTools2.groupObj(array, mapFn); | ||
return Object.values(obj); | ||
}; | ||
})(ArrayTools || (ArrayTools = {})); | ||
var range = ArrayTools.range; | ||
var zip = ArrayTools.zip; | ||
var zipMax = ArrayTools.zipMax; | ||
var sortByMapped = ArrayTools.sortByMapped; | ||
var randomise = ArrayTools.randomise; | ||
var reverse = ArrayTools.reverse; | ||
var entries = ArrayTools.entries; | ||
var repeat = ArrayTools.repeat; | ||
var roll = ArrayTools.roll; | ||
var sortNumberedText = ArrayTools.sortNumberedText; | ||
var partition = ArrayTools.partition; | ||
var groupObj = ArrayTools.groupObj; | ||
var group = ArrayTools.group; | ||
// src/tools/MathsTools.ts | ||
var MathsTools; | ||
((MathsTools2) => { | ||
MathsTools2.fixFloat = (num, precision = 6) => Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision); | ||
MathsTools2.ff = MathsTools2.fixFloat; | ||
MathsTools2.addAll = (...args) => args.reduce((acc, num) => acc + num, 0); | ||
MathsTools2.floorTo = (to, value) => MathsTools2.fixFloat(Math.floor(value / to) * to); | ||
MathsTools2.roundTo = (to, value) => MathsTools2.fixFloat(Math.round(value / to) * to); | ||
MathsTools2.ceilTo = (to, value) => MathsTools2.fixFloat(Math.ceil(value / to) * to); | ||
let round; | ||
((round2) => { | ||
round2.floorTo = MathsTools2.floorTo; | ||
round2.roundTo = MathsTools2.roundTo; | ||
round2.ceilTo = MathsTools2.ceilTo; | ||
round2.to = MathsTools2.roundTo; | ||
})(round = MathsTools2.round || (MathsTools2.round = {})); | ||
MathsTools2.lerp = (progress, fromVal, toVal) => fromVal + (toVal - fromVal) * progress; | ||
MathsTools2.lerpArray = (progress, fromArr, toArr) => ArrayTools.zip(fromArr, toArr).map(([fromVal, toVal]) => MathsTools2.lerp(progress, fromVal, toVal)); | ||
MathsTools2.lerpObj = (progress, fromObj, toObj) => { | ||
const entries2 = Object.entries(fromObj); | ||
const lerped = entries2.map(([key, fromVal]) => typeof fromVal === "number" ? [key, MathsTools2.lerp(progress, fromVal, toObj[key])] : [key, fromVal]); | ||
return Object.fromEntries(lerped); | ||
}; | ||
MathsTools2.clamp = (value, min, max) => Math.max(Math.min(min, max), Math.min(value, Math.max(min, max))); | ||
MathsTools2.getOrdinal = (num = 0) => { | ||
const lastDigit = num % 10; | ||
if ([11, 12, 13].includes(num)) { | ||
return "th"; | ||
} | ||
if (lastDigit === 1) { | ||
return "st"; | ||
} | ||
if (lastDigit === 2) { | ||
return "nd"; | ||
} | ||
if (lastDigit === 3) { | ||
return "rd"; | ||
} | ||
return "th"; | ||
}; | ||
})(MathsTools || (MathsTools = {})); | ||
var ff = MathsTools.fixFloat; | ||
// src/tools/fn.ts | ||
var fn; | ||
((fn2) => { | ||
fn2.noop = () => { | ||
}; | ||
fn2.noact = (item) => item; | ||
fn2.result = (item) => () => item; | ||
fn2.resolve = (item) => () => Promise.resolve(item); | ||
fn2.reject = (item) => () => Promise.reject(item); | ||
fn2.exists = (item) => item !== void 0 && item !== null; | ||
fn2.isTruthy = (item) => Boolean(item); | ||
fn2.isFalsy = (item) => !Boolean(item); | ||
fn2.isEmpty = (item) => Boolean(!item || !item.length); | ||
fn2.isNotEmpty = (item) => Boolean(item && item.length); | ||
fn2.isEqual = (item) => (other) => Boolean(item === other); | ||
fn2.isNotEqual = (item) => (other) => Boolean(item !== other); | ||
fn2.dedupe = (item, index, array) => array.indexOf(item) === index; | ||
fn2.dedupeMapped = (mapFn) => { | ||
let mapped; | ||
return (item, index, array) => { | ||
if (!mapped) | ||
mapped = array.map(mapFn); | ||
return mapped.indexOf(mapped[index]) === index; | ||
}; | ||
}; | ||
fn2.toString = (item) => item + ""; | ||
fn2.toNumber = (item) => Number(item); | ||
fn2.toBool = (item) => item !== "false" && Boolean(item); | ||
fn2.toProp = (prop) => (item) => item && item[prop]; | ||
fn2.toFixed = (precision) => (num) => MathsTools.fixFloat(num, precision); | ||
fn2.asc = (a, b) => { | ||
if (a < b) | ||
return -1; | ||
if (b < a) | ||
return 1; | ||
return 0; | ||
}; | ||
fn2.desc = (a, b) => { | ||
if (a < b) | ||
return 1; | ||
if (b < a) | ||
return -1; | ||
return 0; | ||
}; | ||
fn2.byProp = (propName, sortFn = fn2.asc) => { | ||
return (a, b) => sortFn(a[propName], b[propName]); | ||
}; | ||
fn2.nearestTo = (target) => (a, b) => Math.abs(Number(target) - Number(a)) - Math.abs(Number(target) - Number(b)); | ||
fn2.furthestFrom = (target) => (a, b) => Math.abs(Number(target) - Number(b)) - Math.abs(Number(target) - Number(a)); | ||
fn2.arrayAsc = (a, b) => { | ||
for (let i in a) { | ||
const result2 = fn2.asc(a[i], b[i]); | ||
if (result2 !== 0) | ||
return result2; | ||
} | ||
return 0; | ||
}; | ||
fn2.arrayDesc = (a, b) => { | ||
for (let i in a) { | ||
const result2 = fn2.desc(a[i], b[i]); | ||
if (result2 !== 0) | ||
return result2; | ||
} | ||
return 0; | ||
}; | ||
fn2.combine = (a, b) => a + b; | ||
fn2.combineProp = (propName) => (a, b) => a[propName] + b[propName]; | ||
fn2.mode = (prev, curr, index, arr) => { | ||
if (index > 1) { | ||
return prev; | ||
} | ||
const unique = arr.filter(fn2.dedupe); | ||
const counts = unique.map((item) => arr.filter((i) => i === item)).map((a) => a.length); | ||
const max = Math.max(...counts); | ||
return unique[counts.indexOf(max)]; | ||
}; | ||
fn2.modeMapped = (mapFn) => { | ||
let result2; | ||
return (prev, curr, index, arr) => { | ||
if (result2) | ||
return result2; | ||
const mapped = arr.map(mapFn); | ||
const uniqueU = mapped.filter(fn2.dedupe); | ||
const uniqueT = arr.filter(fn2.dedupeMapped(mapFn)); | ||
const counts = uniqueU.map((item) => mapped.filter((i) => i === item)).map((a) => a.length); | ||
const max = Math.max(...counts); | ||
result2 = uniqueT[counts.indexOf(max)]; | ||
return result2; | ||
}; | ||
}; | ||
fn2.isAllEqual = (val, i, arr) => val === arr[0]; | ||
let filters2; | ||
((filters3) => { | ||
filters3.exists = fn2.exists; | ||
filters3.isTruthy = fn2.isTruthy; | ||
filters3.isFalsy = fn2.isFalsy; | ||
filters3.isEmpty = fn2.isEmpty; | ||
filters3.isNotEmpty = fn2.isNotEmpty; | ||
filters3.isEqual = fn2.isEqual; | ||
filters3.isNotEqual = fn2.isNotEqual; | ||
filters3.dedupe = fn2.dedupe; | ||
filters3.dedupeMapped = fn2.dedupeMapped; | ||
})(filters2 = fn2.filters || (fn2.filters = {})); | ||
let maps2; | ||
((maps3) => { | ||
maps3.toString = fn2.toString; | ||
maps3.toNumber = fn2.toNumber; | ||
maps3.toBool = fn2.toBool; | ||
maps3.toProp = fn2.toProp; | ||
maps3.toFixed = fn2.toFixed; | ||
})(maps2 = fn2.maps || (fn2.maps = {})); | ||
let sorts2; | ||
((sorts3) => { | ||
sorts3.asc = fn2.asc; | ||
sorts3.desc = fn2.desc; | ||
sorts3.byProp = fn2.byProp; | ||
sorts3.nearestTo = fn2.nearestTo; | ||
sorts3.furthestFrom = fn2.furthestFrom; | ||
sorts3.arrayAsc = fn2.arrayAsc; | ||
sorts3.arrayDesc = fn2.arrayDesc; | ||
})(sorts2 = fn2.sorts || (fn2.sorts = {})); | ||
let reduces2; | ||
((reduces3) => { | ||
reduces3.combine = fn2.combine; | ||
reduces3.combineProp = fn2.combineProp; | ||
reduces3.mode = fn2.mode; | ||
reduces3.modeMapped = fn2.modeMapped; | ||
})(reduces2 = fn2.reduces || (fn2.reduces = {})); | ||
let everys2; | ||
((everys3) => { | ||
everys3.isAllEqual = fn2.isAllEqual; | ||
})(everys2 = fn2.everys || (fn2.everys = {})); | ||
})(fn || (fn = {})); | ||
var filters = fn.filters; | ||
var maps = fn.maps; | ||
var sorts = fn.sorts; | ||
var reduces = fn.reduces; | ||
var everys = fn.everys; | ||
// src/tools/fakeChalk.ts | ||
@@ -263,27 +519,27 @@ var noWrap = (x) => x; | ||
]; | ||
var toReadableDuration = (duration, longNames = false, maxUnits = 3) => { | ||
if (duration === 0) | ||
return ""; | ||
const allUnitValues = units.map((unit, index) => { | ||
var _a; | ||
const previousUnitValue = ((_a = units[index - 1]) == null ? void 0 : _a.value) ?? Infinity; | ||
const amount = Math.floor(Math.abs(duration) % previousUnitValue / unit.value); | ||
return { amount, unit }; | ||
}).filter(({ amount }) => amount > 0); | ||
const results = allUnitValues.slice(0, maxUnits).map(({ amount, unit }) => { | ||
const labelObj = longNames ? unit.long : unit.short; | ||
const label = amount > 1 ? labelObj.plural : labelObj.singular; | ||
return `${amount}${label}`; | ||
}); | ||
if (longNames) { | ||
if (results.length <= 1) { | ||
return results.join(""); | ||
var TimeTools; | ||
((TimeTools2) => { | ||
TimeTools2.toReadableDuration = (duration, longNames = false, maxUnits = 3) => { | ||
if (duration === 0) | ||
return ""; | ||
const allUnitValues = units.map((unit, index) => { | ||
var _a; | ||
const previousUnitValue = ((_a = units[index - 1]) == null ? void 0 : _a.value) ?? Infinity; | ||
const amount = Math.floor(Math.abs(duration) % previousUnitValue / unit.value); | ||
return { amount, unit }; | ||
}).filter(({ amount }) => amount > 0); | ||
const results = allUnitValues.slice(0, maxUnits).map(({ amount, unit }) => { | ||
const labelObj = longNames ? unit.long : unit.short; | ||
const label = amount > 1 ? labelObj.plural : labelObj.singular; | ||
return `${amount}${label}`; | ||
}); | ||
if (longNames) { | ||
if (results.length <= 1) { | ||
return results.join(""); | ||
} | ||
return [...results.slice(0, -1), "&&&&", ...results.slice(-1)].join(", ").replace("&&&&,", "&").replace(", &", " &"); | ||
} | ||
return [...results.slice(0, -1), "&&&&", ...results.slice(-1)].join(", ").replace("&&&&,", "&").replace(", &", " &"); | ||
} | ||
return results.join(" "); | ||
}; | ||
var TimeTools = { | ||
toReadableDuration | ||
}; | ||
return results.join(" "); | ||
}; | ||
})(TimeTools || (TimeTools = {})); | ||
@@ -383,586 +639,733 @@ // src/tools/timer.ts | ||
// src/tools/progressBar.ts | ||
var progressBar_exports = {}; | ||
__export(progressBar_exports, { | ||
getProgressBar: () => getProgressBar, | ||
printLn: () => printLn | ||
}); | ||
var progressBar; | ||
((progressBar2) => { | ||
progressBar2.printLn = (...text) => { | ||
var _a, _b; | ||
if (((_a = process == null ? void 0 : process.stdout) == null ? void 0 : _a.clearLine) && ((_b = process == null ? void 0 : process.stdout) == null ? void 0 : _b.cursorTo)) { | ||
if (!text.length) { | ||
process.stdout.write("\n"); | ||
} else { | ||
const output = text.map((item) => item.toString()).join(" "); | ||
process.stdout.clearLine(0); | ||
process.stdout.cursorTo(0); | ||
process.stdout.moveCursor(0, -1); | ||
process.stdout.clearLine(0); | ||
process.stdout.write(output); | ||
process.stdout.write("\n"); | ||
} | ||
} else { | ||
console.log(...text); | ||
} | ||
}; | ||
const print = (text, wrapperFn = fn.noact) => { | ||
const wrapped = wrapperFn(text || ""); | ||
progressBar2.printLn(wrapped); | ||
}; | ||
const getCharWidth = (num, max, width) => Math.round(width * (Math.max(0, Math.min(num / max, 1)) / 1)); | ||
const getBarString = (current, max, width, opts) => { | ||
const { progChar, emptyChar, startChar, endChar, showCurrent, currentChar } = opts; | ||
const numProgChars = getCharWidth(current, max, width); | ||
const numNextChars = getCharWidth(current + 1, max, width); | ||
const numCurrentChars = showCurrent ? numNextChars - numProgChars : 0; | ||
const numEmptyChars = width - numProgChars - numCurrentChars; | ||
const prog = opts.barProgWrapFn(progChar.repeat(numProgChars)); | ||
const curr = opts.barCurrentWrapFn(currentChar.repeat(numCurrentChars)); | ||
const empt = opts.barEmptyWrapFn(emptyChar.repeat(numEmptyChars)); | ||
const body = opts.barWrapFn(`${prog}${curr}${empt}`); | ||
return `${startChar}${body}${endChar}`; | ||
}; | ||
const getSuffix = (current, maxNum, isMaxKnown, opts) => { | ||
let items = [""]; | ||
if (opts.showCount) { | ||
const pad = Math.max(maxNum.toString().length, opts.countWidth); | ||
items.push(`[${current.toString().padStart(pad, " ")} / ${(isMaxKnown ? maxNum.toString() : "?").padStart(pad, " ")}]`); | ||
} | ||
if (opts.showPercent) { | ||
const percent = Math.round(current / Math.max(1, maxNum) * 100); | ||
items.push(`(${percent.toString().padStart("100".toString().length, " ")}%)`); | ||
} | ||
const joined = items.filter((x) => x).join(" "); | ||
return joined.length ? " " + joined : ""; | ||
}; | ||
const getFullOptions = (opts = {}) => { | ||
var _a; | ||
return { | ||
prefix: "", | ||
prefixWidth: 1, | ||
maxWidth: ((_a = process == null ? void 0 : process.stdout) == null ? void 0 : _a.columns) ? process.stdout.columns : 100, | ||
wrapperFn: fn.noact, | ||
barWrapFn: fn.noact, | ||
barProgWrapFn: fn.noact, | ||
barCurrentWrapFn: fn.noact, | ||
barEmptyWrapFn: fn.noact, | ||
showCount: true, | ||
showPercent: false, | ||
countWidth: 0, | ||
progChar: "\u2588", | ||
emptyChar: " ", | ||
startChar: "\u2595", | ||
endChar: "\u258F", | ||
showCurrent: false, | ||
currentChar: "\u259E", | ||
...opts | ||
}; | ||
}; | ||
progressBar2.getProgressBar = (max, options = {}) => { | ||
const opts = getFullOptions(options); | ||
const { prefix, prefixWidth, maxWidth, wrapperFn, startChar, endChar } = opts; | ||
let current = 0; | ||
let finished = false; | ||
const maxNum = typeof max === "number" ? max : 1; | ||
const isMaxKnown = typeof max === "number"; | ||
const update = () => { | ||
const suffix = getSuffix(current, maxNum, isMaxKnown, opts); | ||
const fullPrefix = prefix.padEnd(prefixWidth); | ||
const output = `${fullPrefix}${getBarString( | ||
current, | ||
Math.max(1, maxNum), | ||
Math.max(0, maxWidth - [fullPrefix, suffix, startChar, endChar].join("").length), | ||
opts | ||
)}${suffix}`; | ||
print(output, wrapperFn); | ||
return output; | ||
}; | ||
const next = () => { | ||
if (finished) | ||
return ""; | ||
current++; | ||
return update(); | ||
}; | ||
const set = (newCurrent) => { | ||
if (finished) | ||
return ""; | ||
current = newCurrent; | ||
return update(); | ||
}; | ||
const reset = () => { | ||
return set(0); | ||
}; | ||
const start = () => { | ||
progressBar2.printLn(); | ||
return update(); | ||
}; | ||
const finish = () => { | ||
finished = true; | ||
const output = update(); | ||
progressBar2.printLn(); | ||
return output; | ||
}; | ||
return { | ||
next, | ||
set, | ||
reset, | ||
update, | ||
start, | ||
finish, | ||
max | ||
}; | ||
}; | ||
})(progressBar || (progressBar = {})); | ||
var printLn = progressBar.printLn; | ||
var getProgressBar = progressBar.getProgressBar; | ||
// src/tools/fn.ts | ||
var fn_exports = {}; | ||
__export(fn_exports, { | ||
arrayAsc: () => arrayAsc, | ||
arrayDesc: () => arrayDesc, | ||
asc: () => asc, | ||
byProp: () => byProp, | ||
combine: () => combine, | ||
combineProp: () => combineProp, | ||
dedupe: () => dedupe, | ||
dedupeMapped: () => dedupeMapped, | ||
desc: () => desc, | ||
everys: () => everys, | ||
exists: () => exists, | ||
filters: () => filters, | ||
furthestFrom: () => furthestFrom, | ||
isAllEqual: () => isAllEqual, | ||
isEmpty: () => isEmpty, | ||
isEqual: () => isEqual, | ||
isFalsy: () => isFalsy, | ||
isNotEmpty: () => isNotEmpty, | ||
isNotEqual: () => isNotEqual, | ||
isTruthy: () => isTruthy, | ||
maps: () => maps, | ||
mode: () => mode, | ||
modeMapped: () => modeMapped, | ||
nearestTo: () => nearestTo, | ||
noact: () => noact, | ||
noop: () => noop, | ||
reduces: () => reduces, | ||
reject: () => reject, | ||
resolve: () => resolve, | ||
result: () => result, | ||
sorts: () => sorts, | ||
toBool: () => toBool, | ||
toFixed: () => toFixed, | ||
toNumber: () => toNumber, | ||
toProp: () => toProp, | ||
toString: () => toString | ||
}); | ||
// src/tools/ObjectTools.ts | ||
var ObjectTools; | ||
((ObjectTools2) => { | ||
ObjectTools2.remodel = (obj, func) => Object.fromEntries(func(Object.entries(obj)) ?? Object.entries(obj)); | ||
ObjectTools2.remodelEach = (obj, func) => Object.fromEntries(Object.entries(obj).map((entry, index, entries2) => func(entry, index, entries2) ?? entry)); | ||
ObjectTools2.map = (obj, func) => ObjectTools2.remodel(obj, (entries2) => entries2.map(([key, value], index) => func(key, value, index))); | ||
ObjectTools2.mapValues = (obj, func) => ObjectTools2.remodel(obj, (entries2) => entries2.map(([key, value], index) => [key, func(key, value, index)])); | ||
ObjectTools2.mapKeys = (obj, func) => ObjectTools2.remodel(obj, (entries2) => entries2.map(([key, value], index) => [func(key, value, index), value])); | ||
ObjectTools2.filter = (obj, func) => ObjectTools2.remodel(obj, (entries2) => entries2.filter(([key, value], index) => func(key, value, index))); | ||
ObjectTools2.clean = (obj) => ObjectTools2.filter(obj, (key, value) => value !== void 0); | ||
})(ObjectTools || (ObjectTools = {})); | ||
// src/tools/MathsTools.ts | ||
var MathsTools_exports = {}; | ||
__export(MathsTools_exports, { | ||
addAll: () => addAll, | ||
ceilTo: () => ceilTo, | ||
clamp: () => clamp, | ||
fixFloat: () => fixFloat, | ||
floorTo: () => floorTo, | ||
getOrdinal: () => getOrdinal, | ||
lerp: () => lerp, | ||
lerpArray: () => lerpArray, | ||
lerpObj: () => lerpObj, | ||
round: () => round, | ||
roundTo: () => roundTo | ||
}); | ||
// src/tools/ArrayTools.ts | ||
var range = (length = 1, multiplier = 1, offset = 0) => new Array(Math.floor(length)).fill(1).map((v, i) => fixFloat(i * multiplier) + offset); | ||
var zipFn = (length, arrs) => range(length).map((i) => arrs.map((arr) => (arr || [])[i])); | ||
var zip = (...arrs) => zipFn(Math.min(...(arrs.length ? arrs : [[]]).map((arr) => (arr || []).length)), arrs); | ||
var zipMax = (...arrs) => zipFn(Math.max(...(arrs.length ? arrs : [[]]).map((arr) => (arr || []).length)), arrs); | ||
var sortByMapped = (arr, mapFn, sortFn = sorts.asc) => zip(arr, arr.map(mapFn)).sort((a, b) => sortFn(a[1], b[1])).map(([v]) => v); | ||
var randomise = (arr) => sortByMapped(arr, () => Math.random()); | ||
var reverse = (arr) => [...arr].reverse(); | ||
var entries = (arr) => zip(range(arr.length), arr); | ||
var repeat = (maxLength, ...items) => { | ||
const simple = new Array(maxLength).fill(items[0]); | ||
return items.length === 1 ? simple : simple.map((v, i) => items[i % items.length]); | ||
}; | ||
var roll = (distance, arr) => [ | ||
...arr.slice(distance % arr.length), | ||
...arr.slice(0, distance % arr.length) | ||
]; | ||
var isNumString = (text) => Boolean(text.match(/^[0-9]+$/)); | ||
var partitionNums = (ignoreCase) => (name) => (ignoreCase ? name.toLowerCase() : name).split(/([0-9]+)/).map((s) => isNumString(s) ? Number(s) : s); | ||
var sortNumberedText = (texts, ignoreCase = true) => { | ||
return sortByMapped(texts, partitionNums(ignoreCase), (a, b) => { | ||
for (let i in a) { | ||
const result2 = sorts.asc(a[i], b[i]); | ||
if (result2 !== 0) | ||
return result2; | ||
// src/tools/StringTools.ts | ||
var StringTools; | ||
((StringTools2) => { | ||
StringTools2.capitalise = (input = "") => (input || "").split(/\s/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" "); | ||
StringTools2.angloise = (input) => input.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); | ||
StringTools2.clean = (input = "") => StringTools2.angloise([input].flat().join(" ")).replace(/\s{1,}/g, " ").replace(/[^A-Za-z0-9 ]/gi, ""); | ||
const caseHandler = (overrideSplitter) => { | ||
const getSplit = (input = "") => { | ||
if (overrideSplitter) | ||
return overrideSplitter(input); | ||
const arr = [input].flat(); | ||
return arr.map((s) => StringTools2.clean(s.replace(/-|_/g, " ")).split(" ")).flat().filter((s) => s.length); | ||
}; | ||
const toCamelCase2 = (input, capitaliseFirst = false) => { | ||
const split = getSplit(input); | ||
return split.map((word, index) => index === 0 && !capitaliseFirst ? word.toLowerCase() : StringTools2.capitalise(word)).join(""); | ||
}; | ||
const toLowerCamelCase2 = (input) => toCamelCase2(input, false); | ||
const toUpperCamelCase2 = (input) => toCamelCase2(input, true); | ||
const toCharacterSeparated2 = (input, char, toUpper = false) => { | ||
const split = getSplit(input); | ||
return split.map((word, index) => toUpper ? word.toUpperCase() : word.toLowerCase()).join(char); | ||
}; | ||
const toSlugCase2 = (input, toUpper = false) => toCharacterSeparated2(input, "-", toUpper); | ||
const toLowerSlugCase2 = (input) => toSlugCase2(input, false); | ||
const toUpperSlugCase2 = (input) => toSlugCase2(input, true); | ||
const toSnakeCase2 = (input, toUpper = false) => toCharacterSeparated2(input, "_", toUpper); | ||
const toLowerSnakeCase2 = (input) => toSnakeCase2(input, false); | ||
const toUpperSnakeCase2 = (input) => toSnakeCase2(input, true); | ||
const toSpaced2 = (input, toUpper = false) => toCharacterSeparated2(input, " ", toUpper); | ||
const toLowerSpaced2 = (input) => toSpaced2(input, false); | ||
const toUpperSpaced2 = (input) => toSpaced2(input, true); | ||
const toCapitalisedSpaced2 = (input) => StringTools2.capitalise(toSpaced2(input, false)); | ||
return { | ||
toLowerCamelCase: toLowerCamelCase2, | ||
toUpperCamelCase: toUpperCamelCase2, | ||
toCamelCase: toCamelCase2, | ||
toLowerSlugCase: toLowerSlugCase2, | ||
toUpperSlugCase: toUpperSlugCase2, | ||
toSlugCase: toSlugCase2, | ||
toLowerSnakeCase: toLowerSnakeCase2, | ||
toUpperSnakeCase: toUpperSnakeCase2, | ||
toSnakeCase: toSnakeCase2, | ||
toLowerSpaced: toLowerSpaced2, | ||
toUpperSpaced: toUpperSpaced2, | ||
toCapitalisedSpaced: toCapitalisedSpaced2, | ||
toSpaced: toSpaced2, | ||
toCharacterSeparated: toCharacterSeparated2 | ||
}; | ||
}; | ||
const standardCaseHandler = caseHandler(); | ||
({ | ||
toLowerCamelCase: StringTools2.toLowerCamelCase, | ||
toUpperCamelCase: StringTools2.toUpperCamelCase, | ||
toCamelCase: StringTools2.toCamelCase, | ||
toLowerSlugCase: StringTools2.toLowerSlugCase, | ||
toUpperSlugCase: StringTools2.toUpperSlugCase, | ||
toSlugCase: StringTools2.toSlugCase, | ||
toLowerSnakeCase: StringTools2.toLowerSnakeCase, | ||
toUpperSnakeCase: StringTools2.toUpperSnakeCase, | ||
toSnakeCase: StringTools2.toSnakeCase, | ||
toLowerSpaced: StringTools2.toLowerSpaced, | ||
toUpperSpaced: StringTools2.toUpperSpaced, | ||
toCapitalisedSpaced: StringTools2.toCapitalisedSpaced, | ||
toSpaced: StringTools2.toSpaced, | ||
toCharacterSeparated: StringTools2.toCharacterSeparated | ||
} = standardCaseHandler); | ||
StringTools2.fromSlugCase = standardCaseHandler; | ||
StringTools2.fromSnakeCase = standardCaseHandler; | ||
StringTools2.fromSpaced = standardCaseHandler; | ||
StringTools2.fromCamelCase = caseHandler( | ||
(input) => [input].flat().map((s) => StringTools2.clean(s)).map( | ||
(s) => s.replace(/([A-Z])/g, " $1").replace(/-|_/g, " ").trim() | ||
).map((s) => s.split(" ")).flat() | ||
); | ||
const processClxArray = (arr) => arr.filter(Boolean).map((item) => { | ||
if (typeof item === "string") | ||
return item; | ||
if (item instanceof Array) { | ||
return processClxArray(item); | ||
} | ||
return 0; | ||
}); | ||
}; | ||
var partition = (array, partitionSize = Math.ceil(array.length / 2)) => { | ||
const numParts = Math.ceil(array.length / partitionSize); | ||
const result2 = []; | ||
for (let i = 0; i < numParts; i++) { | ||
result2.push(array.slice(i * partitionSize, (i + 1) * partitionSize)); | ||
} | ||
return result2; | ||
}; | ||
var groupObj = (array, mapFn) => { | ||
const result2 = {}; | ||
array.forEach((item, index) => { | ||
const key = mapFn(item, index, array); | ||
if (key === void 0) | ||
return; | ||
if (!result2[key]) | ||
result2[key] = []; | ||
result2[key].push(item); | ||
}); | ||
return result2; | ||
}; | ||
var group = (array, mapFn) => { | ||
const obj = groupObj(array, mapFn); | ||
return Object.values(obj); | ||
}; | ||
var ArrayTools = { | ||
range, | ||
zip, | ||
zipMax, | ||
sortByMapped, | ||
randomise, | ||
reverse, | ||
entries, | ||
repeat, | ||
roll, | ||
sortNumberedText, | ||
partition, | ||
groupObj, | ||
group, | ||
utils: { | ||
isNumString, | ||
partitionNums | ||
} | ||
}; | ||
if (typeof item === "object") { | ||
return Object.keys(item).filter((key) => item[key]).join(" "); | ||
} | ||
}).flat(); | ||
StringTools2.clx = (...args) => processClxArray(args).join(" "); | ||
})(StringTools || (StringTools = {})); | ||
var clx = StringTools.clx; | ||
// src/tools/MathsTools.ts | ||
var fixFloat = (num, precision = 6) => Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision); | ||
var addAll = (...args) => args.reduce((acc, num) => acc + num, 0); | ||
var floorTo = (to, value) => fixFloat(Math.floor(value / to) * to); | ||
var roundTo = (to, value) => fixFloat(Math.round(value / to) * to); | ||
var ceilTo = (to, value) => fixFloat(Math.ceil(value / to) * to); | ||
var round = { | ||
floorTo, | ||
roundTo, | ||
ceilTo, | ||
to: roundTo | ||
}; | ||
var lerp = (progress, fromVal, toVal) => fromVal + (toVal - fromVal) * progress; | ||
var lerpArray = (progress, fromArr, toArr) => zip(fromArr, toArr).map(([fromVal, toVal]) => lerp(progress, fromVal, toVal)); | ||
var lerpObj = (progress, fromObj, toObj) => { | ||
const entries2 = Object.entries(fromObj); | ||
const lerped = entries2.map(([key, fromVal]) => typeof fromVal === "number" ? [key, lerp(progress, fromVal, toObj[key])] : [key, fromVal]); | ||
return Object.fromEntries(lerped); | ||
}; | ||
var clamp = (value, min, max) => Math.max(Math.min(min, max), Math.min(value, Math.max(min, max))); | ||
var getOrdinal = (num = 0) => { | ||
const lastDigit = num % 10; | ||
if ([11, 12, 13].includes(num)) { | ||
return "th"; | ||
} | ||
if (lastDigit === 1) { | ||
return "st"; | ||
} | ||
if (lastDigit === 2) { | ||
return "nd"; | ||
} | ||
if (lastDigit === 3) { | ||
return "rd"; | ||
} | ||
return "th"; | ||
}; | ||
// src/tools/fn.ts | ||
var noop = () => { | ||
}; | ||
var noact = (item) => item; | ||
var result = (item) => () => item; | ||
var resolve = (item) => () => Promise.resolve(item); | ||
var reject = (item) => () => Promise.reject(item); | ||
var exists = (item) => item !== void 0 && item !== null; | ||
var isTruthy = (item) => Boolean(item); | ||
var isFalsy = (item) => !Boolean(item); | ||
var isEmpty = (item) => Boolean(!item || !item.length); | ||
var isNotEmpty = (item) => Boolean(item && item.length); | ||
var isEqual = (item) => (other) => Boolean(item === other); | ||
var isNotEqual = (item) => (other) => Boolean(item !== other); | ||
var dedupe = (item, index, array) => array.indexOf(item) === index; | ||
var dedupeMapped = (mapFn) => { | ||
let mapped; | ||
return (item, index, array) => { | ||
if (!mapped) | ||
mapped = array.map(mapFn); | ||
return mapped.indexOf(mapped[index]) === index; | ||
// src/tools/PromiseTools.ts | ||
var PromiseTools; | ||
((PromiseTools2) => { | ||
PromiseTools2.getDeferred = () => { | ||
let resolve, reject; | ||
const promise = new Promise((res, rej) => { | ||
resolve = (arg) => { | ||
res(arg); | ||
return promise; | ||
}; | ||
reject = (...args) => { | ||
rej(...args); | ||
return promise; | ||
}; | ||
}); | ||
return { | ||
resolve, | ||
reject, | ||
promise | ||
}; | ||
}; | ||
}; | ||
var filters = { | ||
exists, | ||
isTruthy, | ||
isFalsy, | ||
isEmpty, | ||
isNotEmpty, | ||
isEqual, | ||
isNotEqual, | ||
dedupe, | ||
dedupeMapped | ||
}; | ||
var toString = (item) => item + ""; | ||
var toNumber = (item) => Number(item); | ||
var toBool = (item) => item !== "false" && Boolean(item); | ||
var toProp = (prop) => (item) => item && item[prop]; | ||
var toFixed = (precision) => (num) => fixFloat(num, precision); | ||
var maps = { | ||
toString, | ||
toNumber, | ||
toBool, | ||
toProp, | ||
toFixed | ||
}; | ||
var asc = (a, b) => { | ||
if (a < b) | ||
return -1; | ||
if (b < a) | ||
return 1; | ||
return 0; | ||
}; | ||
var desc = (a, b) => { | ||
if (a < b) | ||
return 1; | ||
if (b < a) | ||
return -1; | ||
return 0; | ||
}; | ||
var byProp = (propName, sortFn = asc) => { | ||
return (a, b) => sortFn(a[propName], b[propName]); | ||
}; | ||
var nearestTo = (target) => (a, b) => Math.abs(Number(target) - Number(a)) - Math.abs(Number(target) - Number(b)); | ||
var furthestFrom = (target) => (a, b) => Math.abs(Number(target) - Number(b)) - Math.abs(Number(target) - Number(a)); | ||
var arrayAsc = (a, b) => { | ||
for (let i in a) { | ||
const result2 = sorts.asc(a[i], b[i]); | ||
if (result2 !== 0) | ||
return result2; | ||
} | ||
return 0; | ||
}; | ||
var arrayDesc = (a, b) => { | ||
for (let i in a) { | ||
const result2 = sorts.desc(a[i], b[i]); | ||
if (result2 !== 0) | ||
return result2; | ||
} | ||
return 0; | ||
}; | ||
var sorts = { | ||
asc, | ||
desc, | ||
byProp, | ||
nearestTo, | ||
furthestFrom, | ||
arrayAsc, | ||
arrayDesc | ||
}; | ||
var combine = (a, b) => a + b; | ||
var combineProp = (propName) => (a, b) => a[propName] + b[propName]; | ||
var mode = (prev, curr, index, arr) => { | ||
if (index > 1) { | ||
return prev; | ||
} | ||
const unique = arr.filter(filters.dedupe); | ||
const counts = unique.map((item) => arr.filter((i) => i === item)).map((a) => a.length); | ||
const max = Math.max(...counts); | ||
return unique[counts.indexOf(max)]; | ||
}; | ||
var modeMapped = (mapFn) => { | ||
let result2; | ||
return (prev, curr, index, arr) => { | ||
if (result2) | ||
return result2; | ||
const mapped = arr.map(mapFn); | ||
const uniqueU = mapped.filter(filters.dedupe); | ||
const uniqueT = arr.filter(filters.dedupeMapped(mapFn)); | ||
const counts = uniqueU.map((item) => mapped.filter((i) => i === item)).map((a) => a.length); | ||
const max = Math.max(...counts); | ||
result2 = uniqueT[counts.indexOf(max)]; | ||
return result2; | ||
PromiseTools2.all = async (promises) => { | ||
await Promise.all(promises); | ||
}; | ||
}; | ||
var reduces = { | ||
combine, | ||
combineProp, | ||
mode, | ||
modeMapped | ||
}; | ||
var isAllEqual = (val, i, arr) => val === arr[0]; | ||
var everys = { | ||
isAllEqual | ||
}; | ||
// src/tools/progressBar.ts | ||
var printLn = (...text) => { | ||
var _a, _b; | ||
if (((_a = process == null ? void 0 : process.stdout) == null ? void 0 : _a.clearLine) && ((_b = process == null ? void 0 : process.stdout) == null ? void 0 : _b.cursorTo)) { | ||
if (!text.length) { | ||
process.stdout.write("\n"); | ||
} else { | ||
const output = text.map((item) => item.toString()).join(" "); | ||
process.stdout.clearLine(0); | ||
process.stdout.cursorTo(0); | ||
process.stdout.moveCursor(0, -1); | ||
process.stdout.clearLine(0); | ||
process.stdout.write(output); | ||
process.stdout.write("\n"); | ||
PromiseTools2.allLimit = (limit, items, noThrow = false) => { | ||
let runningCount = 0; | ||
let errors = []; | ||
let remaining = [...items]; | ||
const result = []; | ||
const deferred = PromiseTools2.getDeferred(); | ||
const update = () => { | ||
if (remaining.length === 0 && runningCount === 0) { | ||
if (errors.length && !noThrow) { | ||
deferred.reject(errors); | ||
return; | ||
} | ||
deferred.resolve(result); | ||
return; | ||
} | ||
if (runningCount < limit && remaining.length) { | ||
const next = remaining.shift(); | ||
const index = items.indexOf(next); | ||
run(next, index); | ||
} | ||
}; | ||
const run = async (prom, index) => { | ||
runningCount++; | ||
try { | ||
result[index] = await prom(index); | ||
} catch (err) { | ||
errors.push(err); | ||
} | ||
runningCount--; | ||
update(); | ||
}; | ||
for (let i = 0; i < Math.min(limit, items.length); i++) { | ||
update(); | ||
} | ||
} else { | ||
console.log(...text); | ||
} | ||
}; | ||
var print = (text, wrapperFn = noact) => { | ||
const wrapped = wrapperFn(text || ""); | ||
printLn(wrapped); | ||
}; | ||
var getCharWidth = (num, max, width) => Math.round(width * (Math.max(0, Math.min(num / max, 1)) / 1)); | ||
var getBarString = (current, max, width, opts) => { | ||
const { progChar, emptyChar, startChar, endChar, showCurrent, currentChar } = opts; | ||
const numProgChars = getCharWidth(current, max, width); | ||
const numNextChars = getCharWidth(current + 1, max, width); | ||
const numCurrentChars = showCurrent ? numNextChars - numProgChars : 0; | ||
const numEmptyChars = width - numProgChars - numCurrentChars; | ||
const prog = opts.barProgWrapFn(progChar.repeat(numProgChars)); | ||
const curr = opts.barCurrentWrapFn(currentChar.repeat(numCurrentChars)); | ||
const empt = opts.barEmptyWrapFn(emptyChar.repeat(numEmptyChars)); | ||
const body = opts.barWrapFn(`${prog}${curr}${empt}`); | ||
return `${startChar}${body}${endChar}`; | ||
}; | ||
var getSuffix = (current, maxNum, isMaxKnown, opts) => { | ||
let items = [""]; | ||
if (opts.showCount) { | ||
const pad = Math.max(maxNum.toString().length, opts.countWidth); | ||
items.push(`[${current.toString().padStart(pad, " ")} / ${(isMaxKnown ? maxNum.toString() : "?").padStart(pad, " ")}]`); | ||
} | ||
if (opts.showPercent) { | ||
const percent = Math.round(current / Math.max(1, maxNum) * 100); | ||
items.push(`(${percent.toString().padStart("100".toString().length, " ")}%)`); | ||
} | ||
const joined = items.filter((x) => x).join(" "); | ||
return joined.length ? " " + joined : ""; | ||
}; | ||
var getFullOptions = (opts = {}) => { | ||
var _a; | ||
return { | ||
prefix: "", | ||
prefixWidth: 1, | ||
maxWidth: ((_a = process == null ? void 0 : process.stdout) == null ? void 0 : _a.columns) ? process.stdout.columns : 100, | ||
wrapperFn: noact, | ||
barWrapFn: noact, | ||
barProgWrapFn: noact, | ||
barCurrentWrapFn: noact, | ||
barEmptyWrapFn: noact, | ||
showCount: true, | ||
showPercent: false, | ||
countWidth: 0, | ||
progChar: "\u2588", | ||
emptyChar: " ", | ||
startChar: "\u2595", | ||
endChar: "\u258F", | ||
showCurrent: false, | ||
currentChar: "\u259E", | ||
...opts | ||
if (!items || items.length === 0) { | ||
deferred.resolve(result); | ||
} | ||
return deferred.promise; | ||
}; | ||
}; | ||
var getProgressBar = (max, options = {}) => { | ||
const opts = getFullOptions(options); | ||
const { prefix, prefixWidth, maxWidth, wrapperFn, startChar, endChar } = opts; | ||
let current = 0; | ||
let finished = false; | ||
const maxNum = typeof max === "number" ? max : 1; | ||
const isMaxKnown = typeof max === "number"; | ||
const update = () => { | ||
const suffix = getSuffix(current, maxNum, isMaxKnown, opts); | ||
const fullPrefix = prefix.padEnd(prefixWidth); | ||
const output = `${fullPrefix}${getBarString( | ||
current, | ||
Math.max(1, maxNum), | ||
Math.max(0, maxWidth - [fullPrefix, suffix, startChar, endChar].join("").length), | ||
opts | ||
)}${suffix}`; | ||
print(output, wrapperFn); | ||
return output; | ||
PromiseTools2.each = async (items, func) => { | ||
await Promise.all(items.map((item, index, array) => func(item, index, array))); | ||
}; | ||
const next = () => { | ||
if (finished) | ||
return ""; | ||
current++; | ||
return update(); | ||
PromiseTools2.eachLimit = async (limit, items, func) => { | ||
await PromiseTools2.allLimit( | ||
limit, | ||
items.map((item, index, array) => () => func(item, index, array)) | ||
); | ||
}; | ||
const set = (newCurrent) => { | ||
if (finished) | ||
return ""; | ||
current = newCurrent; | ||
return update(); | ||
PromiseTools2.map = async (items, func) => { | ||
const result = []; | ||
await Promise.all( | ||
items.map(async (item, index, array) => { | ||
const res = await func(item, index, array); | ||
result[index] = res; | ||
}) | ||
); | ||
return result; | ||
}; | ||
const reset = () => { | ||
return set(0); | ||
PromiseTools2.mapLimit = async (limit, items, func) => await PromiseTools2.allLimit( | ||
limit, | ||
items.map((item, index, array) => () => { | ||
const res = func(item, index, array); | ||
return res; | ||
}) | ||
); | ||
const objectify = async (func, input) => { | ||
const keys = Object.keys(input); | ||
const results = await func(Object.values(input)); | ||
return Object.fromEntries(keys.map((key, index) => [key, results[index]])); | ||
}; | ||
const start = () => { | ||
printLn(); | ||
return update(); | ||
PromiseTools2.allObj = async (input) => { | ||
return objectify((arr) => Promise.all(arr), input); | ||
}; | ||
const finish = () => { | ||
finished = true; | ||
const output = update(); | ||
printLn(); | ||
return output; | ||
PromiseTools2.allLimitObj = async (limit, input, noThrow = false) => { | ||
return objectify((items) => { | ||
return PromiseTools2.allLimit(limit, items, noThrow); | ||
}, input); | ||
}; | ||
return { | ||
next, | ||
set, | ||
reset, | ||
update, | ||
start, | ||
finish, | ||
max | ||
}; | ||
}; | ||
})(PromiseTools || (PromiseTools = {})); | ||
var getDeferred = PromiseTools.getDeferred; | ||
var all = PromiseTools.all; | ||
var allLimit = PromiseTools.allLimit; | ||
var each = PromiseTools.each; | ||
var eachLimit = PromiseTools.eachLimit; | ||
var map = PromiseTools.map; | ||
var mapLimit = PromiseTools.mapLimit; | ||
var allObj = PromiseTools.allObj; | ||
var allLimitObj = PromiseTools.allLimitObj; | ||
// src/tools/errorHandling.ts | ||
var tryOr = async (orValue, func, ...args) => { | ||
try { | ||
return await func(...args); | ||
} catch (err) { | ||
return orValue; | ||
} | ||
}; | ||
var retry = async (maxTries = 10, delay = 0, suppress = true, run = result(void 0)) => { | ||
const loop = async (attempt, lastErr) => { | ||
if (attempt >= maxTries) { | ||
if (!suppress) | ||
throw lastErr; | ||
return void 0; | ||
} | ||
// src/tools/ErrorTools.ts | ||
var ErrorTools; | ||
((ErrorTools2) => { | ||
ErrorTools2.tryOr = async (orValue, func, ...args) => { | ||
try { | ||
const result2 = await run(attempt); | ||
return result2; | ||
return await func(...args); | ||
} catch (err) { | ||
if (delay) | ||
await wait(delay); | ||
return await loop(attempt + 1, err); | ||
return orValue; | ||
} | ||
}; | ||
return await loop(0); | ||
}; | ||
var retryOr = async (orValue, maxTries = 10, delay = 0, suppress = true, run = result(orValue)) => tryOr(orValue, () => retry(maxTries, delay, suppress, run)); | ||
// src/tools/PromiseTools.ts | ||
var getDeferred = () => { | ||
let resolve2, reject2; | ||
const promise = new Promise((res, rej) => { | ||
resolve2 = (arg) => { | ||
res(arg); | ||
return promise; | ||
ErrorTools2.retry = async (maxTries = 10, delay = 0, suppress = true, run = fn.result(void 0)) => { | ||
const loop = async (attempt, lastErr) => { | ||
if (attempt >= maxTries) { | ||
if (!suppress) | ||
throw lastErr; | ||
return void 0; | ||
} | ||
try { | ||
const result = await run(attempt); | ||
return result; | ||
} catch (err) { | ||
if (delay) | ||
await wait(delay); | ||
return await loop(attempt + 1, err); | ||
} | ||
}; | ||
reject2 = (...args) => { | ||
rej(...args); | ||
return promise; | ||
}; | ||
}); | ||
return { | ||
resolve: resolve2, | ||
reject: reject2, | ||
promise | ||
return await loop(0); | ||
}; | ||
}; | ||
var all = async (promises) => { | ||
await Promise.all(promises); | ||
}; | ||
var allLimit = (limit, items, noThrow = false) => { | ||
let runningCount = 0; | ||
let errors = []; | ||
let remaining = [...items]; | ||
const result2 = []; | ||
const deferred = getDeferred(); | ||
const update = () => { | ||
if (remaining.length === 0 && runningCount === 0) { | ||
if (errors.length && !noThrow) { | ||
deferred.reject(errors); | ||
return; | ||
ErrorTools2.retryOr = async (orValue, maxTries = 10, delay = 0, suppress = true, run = fn.result(orValue)) => ErrorTools2.tryOr(orValue, () => ErrorTools2.retry(maxTries, delay, suppress, run)); | ||
})(ErrorTools || (ErrorTools = {})); | ||
var tryOr = ErrorTools.tryOr; | ||
var retry = ErrorTools.retry; | ||
var retryOr = ErrorTools.retryOr; | ||
// src/tools/ColourTools.ts | ||
var ColourTools; | ||
((ColourTools2) => { | ||
ColourTools2.namedColours = { | ||
aliceblue: [240, 248, 255], | ||
antiquewhite: [250, 235, 215], | ||
aqua: [0, 255, 255], | ||
aquamarine: [127, 255, 212], | ||
azure: [240, 255, 255], | ||
beige: [245, 245, 220], | ||
bisque: [255, 228, 196], | ||
black: [0, 0, 0], | ||
blanchedalmond: [255, 235, 205], | ||
blue: [0, 0, 255], | ||
blueviolet: [138, 43, 226], | ||
brown: [165, 42, 42], | ||
burlywood: [222, 184, 135], | ||
cadetblue: [95, 158, 160], | ||
chartreuse: [127, 255, 0], | ||
chocolate: [210, 105, 30], | ||
coral: [255, 127, 80], | ||
cornflowerblue: [100, 149, 237], | ||
cornsilk: [255, 248, 220], | ||
crimson: [220, 20, 60], | ||
cyan: [0, 255, 255], | ||
darkblue: [0, 0, 139], | ||
darkcyan: [0, 139, 139], | ||
darkgoldenrod: [184, 134, 11], | ||
darkgray: [169, 169, 169], | ||
darkgreen: [0, 100, 0], | ||
darkgrey: [169, 169, 169], | ||
darkkhaki: [189, 183, 107], | ||
darkmagenta: [139, 0, 139], | ||
darkolivegreen: [85, 107, 47], | ||
darkorange: [255, 140, 0], | ||
darkorchid: [153, 50, 204], | ||
darkred: [139, 0, 0], | ||
darksalmon: [233, 150, 122], | ||
darkseagreen: [143, 188, 143], | ||
darkslateblue: [72, 61, 139], | ||
darkslategray: [47, 79, 79], | ||
darkslategrey: [47, 79, 79], | ||
darkturquoise: [0, 206, 209], | ||
darkviolet: [148, 0, 211], | ||
deeppink: [255, 20, 147], | ||
deepskyblue: [0, 191, 255], | ||
dimgray: [105, 105, 105], | ||
dimgrey: [105, 105, 105], | ||
dodgerblue: [30, 144, 255], | ||
firebrick: [178, 34, 34], | ||
floralwhite: [255, 250, 240], | ||
forestgreen: [34, 139, 34], | ||
fractal: [128, 128, 128], | ||
fuchsia: [255, 0, 255], | ||
gainsboro: [220, 220, 220], | ||
ghostwhite: [248, 248, 255], | ||
gold: [255, 215, 0], | ||
goldenrod: [218, 165, 32], | ||
gray0: [0, 0, 0], | ||
gray1: [3, 3, 3], | ||
gray2: [5, 5, 5], | ||
gray3: [8, 8, 8], | ||
gray4: [10, 10, 10], | ||
gray5: [13, 13, 13], | ||
gray6: [15, 15, 15], | ||
gray7: [18, 18, 18], | ||
gray8: [20, 20, 20], | ||
gray9: [23, 23, 23], | ||
gray10: [26, 26, 26], | ||
gray11: [28, 28, 28], | ||
gray12: [31, 31, 31], | ||
gray13: [33, 33, 33], | ||
gray14: [36, 36, 36], | ||
gray15: [38, 38, 38], | ||
gray16: [41, 41, 41], | ||
gray17: [43, 43, 43], | ||
gray18: [46, 46, 46], | ||
gray19: [48, 48, 48], | ||
gray20: [51, 51, 51], | ||
gray21: [54, 54, 54], | ||
gray22: [56, 56, 56], | ||
gray23: [59, 59, 59], | ||
gray24: [61, 61, 61], | ||
gray25: [64, 64, 64], | ||
gray26: [66, 66, 66], | ||
gray27: [69, 69, 69], | ||
gray28: [71, 71, 71], | ||
gray29: [74, 74, 74], | ||
gray30: [77, 77, 77], | ||
gray31: [79, 79, 79], | ||
gray32: [82, 82, 82], | ||
gray33: [84, 84, 84], | ||
gray34: [87, 87, 87], | ||
gray35: [89, 89, 89], | ||
gray36: [92, 92, 92], | ||
gray37: [94, 94, 94], | ||
gray38: [97, 97, 97], | ||
gray39: [99, 99, 99], | ||
gray40: [102, 102, 102], | ||
gray41: [105, 105, 105], | ||
gray42: [107, 107, 107], | ||
gray43: [110, 110, 110], | ||
gray44: [112, 112, 112], | ||
gray45: [115, 115, 115], | ||
gray46: [117, 117, 117], | ||
gray47: [120, 120, 120], | ||
gray48: [122, 122, 122], | ||
gray49: [125, 125, 125], | ||
gray50: [127, 127, 127], | ||
gray51: [130, 130, 130], | ||
gray52: [133, 133, 133], | ||
gray53: [135, 135, 135], | ||
gray54: [138, 138, 138], | ||
gray55: [140, 140, 140], | ||
gray56: [143, 143, 143], | ||
gray57: [145, 145, 145], | ||
gray58: [148, 148, 148], | ||
gray59: [150, 150, 150], | ||
gray60: [153, 153, 153], | ||
gray61: [156, 156, 156], | ||
gray62: [158, 158, 158], | ||
gray63: [161, 161, 161], | ||
gray64: [163, 163, 163], | ||
gray65: [166, 166, 166], | ||
gray66: [168, 168, 168], | ||
gray67: [171, 171, 171], | ||
gray68: [173, 173, 173], | ||
gray69: [176, 176, 176], | ||
gray70: [179, 179, 179], | ||
gray71: [181, 181, 181], | ||
gray72: [184, 184, 184], | ||
gray73: [186, 186, 186], | ||
gray74: [189, 189, 189], | ||
gray75: [191, 191, 191], | ||
gray76: [194, 194, 194], | ||
gray77: [196, 196, 196], | ||
gray78: [199, 199, 199], | ||
gray79: [201, 201, 201], | ||
gray80: [204, 204, 204], | ||
gray81: [207, 207, 207], | ||
gray82: [209, 209, 209], | ||
gray83: [212, 212, 212], | ||
gray84: [214, 214, 214], | ||
gray85: [217, 217, 217], | ||
gray86: [219, 219, 219], | ||
gray87: [222, 222, 222], | ||
gray88: [224, 224, 224], | ||
gray89: [227, 227, 227], | ||
gray90: [229, 229, 229], | ||
gray91: [232, 232, 232], | ||
gray92: [235, 235, 235], | ||
gray93: [237, 237, 237], | ||
gray94: [240, 240, 240], | ||
gray95: [242, 242, 242], | ||
gray96: [245, 245, 245], | ||
gray97: [247, 247, 247], | ||
gray98: [250, 250, 250], | ||
gray99: [252, 252, 252], | ||
gray100: [255, 255, 255], | ||
gray: [126, 126, 126], | ||
green: [0, 128, 0], | ||
greenyellow: [173, 255, 47], | ||
grey: [128, 128, 128], | ||
honeydew: [240, 255, 240], | ||
hotpink: [255, 105, 180], | ||
indianred: [205, 92, 92], | ||
indigo: [75, 0, 130], | ||
ivory: [255, 255, 240], | ||
khaki: [240, 230, 140], | ||
lavender: [230, 230, 250], | ||
lavenderblush: [255, 240, 245], | ||
lawngreen: [124, 252, 0], | ||
lemonchiffon: [255, 250, 205], | ||
lightblue: [173, 216, 230], | ||
lightcoral: [240, 128, 128], | ||
lightcyan: [224, 255, 255], | ||
lightgoldenrodyellow: [250, 250, 210], | ||
lightgray: [211, 211, 211], | ||
lightgreen: [144, 238, 144], | ||
lightgrey: [211, 211, 211], | ||
lightpink: [255, 182, 193], | ||
lightsalmon: [255, 160, 122], | ||
lightseagreen: [32, 178, 170], | ||
lightskyblue: [135, 206, 250], | ||
lightslategray: [119, 136, 153], | ||
lightslategrey: [119, 136, 153], | ||
lightsteelblue: [176, 196, 222], | ||
lightyellow: [255, 255, 224], | ||
lime: [0, 255, 0], | ||
limegreen: [50, 205, 50], | ||
linen: [250, 240, 230], | ||
magenta: [255, 0, 255], | ||
maroon: [128, 0, 0], | ||
mediumaquamarine: [102, 205, 170], | ||
mediumblue: [0, 0, 205], | ||
mediumorchid: [186, 85, 211], | ||
mediumpurple: [147, 112, 219], | ||
mediumseagreen: [60, 179, 113], | ||
mediumslateblue: [123, 104, 238], | ||
mediumspringgreen: [0, 250, 154], | ||
mediumturquoise: [72, 209, 204], | ||
mediumvioletred: [199, 21, 133], | ||
midnightblue: [25, 25, 112], | ||
mintcream: [245, 255, 250], | ||
mistyrose: [255, 228, 225], | ||
moccasin: [255, 228, 181], | ||
navajowhite: [255, 222, 173], | ||
navy: [0, 0, 128], | ||
none: [0, 0, 0], | ||
oldlace: [253, 245, 230], | ||
olive: [128, 128, 0], | ||
olivedrab: [107, 142, 35], | ||
orange: [255, 165, 0], | ||
orangered: [255, 69, 0], | ||
orchid: [218, 112, 214], | ||
palegoldenrod: [238, 232, 170], | ||
palegreen: [152, 251, 152], | ||
paleturquoise: [175, 238, 238], | ||
palevioletred: [219, 112, 147], | ||
papayawhip: [255, 239, 213], | ||
peachpuff: [255, 218, 185], | ||
peru: [205, 133, 63], | ||
pink: [255, 192, 203], | ||
plum: [221, 160, 221], | ||
powderblue: [176, 224, 230], | ||
purple: [128, 0, 128], | ||
red: [255, 0, 0], | ||
rosybrown: [188, 143, 143], | ||
royalblue: [65, 105, 225], | ||
saddlebrown: [139, 69, 19], | ||
salmon: [250, 128, 114], | ||
sandybrown: [244, 164, 96], | ||
seagreen: [46, 139, 87], | ||
seashell: [255, 245, 238], | ||
sienna: [160, 82, 45], | ||
silver: [192, 192, 192], | ||
skyblue: [135, 206, 235], | ||
slateblue: [106, 90, 205], | ||
slategray: [112, 128, 144], | ||
slategrey: [112, 128, 144], | ||
snow: [255, 250, 250], | ||
springgreen: [0, 255, 127], | ||
steelblue: [70, 130, 180], | ||
tan: [210, 180, 140], | ||
teal: [0, 128, 128], | ||
thistle: [216, 191, 216], | ||
tomato: [255, 99, 71], | ||
turquoise: [64, 224, 208], | ||
violet: [238, 130, 238], | ||
wheat: [245, 222, 179], | ||
white: [255, 255, 255], | ||
whitesmoke: [245, 245, 245], | ||
yellow: [255, 255, 0], | ||
yellowgreen: [154, 205, 50] | ||
}; | ||
const limitValue = (val) => Math.max(0, Math.min(255, val)); | ||
const roundMinMax = (value, min = 0, max = 255) => Math.min(max, Math.max(min, Math.round(value))); | ||
ColourTools2.parse = (input) => { | ||
const trimmed = (input + "").trim(); | ||
if (ColourTools2.namedColours[trimmed]) { | ||
return ColourTools2.namedColours[trimmed]; | ||
} | ||
if (/^rgb/.test(trimmed) || /([0-9]{1,3}(,|\s|\/)+){2}[0-9]{1,3}/.test(trimmed)) { | ||
const stripped = trimmed.replace(/[^0-9,/]/g, ""); | ||
const [r, g, b] = [...stripped.split(/[^0-9]/).map(Number), 0, 0, 0].map(limitValue); | ||
return [r, g, b]; | ||
} | ||
if (/^#/.test(trimmed) || /^([0-9A-F]{3}|[0-9A-F]{6})$/.test(trimmed)) { | ||
const stripped = trimmed.toUpperCase().replace(/[^0-9A-F]/g, ""); | ||
let hexs = []; | ||
if (/^[0-9A-F]{3}$/.test(stripped)) { | ||
hexs = [...stripped.match(/[0-9A-F]{1}/g) || []].map((hex) => parseInt(hex, 16)); | ||
hexs = hexs.map((val) => val * 17); | ||
} | ||
deferred.resolve(result2); | ||
return; | ||
if (/^[0-9A-F]{6}$/.test(stripped)) { | ||
hexs = [...stripped.match(/[0-9A-F]{2}/g) || []].map((hex) => parseInt(hex, 16)); | ||
} | ||
const [r, g, b] = hexs.map(limitValue); | ||
return [r, g, b]; | ||
} | ||
if (runningCount < limit && remaining.length) { | ||
const next = remaining.shift(); | ||
const index = items.indexOf(next); | ||
run(next, index); | ||
return [0, 0, 0]; | ||
}; | ||
ColourTools2.toHex = (colour) => { | ||
const hexs = colour.map((val) => (val ?? 0).toString(16).padStart(2, "0")); | ||
return `#${hexs.join("")}`; | ||
}; | ||
ColourTools2.getLuminance = ([r, g, b]) => { | ||
const [y, u, v] = ColourTools2.toYUV([r, g, b]); | ||
return y; | ||
}; | ||
ColourTools2.toYUV = ([r, g, b]) => { | ||
const y = MathsTools.fixFloat(0.299 * (r ?? 0) + 0.587 * (g ?? 0) + 0.114 * (b ?? 0)); | ||
const u = MathsTools.fixFloat(-0.14713 * (r ?? 0) - 0.28886 * (g ?? 0) + 0.436 * (b ?? 0)); | ||
const v = MathsTools.fixFloat(0.615 * (r ?? 0) - 0.51499 * (g ?? 0) - 0.10001 * (b ?? 0)); | ||
return [y, u, v]; | ||
}; | ||
ColourTools2.toHSL = (colour, round = true) => { | ||
const r = colour[0] / 255; | ||
const g = colour[1] / 255; | ||
const b = colour[2] / 255; | ||
const M = Math.max(r, g, b); | ||
const m = M - Math.min(r, g, b); | ||
let d = 0; | ||
if (m) { | ||
if (M === r) { | ||
d = (g - b) / m; | ||
} else { | ||
if (M === g) { | ||
d = 2 + (b - r) / m; | ||
} else { | ||
d = 4 + (r - g) / m; | ||
} | ||
} | ||
} | ||
const result = [ | ||
60 * d < 0 ? 60 * d + 360 : 60 * d, | ||
100 * (m ? M <= 0.5 ? m / (2 * M - m) : m / (2 - (2 * M - m)) : 0), | ||
100 * (2 * M - m) / 2 | ||
]; | ||
if (round) { | ||
return [roundMinMax(result[0], 0, 360), roundMinMax(result[1], 0, 100), roundMinMax(result[2], 0, 100)]; | ||
} | ||
return result; | ||
}; | ||
const run = async (prom, index) => { | ||
runningCount++; | ||
try { | ||
result2[index] = await prom(index); | ||
} catch (err) { | ||
errors.push(err); | ||
ColourTools2.fromHSL = (hsl, round = true) => { | ||
const h = hsl[0]; | ||
const s = hsl[1] / 100; | ||
const l = hsl[2] / 100; | ||
const k = (n) => (n + h / 30) % 12; | ||
const a = s * Math.min(l, 1 - l); | ||
const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1))); | ||
const result = [255 * f(0), 255 * f(8), 255 * f(4)]; | ||
if (round) { | ||
return [roundMinMax(result[0], 0, 255), roundMinMax(result[1], 0, 255), roundMinMax(result[2], 0, 255)]; | ||
} | ||
runningCount--; | ||
update(); | ||
return result; | ||
}; | ||
for (let i = 0; i < Math.min(limit, items.length); i++) { | ||
update(); | ||
} | ||
if (!items || items.length === 0) { | ||
deferred.resolve(result2); | ||
} | ||
return deferred.promise; | ||
}; | ||
var each = async (items, func) => { | ||
await Promise.all(items.map((item, index, array) => func(item, index, array))); | ||
}; | ||
var eachLimit = async (limit, items, func) => { | ||
await allLimit( | ||
limit, | ||
items.map((item, index, array) => () => func(item, index, array)) | ||
); | ||
}; | ||
var map = async (items, func) => { | ||
const result2 = []; | ||
await Promise.all( | ||
items.map(async (item, index, array) => { | ||
const res = await func(item, index, array); | ||
result2[index] = res; | ||
}) | ||
); | ||
return result2; | ||
}; | ||
var mapLimit = async (limit, items, func) => await allLimit( | ||
limit, | ||
items.map((item, index, array) => () => { | ||
const res = func(item, index, array); | ||
return res; | ||
}) | ||
); | ||
var objectify = async (func, input) => { | ||
const keys = Object.keys(input); | ||
const results = await func(Object.values(input)); | ||
return Object.fromEntries(keys.map((key, index) => [key, results[index]])); | ||
}; | ||
var allObj = async (input) => { | ||
return objectify((arr) => Promise.all(arr), input); | ||
}; | ||
var allLimitObj = async (limit, input, noThrow = false) => { | ||
return objectify((items) => { | ||
return allLimit(limit, items, noThrow); | ||
}, input); | ||
}; | ||
var PromiseTools = { | ||
getDeferred, | ||
all, | ||
allLimit, | ||
each, | ||
eachLimit, | ||
map, | ||
mapLimit, | ||
allObj, | ||
allLimitObj | ||
}; | ||
ColourTools2.invertColour = ([r, g, b]) => [255 - r, 255 - g, 255 - b]; | ||
const white = [255, 255, 255]; | ||
const black = [0, 0, 0]; | ||
ColourTools2.getContrastedColour = (colour) => ColourTools2.getLuminance(colour) > 186 ? black : white; | ||
ColourTools2.getLimitedColour = (colour, checkFn, adjustFn) => { | ||
const hsl = ColourTools2.toHSL(colour); | ||
if (checkFn(hsl)) { | ||
const adjusted = adjustFn(hsl); | ||
const out = ColourTools2.fromHSL(adjusted); | ||
return out; | ||
} | ||
return colour; | ||
}; | ||
})(ColourTools || (ColourTools = {})); | ||
// src/tools/ObjectTools.ts | ||
var remodel = (obj, func) => Object.fromEntries(func(Object.entries(obj)) ?? Object.entries(obj)); | ||
var remodelEach = (obj, func) => Object.fromEntries(Object.entries(obj).map((entry, index, entries2) => func(entry, index, entries2) ?? entry)); | ||
var map2 = (obj, func) => remodel(obj, (entries2) => entries2.map(([key, value], index) => func(key, value, index))); | ||
var mapValues = (obj, func) => remodel(obj, (entries2) => entries2.map(([key, value], index) => [key, func(key, value, index)])); | ||
var mapKeys = (obj, func) => remodel(obj, (entries2) => entries2.map(([key, value], index) => [func(key, value, index), value])); | ||
var filter = (obj, func) => remodel(obj, (entries2) => entries2.filter(([key, value], index) => func(key, value, index))); | ||
var clean = (obj) => filter(obj, (key, value) => value !== void 0); | ||
var ObjectTools = { | ||
remodel, | ||
remodelEach, | ||
map: map2, | ||
mapValues, | ||
mapKeys, | ||
filter, | ||
clean | ||
}; | ||
// src/tools/symbols.ts | ||
@@ -1060,9 +1463,9 @@ var symbols = { | ||
} | ||
add(id, fn) { | ||
add(id, fn2) { | ||
const promise = this.getPromise(id).then(async () => { | ||
const result2 = await fn(); | ||
const result = await fn2(); | ||
const pauseTime = this.pauseTimes.get(id) ?? -1; | ||
if (pauseTime >= 0) | ||
await wait(pauseTime); | ||
return result2; | ||
return result; | ||
}); | ||
@@ -1077,478 +1480,2 @@ this.promises.set(id, promise); | ||
var queue = new QueueManager(); | ||
// src/tools/ColourTools.ts | ||
var ColourTools_exports = {}; | ||
__export(ColourTools_exports, { | ||
fromHSL: () => fromHSL, | ||
getContrastedColour: () => getContrastedColour, | ||
getLimitedColour: () => getLimitedColour, | ||
getLuminance: () => getLuminance, | ||
invertColour: () => invertColour, | ||
namedColours: () => namedColours, | ||
parse: () => parse, | ||
toHSL: () => toHSL, | ||
toHex: () => toHex, | ||
toYUV: () => toYUV | ||
}); | ||
var namedColours = { | ||
aliceblue: [240, 248, 255], | ||
antiquewhite: [250, 235, 215], | ||
aqua: [0, 255, 255], | ||
aquamarine: [127, 255, 212], | ||
azure: [240, 255, 255], | ||
beige: [245, 245, 220], | ||
bisque: [255, 228, 196], | ||
black: [0, 0, 0], | ||
blanchedalmond: [255, 235, 205], | ||
blue: [0, 0, 255], | ||
blueviolet: [138, 43, 226], | ||
brown: [165, 42, 42], | ||
burlywood: [222, 184, 135], | ||
cadetblue: [95, 158, 160], | ||
chartreuse: [127, 255, 0], | ||
chocolate: [210, 105, 30], | ||
coral: [255, 127, 80], | ||
cornflowerblue: [100, 149, 237], | ||
cornsilk: [255, 248, 220], | ||
crimson: [220, 20, 60], | ||
cyan: [0, 255, 255], | ||
darkblue: [0, 0, 139], | ||
darkcyan: [0, 139, 139], | ||
darkgoldenrod: [184, 134, 11], | ||
darkgray: [169, 169, 169], | ||
darkgreen: [0, 100, 0], | ||
darkgrey: [169, 169, 169], | ||
darkkhaki: [189, 183, 107], | ||
darkmagenta: [139, 0, 139], | ||
darkolivegreen: [85, 107, 47], | ||
darkorange: [255, 140, 0], | ||
darkorchid: [153, 50, 204], | ||
darkred: [139, 0, 0], | ||
darksalmon: [233, 150, 122], | ||
darkseagreen: [143, 188, 143], | ||
darkslateblue: [72, 61, 139], | ||
darkslategray: [47, 79, 79], | ||
darkslategrey: [47, 79, 79], | ||
darkturquoise: [0, 206, 209], | ||
darkviolet: [148, 0, 211], | ||
deeppink: [255, 20, 147], | ||
deepskyblue: [0, 191, 255], | ||
dimgray: [105, 105, 105], | ||
dimgrey: [105, 105, 105], | ||
dodgerblue: [30, 144, 255], | ||
firebrick: [178, 34, 34], | ||
floralwhite: [255, 250, 240], | ||
forestgreen: [34, 139, 34], | ||
fractal: [128, 128, 128], | ||
fuchsia: [255, 0, 255], | ||
gainsboro: [220, 220, 220], | ||
ghostwhite: [248, 248, 255], | ||
gold: [255, 215, 0], | ||
goldenrod: [218, 165, 32], | ||
gray0: [0, 0, 0], | ||
gray1: [3, 3, 3], | ||
gray2: [5, 5, 5], | ||
gray3: [8, 8, 8], | ||
gray4: [10, 10, 10], | ||
gray5: [13, 13, 13], | ||
gray6: [15, 15, 15], | ||
gray7: [18, 18, 18], | ||
gray8: [20, 20, 20], | ||
gray9: [23, 23, 23], | ||
gray10: [26, 26, 26], | ||
gray11: [28, 28, 28], | ||
gray12: [31, 31, 31], | ||
gray13: [33, 33, 33], | ||
gray14: [36, 36, 36], | ||
gray15: [38, 38, 38], | ||
gray16: [41, 41, 41], | ||
gray17: [43, 43, 43], | ||
gray18: [46, 46, 46], | ||
gray19: [48, 48, 48], | ||
gray20: [51, 51, 51], | ||
gray21: [54, 54, 54], | ||
gray22: [56, 56, 56], | ||
gray23: [59, 59, 59], | ||
gray24: [61, 61, 61], | ||
gray25: [64, 64, 64], | ||
gray26: [66, 66, 66], | ||
gray27: [69, 69, 69], | ||
gray28: [71, 71, 71], | ||
gray29: [74, 74, 74], | ||
gray30: [77, 77, 77], | ||
gray31: [79, 79, 79], | ||
gray32: [82, 82, 82], | ||
gray33: [84, 84, 84], | ||
gray34: [87, 87, 87], | ||
gray35: [89, 89, 89], | ||
gray36: [92, 92, 92], | ||
gray37: [94, 94, 94], | ||
gray38: [97, 97, 97], | ||
gray39: [99, 99, 99], | ||
gray40: [102, 102, 102], | ||
gray41: [105, 105, 105], | ||
gray42: [107, 107, 107], | ||
gray43: [110, 110, 110], | ||
gray44: [112, 112, 112], | ||
gray45: [115, 115, 115], | ||
gray46: [117, 117, 117], | ||
gray47: [120, 120, 120], | ||
gray48: [122, 122, 122], | ||
gray49: [125, 125, 125], | ||
gray50: [127, 127, 127], | ||
gray51: [130, 130, 130], | ||
gray52: [133, 133, 133], | ||
gray53: [135, 135, 135], | ||
gray54: [138, 138, 138], | ||
gray55: [140, 140, 140], | ||
gray56: [143, 143, 143], | ||
gray57: [145, 145, 145], | ||
gray58: [148, 148, 148], | ||
gray59: [150, 150, 150], | ||
gray60: [153, 153, 153], | ||
gray61: [156, 156, 156], | ||
gray62: [158, 158, 158], | ||
gray63: [161, 161, 161], | ||
gray64: [163, 163, 163], | ||
gray65: [166, 166, 166], | ||
gray66: [168, 168, 168], | ||
gray67: [171, 171, 171], | ||
gray68: [173, 173, 173], | ||
gray69: [176, 176, 176], | ||
gray70: [179, 179, 179], | ||
gray71: [181, 181, 181], | ||
gray72: [184, 184, 184], | ||
gray73: [186, 186, 186], | ||
gray74: [189, 189, 189], | ||
gray75: [191, 191, 191], | ||
gray76: [194, 194, 194], | ||
gray77: [196, 196, 196], | ||
gray78: [199, 199, 199], | ||
gray79: [201, 201, 201], | ||
gray80: [204, 204, 204], | ||
gray81: [207, 207, 207], | ||
gray82: [209, 209, 209], | ||
gray83: [212, 212, 212], | ||
gray84: [214, 214, 214], | ||
gray85: [217, 217, 217], | ||
gray86: [219, 219, 219], | ||
gray87: [222, 222, 222], | ||
gray88: [224, 224, 224], | ||
gray89: [227, 227, 227], | ||
gray90: [229, 229, 229], | ||
gray91: [232, 232, 232], | ||
gray92: [235, 235, 235], | ||
gray93: [237, 237, 237], | ||
gray94: [240, 240, 240], | ||
gray95: [242, 242, 242], | ||
gray96: [245, 245, 245], | ||
gray97: [247, 247, 247], | ||
gray98: [250, 250, 250], | ||
gray99: [252, 252, 252], | ||
gray100: [255, 255, 255], | ||
gray: [126, 126, 126], | ||
green: [0, 128, 0], | ||
greenyellow: [173, 255, 47], | ||
grey: [128, 128, 128], | ||
honeydew: [240, 255, 240], | ||
hotpink: [255, 105, 180], | ||
indianred: [205, 92, 92], | ||
indigo: [75, 0, 130], | ||
ivory: [255, 255, 240], | ||
khaki: [240, 230, 140], | ||
lavender: [230, 230, 250], | ||
lavenderblush: [255, 240, 245], | ||
lawngreen: [124, 252, 0], | ||
lemonchiffon: [255, 250, 205], | ||
lightblue: [173, 216, 230], | ||
lightcoral: [240, 128, 128], | ||
lightcyan: [224, 255, 255], | ||
lightgoldenrodyellow: [250, 250, 210], | ||
lightgray: [211, 211, 211], | ||
lightgreen: [144, 238, 144], | ||
lightgrey: [211, 211, 211], | ||
lightpink: [255, 182, 193], | ||
lightsalmon: [255, 160, 122], | ||
lightseagreen: [32, 178, 170], | ||
lightskyblue: [135, 206, 250], | ||
lightslategray: [119, 136, 153], | ||
lightslategrey: [119, 136, 153], | ||
lightsteelblue: [176, 196, 222], | ||
lightyellow: [255, 255, 224], | ||
lime: [0, 255, 0], | ||
limegreen: [50, 205, 50], | ||
linen: [250, 240, 230], | ||
magenta: [255, 0, 255], | ||
maroon: [128, 0, 0], | ||
mediumaquamarine: [102, 205, 170], | ||
mediumblue: [0, 0, 205], | ||
mediumorchid: [186, 85, 211], | ||
mediumpurple: [147, 112, 219], | ||
mediumseagreen: [60, 179, 113], | ||
mediumslateblue: [123, 104, 238], | ||
mediumspringgreen: [0, 250, 154], | ||
mediumturquoise: [72, 209, 204], | ||
mediumvioletred: [199, 21, 133], | ||
midnightblue: [25, 25, 112], | ||
mintcream: [245, 255, 250], | ||
mistyrose: [255, 228, 225], | ||
moccasin: [255, 228, 181], | ||
navajowhite: [255, 222, 173], | ||
navy: [0, 0, 128], | ||
none: [0, 0, 0], | ||
oldlace: [253, 245, 230], | ||
olive: [128, 128, 0], | ||
olivedrab: [107, 142, 35], | ||
orange: [255, 165, 0], | ||
orangered: [255, 69, 0], | ||
orchid: [218, 112, 214], | ||
palegoldenrod: [238, 232, 170], | ||
palegreen: [152, 251, 152], | ||
paleturquoise: [175, 238, 238], | ||
palevioletred: [219, 112, 147], | ||
papayawhip: [255, 239, 213], | ||
peachpuff: [255, 218, 185], | ||
peru: [205, 133, 63], | ||
pink: [255, 192, 203], | ||
plum: [221, 160, 221], | ||
powderblue: [176, 224, 230], | ||
purple: [128, 0, 128], | ||
red: [255, 0, 0], | ||
rosybrown: [188, 143, 143], | ||
royalblue: [65, 105, 225], | ||
saddlebrown: [139, 69, 19], | ||
salmon: [250, 128, 114], | ||
sandybrown: [244, 164, 96], | ||
seagreen: [46, 139, 87], | ||
seashell: [255, 245, 238], | ||
sienna: [160, 82, 45], | ||
silver: [192, 192, 192], | ||
skyblue: [135, 206, 235], | ||
slateblue: [106, 90, 205], | ||
slategray: [112, 128, 144], | ||
slategrey: [112, 128, 144], | ||
snow: [255, 250, 250], | ||
springgreen: [0, 255, 127], | ||
steelblue: [70, 130, 180], | ||
tan: [210, 180, 140], | ||
teal: [0, 128, 128], | ||
thistle: [216, 191, 216], | ||
tomato: [255, 99, 71], | ||
turquoise: [64, 224, 208], | ||
violet: [238, 130, 238], | ||
wheat: [245, 222, 179], | ||
white: [255, 255, 255], | ||
whitesmoke: [245, 245, 245], | ||
yellow: [255, 255, 0], | ||
yellowgreen: [154, 205, 50] | ||
}; | ||
var limitValue = (val) => Math.max(0, Math.min(255, val)); | ||
var roundMinMax = (value, min = 0, max = 255) => Math.min(max, Math.max(min, Math.round(value))); | ||
var parse = (input) => { | ||
const trimmed = (input + "").trim(); | ||
if (namedColours[trimmed]) { | ||
return namedColours[trimmed]; | ||
} | ||
if (/^rgb/.test(trimmed) || /([0-9]{1,3}(,|\s|\/)+){2}[0-9]{1,3}/.test(trimmed)) { | ||
const stripped = trimmed.replace(/[^0-9,/]/g, ""); | ||
const [r, g, b] = [...stripped.split(/[^0-9]/).map(Number), 0, 0, 0].map(limitValue); | ||
return [r, g, b]; | ||
} | ||
if (/^#/.test(trimmed) || /^([0-9A-F]{3}|[0-9A-F]{6})$/.test(trimmed)) { | ||
const stripped = trimmed.toUpperCase().replace(/[^0-9A-F]/g, ""); | ||
let hexs = []; | ||
if (/^[0-9A-F]{3}$/.test(stripped)) { | ||
hexs = [...stripped.match(/[0-9A-F]{1}/g) || []].map((hex) => parseInt(hex, 16)); | ||
hexs = hexs.map((val) => val * 17); | ||
} | ||
if (/^[0-9A-F]{6}$/.test(stripped)) { | ||
hexs = [...stripped.match(/[0-9A-F]{2}/g) || []].map((hex) => parseInt(hex, 16)); | ||
} | ||
const [r, g, b] = hexs.map(limitValue); | ||
return [r, g, b]; | ||
} | ||
return [0, 0, 0]; | ||
}; | ||
var toHex = (colour) => { | ||
const hexs = colour.map((val) => (val ?? 0).toString(16).padStart(2, "0")); | ||
return `#${hexs.join("")}`; | ||
}; | ||
var getLuminance = ([r, g, b]) => { | ||
const [y, u, v] = toYUV([r, g, b]); | ||
return y; | ||
}; | ||
var toYUV = ([r, g, b]) => { | ||
const y = fixFloat(0.299 * (r ?? 0) + 0.587 * (g ?? 0) + 0.114 * (b ?? 0)); | ||
const u = fixFloat(-0.14713 * (r ?? 0) - 0.28886 * (g ?? 0) + 0.436 * (b ?? 0)); | ||
const v = fixFloat(0.615 * (r ?? 0) - 0.51499 * (g ?? 0) - 0.10001 * (b ?? 0)); | ||
return [y, u, v]; | ||
}; | ||
var toHSL = (colour, round2 = true) => { | ||
const r = colour[0] / 255; | ||
const g = colour[1] / 255; | ||
const b = colour[2] / 255; | ||
const M = Math.max(r, g, b); | ||
const m = M - Math.min(r, g, b); | ||
let d = 0; | ||
if (m) { | ||
if (M === r) { | ||
d = (g - b) / m; | ||
} else { | ||
if (M === g) { | ||
d = 2 + (b - r) / m; | ||
} else { | ||
d = 4 + (r - g) / m; | ||
} | ||
} | ||
} | ||
const result2 = [ | ||
60 * d < 0 ? 60 * d + 360 : 60 * d, | ||
100 * (m ? M <= 0.5 ? m / (2 * M - m) : m / (2 - (2 * M - m)) : 0), | ||
100 * (2 * M - m) / 2 | ||
]; | ||
if (round2) { | ||
return [roundMinMax(result2[0], 0, 360), roundMinMax(result2[1], 0, 100), roundMinMax(result2[2], 0, 100)]; | ||
} | ||
return result2; | ||
}; | ||
var fromHSL = (hsl, round2 = true) => { | ||
const h = hsl[0]; | ||
const s = hsl[1] / 100; | ||
const l = hsl[2] / 100; | ||
const k = (n) => (n + h / 30) % 12; | ||
const a = s * Math.min(l, 1 - l); | ||
const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1))); | ||
const result2 = [255 * f(0), 255 * f(8), 255 * f(4)]; | ||
if (round2) { | ||
return [roundMinMax(result2[0], 0, 255), roundMinMax(result2[1], 0, 255), roundMinMax(result2[2], 0, 255)]; | ||
} | ||
return result2; | ||
}; | ||
var invertColour = ([r, g, b]) => [255 - r, 255 - g, 255 - b]; | ||
var white = [255, 255, 255]; | ||
var black = [0, 0, 0]; | ||
var getContrastedColour = (colour) => getLuminance(colour) > 186 ? black : white; | ||
var getLimitedColour = (colour, checkFn, adjustFn) => { | ||
const hsl = toHSL(colour); | ||
if (checkFn(hsl)) { | ||
const adjusted = adjustFn(hsl); | ||
const out = fromHSL(adjusted); | ||
return out; | ||
} | ||
return colour; | ||
}; | ||
// src/tools/StringTools.ts | ||
var StringTools_exports = {}; | ||
__export(StringTools_exports, { | ||
angloise: () => angloise, | ||
capitalise: () => capitalise, | ||
clean: () => clean2, | ||
clx: () => clx, | ||
fromCamelCase: () => fromCamelCase, | ||
fromSlugCase: () => fromSlugCase, | ||
fromSnakeCase: () => fromSnakeCase, | ||
fromSpaced: () => fromSpaced, | ||
toCamelCase: () => toCamelCase, | ||
toCapitalisedSpaced: () => toCapitalisedSpaced, | ||
toCharacterSeparated: () => toCharacterSeparated, | ||
toLowerCamelCase: () => toLowerCamelCase, | ||
toLowerSlugCase: () => toLowerSlugCase, | ||
toLowerSnakeCase: () => toLowerSnakeCase, | ||
toLowerSpaced: () => toLowerSpaced, | ||
toSlugCase: () => toSlugCase, | ||
toSnakeCase: () => toSnakeCase, | ||
toSpaced: () => toSpaced, | ||
toUpperCamelCase: () => toUpperCamelCase, | ||
toUpperSlugCase: () => toUpperSlugCase, | ||
toUpperSnakeCase: () => toUpperSnakeCase, | ||
toUpperSpaced: () => toUpperSpaced | ||
}); | ||
var capitalise = (input = "") => (input || "").split(/\s/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" "); | ||
var angloise = (input) => input.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); | ||
var clean2 = (input = "") => angloise([input].flat().join(" ")).replace(/\s{1,}/g, " ").replace(/[^A-Za-z0-9 ]/gi, ""); | ||
var caseHandler = (overrideSplitter) => { | ||
const getSplit = (input = "") => { | ||
if (overrideSplitter) | ||
return overrideSplitter(input); | ||
const arr = [input].flat(); | ||
return arr.map((s) => clean2(s.replace(/-|_/g, " ")).split(" ")).flat().filter((s) => s.length); | ||
}; | ||
const toCamelCase2 = (input, capitaliseFirst = false) => { | ||
const split = getSplit(input); | ||
return split.map((word, index) => index === 0 && !capitaliseFirst ? word.toLowerCase() : capitalise(word)).join(""); | ||
}; | ||
const toLowerCamelCase2 = (input) => toCamelCase2(input, false); | ||
const toUpperCamelCase2 = (input) => toCamelCase2(input, true); | ||
const toCharacterSeparated2 = (input, char, toUpper = false) => { | ||
const split = getSplit(input); | ||
return split.map((word, index) => toUpper ? word.toUpperCase() : word.toLowerCase()).join(char); | ||
}; | ||
const toSlugCase2 = (input, toUpper = false) => toCharacterSeparated2(input, "-", toUpper); | ||
const toLowerSlugCase2 = (input) => toSlugCase2(input, false); | ||
const toUpperSlugCase2 = (input) => toSlugCase2(input, true); | ||
const toSnakeCase2 = (input, toUpper = false) => toCharacterSeparated2(input, "_", toUpper); | ||
const toLowerSnakeCase2 = (input) => toSnakeCase2(input, false); | ||
const toUpperSnakeCase2 = (input) => toSnakeCase2(input, true); | ||
const toSpaced2 = (input, toUpper = false) => toCharacterSeparated2(input, " ", toUpper); | ||
const toLowerSpaced2 = (input) => toSpaced2(input, false); | ||
const toUpperSpaced2 = (input) => toSpaced2(input, true); | ||
const toCapitalisedSpaced2 = (input) => capitalise(toSpaced2(input, false)); | ||
return { | ||
toLowerCamelCase: toLowerCamelCase2, | ||
toUpperCamelCase: toUpperCamelCase2, | ||
toCamelCase: toCamelCase2, | ||
toLowerSlugCase: toLowerSlugCase2, | ||
toUpperSlugCase: toUpperSlugCase2, | ||
toSlugCase: toSlugCase2, | ||
toLowerSnakeCase: toLowerSnakeCase2, | ||
toUpperSnakeCase: toUpperSnakeCase2, | ||
toSnakeCase: toSnakeCase2, | ||
toLowerSpaced: toLowerSpaced2, | ||
toUpperSpaced: toUpperSpaced2, | ||
toCapitalisedSpaced: toCapitalisedSpaced2, | ||
toSpaced: toSpaced2, | ||
toCharacterSeparated: toCharacterSeparated2 | ||
}; | ||
}; | ||
var standardCaseHandler = caseHandler(); | ||
var { | ||
toLowerCamelCase, | ||
toUpperCamelCase, | ||
toCamelCase, | ||
toLowerSlugCase, | ||
toUpperSlugCase, | ||
toSlugCase, | ||
toLowerSnakeCase, | ||
toUpperSnakeCase, | ||
toSnakeCase, | ||
toLowerSpaced, | ||
toUpperSpaced, | ||
toCapitalisedSpaced, | ||
toSpaced, | ||
toCharacterSeparated | ||
} = standardCaseHandler; | ||
var fromSlugCase = standardCaseHandler; | ||
var fromSnakeCase = standardCaseHandler; | ||
var fromSpaced = standardCaseHandler; | ||
var fromCamelCase = caseHandler( | ||
(input) => [input].flat().map((s) => clean2(s)).map( | ||
(s) => s.replace(/([A-Z])/g, " $1").replace(/-|_/g, " ").trim() | ||
).map((s) => s.split(" ")).flat() | ||
); | ||
var processClxArray = (arr) => arr.filter(Boolean).map((item) => { | ||
if (typeof item === "string") | ||
return item; | ||
if (item instanceof Array) { | ||
return processClxArray(item); | ||
} | ||
if (typeof item === "object") { | ||
return Object.keys(item).filter((key) => item[key]).join(" "); | ||
} | ||
}).flat(); | ||
var clx = (...args) => processClxArray(args).join(" "); | ||
// src/index.ts | ||
var MathTools = MathsTools_exports; | ||
var { filters: filters2, maps: maps2, sorts: sorts2, reduces: reduces2, everys: everys2 } = fn_exports; | ||
// Annotate the CommonJS export names for ESM import in node: | ||
@@ -1561,2 +1488,3 @@ 0 && (module.exports = { | ||
DECADE, | ||
ErrorTools, | ||
HOUR, | ||
@@ -1567,3 +1495,2 @@ MILLENNIUM, | ||
MONTH, | ||
MathTools, | ||
MathsTools, | ||
@@ -1590,2 +1517,3 @@ ObjectTools, | ||
everys, | ||
ff, | ||
filters, | ||
@@ -1592,0 +1520,0 @@ fn, |
@@ -5,2 +5,7 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ | ||
testEnvironment: 'node', | ||
}; | ||
globals: { | ||
'ts-jest': { | ||
diagnostics: false | ||
} | ||
} | ||
}; |
{ | ||
"name": "swiss-ak", | ||
"version": "2.2.1", | ||
"version": "2.3.0", | ||
"author": "Jack Cannon <jackc@annon.co.uk> (http://c.annon.co.uk/)", | ||
@@ -33,5 +33,6 @@ "license": "MIT", | ||
"docs": "swiss-docs -i src -o README.md", | ||
"test": "jest --no-cache", | ||
"prepublishOnly": "yarn test && yarn build" | ||
"test": "yarn build && yarn test:raw", | ||
"test:raw": "jest --no-cache", | ||
"prepublishOnly": "yarn test" | ||
} | ||
} |
export * from './tools/types'; | ||
export * from './tools/times'; | ||
export * from './tools/waiters'; | ||
export * from './tools/fn'; | ||
export * from './tools/timer'; | ||
export * from './tools/progressBar'; | ||
export * from './tools/errorHandling'; | ||
export * from './tools/PromiseTools'; | ||
export * from './tools/ArrayTools'; | ||
export * from './tools/ObjectTools'; | ||
export * from './tools/StringTools'; | ||
export * from './tools/PromiseTools'; | ||
export * from './tools/ErrorTools'; | ||
export * from './tools/MathsTools'; | ||
export * from './tools/ColourTools'; | ||
export * from './tools/TimeTools'; | ||
@@ -14,21 +18,10 @@ export * from './tools/symbols'; | ||
import * as times from './tools/times'; | ||
import * as waiters from './tools/waiters'; | ||
import * as progressBar from './tools/progressBar'; | ||
import * as ColourTools from './tools/ColourTools'; | ||
export * as StringTools from './tools/StringTools'; | ||
// import * as progressBar from './tools/progressBar'; | ||
// import * as ColourTools from './tools/ColourTools'; | ||
// export * as StringTools from './tools/StringTools'; | ||
export { clx, ClxType } from './tools/StringTools'; | ||
// export { clx, ClxType } from './tools/StringTools'; | ||
import * as MathsTools from './tools/MathsTools'; | ||
export { MathsTools }; | ||
// an alias | ||
export const MathTools = MathsTools; | ||
// export * as MathsTools from './tools/MathsTools'; | ||
export { times, waiters, progressBar, ColourTools }; | ||
// Higher order functions | ||
import * as fn from './tools/fn'; | ||
const { filters, maps, sorts, reduces, everys } = fn; | ||
export { fn, filters, maps, sorts, reduces, everys }; | ||
// export { progressBar }; |
@@ -1,3 +0,3 @@ | ||
import { sorts } from './fn'; | ||
import { fixFloat } from './MathsTools'; | ||
import { fn } from './fn'; | ||
import { MathsTools } from './MathsTools'; | ||
@@ -10,321 +10,353 @@ //<!-- DOCS: 100 --> | ||
*/ | ||
export namespace ArrayTools { | ||
/**<!-- DOCS: 101 ### --> | ||
* utils | ||
* | ||
* - `ArrayTools.utils` | ||
* | ||
* Small helper functions that may help, but aren't important enough to be in ArrayTools directly | ||
*/ | ||
export namespace utils { | ||
/**<!-- DOCS: 101 #### --> | ||
* isNumString | ||
* | ||
* - `ArrayTools.utils.isNumString` | ||
* | ||
* Returns true if the given string is a number | ||
*/ | ||
export const isNumString = (text: string) => Boolean(text.match(/^[0-9-.]+$/)); | ||
/**<!-- DOCS: ### --> | ||
* range | ||
* | ||
* - `range` | ||
* - `ArrayTools.range` | ||
* | ||
* Returns an array of the given length, where each value is equal to it's index | ||
* e.g. [0, 1, 2, ..., length] | ||
* | ||
* ```typescript | ||
* ArrayTools.range(3); // [0, 1, 2] | ||
* ArrayTools.range(5); // [0, 1, 2, 3, 4] | ||
* ArrayTools.range(10); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | ||
* | ||
* ArrayTools.range(3, 2); // [0, 2, 4] | ||
* ArrayTools.range(5, 2); // [0, 2, 4, 6, 8] | ||
* ArrayTools.range(10, 10); // [0, 10, 20, 30, 40, 50, 60, 70, 80, 90] | ||
* ``` | ||
*/ | ||
export const range = (length: number = 1, multiplier: number = 1, offset: number = 0): number[] => | ||
new Array(Math.floor(length)).fill(1).map((v, i) => fixFloat(i * multiplier) + offset); | ||
/**<!-- DOCS: 101 #### --> | ||
* partitionNums | ||
* | ||
* - `ArrayTools.utils.partitionNums` | ||
* | ||
* Splits a string into an array of strings and numbers | ||
*/ | ||
export const partitionNums = (ignoreCase: boolean) => (name: string) => | ||
(ignoreCase ? name.toLowerCase() : name).split(/([0-9]+)/).map((s) => (isNumString(s) ? Number(s) : s)); | ||
} | ||
type UnwrapArray<T> = T extends Array<infer U> ? U : T; | ||
type UnwrapArrays<T extends [...any[]]> = T extends [infer Head, ...infer Tail] ? [UnwrapArray<Head>, ...UnwrapArrays<Tail>] : []; | ||
/**<!-- DOCS: ### --> | ||
* range | ||
* | ||
* - `range` | ||
* - `ArrayTools.range` | ||
* | ||
* Returns an array of the given length, where each value is equal to it's index | ||
* e.g. [0, 1, 2, ..., length] | ||
* | ||
* ```typescript | ||
* ArrayTools.range(3); // [0, 1, 2] | ||
* ArrayTools.range(5); // [0, 1, 2, 3, 4] | ||
* ArrayTools.range(10); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | ||
* | ||
* ArrayTools.range(3, 2); // [0, 2, 4] | ||
* ArrayTools.range(5, 2); // [0, 2, 4, 6, 8] | ||
* ArrayTools.range(10, 10); // [0, 10, 20, 30, 40, 50, 60, 70, 80, 90] | ||
* ``` | ||
*/ | ||
export const range = (length: number = 1, multiplier: number = 1, offset: number = 0): number[] => | ||
new Array(Math.floor(length)).fill(1).map((v, i) => MathsTools.fixFloat(i * multiplier) + offset); | ||
const zipFn = <T extends [...any[]]>(length: number, arrs: T): UnwrapArrays<T>[] => | ||
range(length).map((i) => arrs.map((arr) => (arr || [])[i])) as UnwrapArrays<T>[]; | ||
type UnwrapArray<T> = T extends Array<infer U> ? U : T; | ||
type UnwrapArrays<T extends [...any[]]> = T extends [infer Head, ...infer Tail] ? [UnwrapArray<Head>, ...UnwrapArrays<Tail>] : []; | ||
/**<!-- DOCS: ### --> | ||
* zip | ||
* | ||
* - `zip` | ||
* - `ArrayTools.zip` | ||
* | ||
* Converts multiple arrays into an array of 'tuples' for each value at the corresponding indexes. | ||
* | ||
* Limited to the length of the shortest provided array | ||
* | ||
* Inspired by python's 'zip' | ||
* | ||
* ```typescript | ||
* ArrayTools.zip([1, 2, 3, 4], ['a', 'b', 'c']); // [ [1, 'a'], [2, 'b'], [3, 'c'] ] | ||
* ``` | ||
*/ | ||
export const zip = <T extends [...any[]]>(...arrs: T): UnwrapArrays<T>[] => | ||
zipFn(Math.min(...(arrs.length ? arrs : [[]]).map((arr) => (arr || []).length)), arrs); | ||
const zipFn = <T extends [...any[]]>(length: number, arrs: T): UnwrapArrays<T>[] => | ||
range(length).map((i) => arrs.map((arr) => (arr || [])[i])) as UnwrapArrays<T>[]; | ||
/**<!-- DOCS: ### --> | ||
* zipMax | ||
* | ||
* - `zipMax` | ||
* - `ArrayTools.zipMax` | ||
* | ||
* Converts multiple arrays into an array of 'tuples' for each value at the corresponding indexes. | ||
* | ||
* Unlike `zip`, it will match the length of the longest provided array, filling in any missing values with `undefined` | ||
* | ||
* Inspired by python's 'zip' | ||
* | ||
* ```typescript | ||
* ArrayTools.zipMax([1, 2, 3, 4], ['a', 'b', 'c']); //[ [ 1, 'a' ], [ 2, 'b' ], [ 3, 'c' ], [ 4, undefined ] ] | ||
* ``` | ||
*/ | ||
export const zipMax = <T extends [...any[]]>(...arrs: T): UnwrapArrays<T>[] => | ||
zipFn(Math.max(...(arrs.length ? arrs : [[]]).map((arr) => (arr || []).length)), arrs); | ||
/**<!-- DOCS: ### --> | ||
* zip | ||
* | ||
* - `zip` | ||
* - `ArrayTools.zip` | ||
* | ||
* Converts multiple arrays into an array of 'tuples' for each value at the corresponding indexes. | ||
* | ||
* Limited to the length of the shortest provided array | ||
* | ||
* Inspired by python's 'zip' | ||
* | ||
* ```typescript | ||
* ArrayTools.zip([1, 2, 3, 4], ['a', 'b', 'c']); // [ [1, 'a'], [2, 'b'], [3, 'c'] ] | ||
* ``` | ||
*/ | ||
export const zip = <T extends [...any[]]>(...arrs: T): UnwrapArrays<T>[] => | ||
zipFn(Math.min(...(arrs.length ? arrs : [[]]).map((arr) => (arr || []).length)), arrs); | ||
/**<!-- DOCS: ### --> | ||
* sortByMapped | ||
* | ||
* - `sortByMapped` | ||
* - `ArrayTools.sortByMapped` | ||
* | ||
* Sort an array by a mapped form of the values, but returning the initial values | ||
* | ||
* ```typescript | ||
* ArrayTools.sortByMapped(['2p', '3p', '1p'], (v) => Number(v.replace('p', ''))); // ['1p', '2p', '3p'] | ||
* ArrayTools.sortByMapped( | ||
* ['2p', '3p', '1p'], | ||
* (v) => Number(v.replace('p', '')), | ||
* (a, b) => b - a | ||
* ); // ['3p', '2p', '1p'] | ||
* ``` | ||
*/ | ||
export const sortByMapped = <T = string, M = number>( | ||
arr: T[], | ||
mapFn: (value: T, index: number, array: T[]) => M, | ||
sortFn: (a: M, b: M) => number = sorts.asc | ||
): T[] => | ||
zip(arr, arr.map(mapFn)) | ||
.sort((a, b) => sortFn(a[1] as M, b[1] as M)) | ||
.map(([v]) => v); | ||
/**<!-- DOCS: ### --> | ||
* zipMax | ||
* | ||
* - `zipMax` | ||
* - `ArrayTools.zipMax` | ||
* | ||
* Converts multiple arrays into an array of 'tuples' for each value at the corresponding indexes. | ||
* | ||
* Unlike `zip`, it will match the length of the longest provided array, filling in any missing values with `undefined` | ||
* | ||
* Inspired by python's 'zip' | ||
* | ||
* ```typescript | ||
* ArrayTools.zipMax([1, 2, 3, 4], ['a', 'b', 'c']); //[ [ 1, 'a' ], [ 2, 'b' ], [ 3, 'c' ], [ 4, undefined ] ] | ||
* ``` | ||
*/ | ||
export const zipMax = <T extends [...any[]]>(...arrs: T): UnwrapArrays<T>[] => | ||
zipFn(Math.max(...(arrs.length ? arrs : [[]]).map((arr) => (arr || []).length)), arrs); | ||
/**<!-- DOCS: ### --> | ||
* randomise | ||
* | ||
* - `randomise` | ||
* - `ArrayTools.randomise` | ||
* | ||
* Returns a clone of the provided array with it's items in a random order | ||
* | ||
* ```typescript | ||
* ArrayTools.randomise([1, 2, 3, 4, 5, 6]); // [ 5, 3, 4, 1, 2, 6 ] | ||
* ArrayTools.randomise([1, 2, 3, 4, 5, 6]); // [ 5, 1, 3, 2, 4, 6 ] | ||
* ArrayTools.randomise([1, 2, 3, 4, 5, 6]); // [ 6, 1, 4, 5, 2, 3 ] | ||
* ArrayTools.randomise([1, 2, 3, 4, 5, 6]); // [ 1, 4, 5, 2, 3, 6 ] | ||
* ArrayTools.randomise([1, 2, 3, 4, 5, 6]); // [ 2, 6, 1, 3, 4, 5 ] | ||
* ``` | ||
*/ | ||
export const randomise = <T = string>(arr: T[]): T[] => sortByMapped(arr, () => Math.random()); | ||
/**<!-- DOCS: ### --> | ||
* sortByMapped | ||
* | ||
* - `sortByMapped` | ||
* - `ArrayTools.sortByMapped` | ||
* | ||
* Sort an array by a mapped form of the values, but returning the initial values | ||
* | ||
* ```typescript | ||
* ArrayTools.sortByMapped(['2p', '3p', '1p'], (v) => Number(v.replace('p', ''))); // ['1p', '2p', '3p'] | ||
* ArrayTools.sortByMapped( | ||
* ['2p', '3p', '1p'], | ||
* (v) => Number(v.replace('p', '')), | ||
* (a, b) => b - a | ||
* ); // ['3p', '2p', '1p'] | ||
* ``` | ||
*/ | ||
export const sortByMapped = <T = string, M = number>( | ||
arr: T[], | ||
mapFn: (value: T, index: number, array: T[]) => M, | ||
sortFn: (a: M, b: M) => number = fn.asc | ||
): T[] => | ||
zip(arr, arr.map(mapFn)) | ||
.sort((a, b) => sortFn(a[1] as M, b[1] as M)) | ||
.map(([v]) => v); | ||
/**<!-- DOCS: ### --> | ||
* reverse | ||
* | ||
* - `reverse` | ||
* - `ArrayTools.reverse` | ||
* | ||
* Returns a new array with the order reversed without affecting original array | ||
* | ||
* ```typescript | ||
* const arr1 = [1, 2, 3]; | ||
* arr1 // [1, 2, 3] | ||
* arr1.reverse(); // [3, 2, 1] | ||
* arr1 // [3, 2, 1] | ||
* | ||
* const arr2 = [1, 2, 3]; | ||
* arr2 // [1, 2, 3] | ||
* ArrayTools.reverse(arr2); // [3, 2, 1] | ||
* arr2 // [1, 2, 3] | ||
* ``` | ||
*/ | ||
export const reverse = <T = string>(arr: T[]): T[] => [...arr].reverse(); | ||
/**<!-- DOCS: ### --> | ||
* randomise | ||
* | ||
* - `randomise` | ||
* - `ArrayTools.randomise` | ||
* | ||
* Returns a clone of the provided array with it's items in a random order | ||
* | ||
* ```typescript | ||
* ArrayTools.randomise([1, 2, 3, 4, 5, 6]); // [ 5, 3, 4, 1, 2, 6 ] | ||
* ArrayTools.randomise([1, 2, 3, 4, 5, 6]); // [ 5, 1, 3, 2, 4, 6 ] | ||
* ArrayTools.randomise([1, 2, 3, 4, 5, 6]); // [ 6, 1, 4, 5, 2, 3 ] | ||
* ArrayTools.randomise([1, 2, 3, 4, 5, 6]); // [ 1, 4, 5, 2, 3, 6 ] | ||
* ArrayTools.randomise([1, 2, 3, 4, 5, 6]); // [ 2, 6, 1, 3, 4, 5 ] | ||
* ``` | ||
*/ | ||
export const randomise = <T = string>(arr: T[]): T[] => sortByMapped(arr, () => Math.random()); | ||
/**<!-- DOCS: ### --> | ||
* entries | ||
* | ||
* - `entries` | ||
* - `ArrayTools.entries` | ||
* | ||
* Returns array of 'tuples' of index/value pairs | ||
* | ||
* ```typescript | ||
* const arr = ['a', 'b', 'c']; | ||
* ArrayTools.entries(arr); // [ [0, 'a'], [1, 'b'], [2, 'c'] ] | ||
* | ||
* for (let [index, value] of entries(arr)) { | ||
* console.log(index); // 0, 1, 2 | ||
* console.log(value); // 'a', 'b', 'c' | ||
* } | ||
* ``` | ||
*/ | ||
export const entries = <T = string>(arr: T[]): [number, T][] => zip(range(arr.length), arr) as any; | ||
/**<!-- DOCS: ### --> | ||
* reverse | ||
* | ||
* - `reverse` | ||
* - `ArrayTools.reverse` | ||
* | ||
* Returns a new array with the order reversed without affecting original array | ||
* | ||
* ```typescript | ||
* const arr1 = [1, 2, 3]; | ||
* arr1 // [1, 2, 3] | ||
* arr1.reverse(); // [3, 2, 1] | ||
* arr1 // [3, 2, 1] | ||
* | ||
* const arr2 = [1, 2, 3]; | ||
* arr2 // [1, 2, 3] | ||
* ArrayTools.reverse(arr2); // [3, 2, 1] | ||
* arr2 // [1, 2, 3] | ||
* ``` | ||
*/ | ||
export const reverse = <T = string>(arr: T[]): T[] => [...arr].reverse(); | ||
/**<!-- DOCS: ### --> | ||
* repeat | ||
* | ||
* - `repeat` | ||
* - `ArrayTools.repeat` | ||
* | ||
* Returns an array with the given items repeated | ||
* | ||
* ```typescript | ||
* ArrayTools.repeat(5, 'a'); // [ 'a', 'a', 'a', 'a', 'a' ] | ||
* ArrayTools.repeat(5, 'a', 'b'); // [ 'a', 'b', 'a', 'b', 'a' ] | ||
* ``` | ||
*/ | ||
export const repeat = <T = string>(maxLength: number, ...items: T[]): T[] => { | ||
const simple = new Array(maxLength).fill(items[0]); | ||
return items.length === 1 ? simple : simple.map((v, i) => items[i % items.length]); | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* entries | ||
* | ||
* - `entries` | ||
* - `ArrayTools.entries` | ||
* | ||
* Returns array of 'tuples' of index/value pairs | ||
* | ||
* ```typescript | ||
* const arr = ['a', 'b', 'c']; | ||
* ArrayTools.entries(arr); // [ [0, 'a'], [1, 'b'], [2, 'c'] ] | ||
* | ||
* for (let [index, value] of entries(arr)) { | ||
* console.log(index); // 0, 1, 2 | ||
* console.log(value); // 'a', 'b', 'c' | ||
* } | ||
* ``` | ||
*/ | ||
export const entries = <T = string>(arr: T[]): [number, T][] => zip(range(arr.length), arr) as any; | ||
/**<!-- DOCS: ### --> | ||
* roll | ||
* | ||
* - `roll` | ||
* - `ArrayTools.roll` | ||
* | ||
* 'Roll' the array by a given amount so that is has a new first item. Length and contents remain the same, but the order is changed | ||
* | ||
* ```typescript | ||
* ArrayTools.roll(1, [0, 1, 2, 3, 4, 5, 6, 7]); // [ 1, 2, 3, 4, 5, 6, 7, 0 ] | ||
* ArrayTools.roll(4, [0, 1, 2, 3, 4, 5, 6, 7]); // [ 4, 5, 6, 7, 0, 1, 2, 3 ] | ||
* ``` | ||
*/ | ||
export const roll = <T extends unknown>(distance: number, arr: T[]): T[] => [ | ||
...arr.slice(distance % arr.length), | ||
...arr.slice(0, distance % arr.length) | ||
]; | ||
/**<!-- DOCS: ### --> | ||
* repeat | ||
* | ||
* - `repeat` | ||
* - `ArrayTools.repeat` | ||
* | ||
* Returns an array with the given items repeated | ||
* | ||
* ```typescript | ||
* ArrayTools.repeat(5, 'a'); // [ 'a', 'a', 'a', 'a', 'a' ] | ||
* ArrayTools.repeat(5, 'a', 'b'); // [ 'a', 'b', 'a', 'b', 'a' ] | ||
* ``` | ||
*/ | ||
export const repeat = <T = string>(maxLength: number, ...items: T[]): T[] => { | ||
const simple = new Array(maxLength).fill(items[0]); | ||
return items.length === 1 ? simple : simple.map((v, i) => items[i % items.length]); | ||
}; | ||
const isNumString = (text: string) => Boolean(text.match(/^[0-9]+$/)); | ||
const partitionNums = (ignoreCase: boolean) => (name: string) => | ||
(ignoreCase ? name.toLowerCase() : name).split(/([0-9]+)/).map((s) => (isNumString(s) ? Number(s) : s)); | ||
/**<!-- DOCS: ### --> | ||
* roll | ||
* | ||
* - `roll` | ||
* - `ArrayTools.roll` | ||
* | ||
* 'Roll' the array by a given amount so that is has a new first item. Length and contents remain the same, but the order is changed | ||
* | ||
* ```typescript | ||
* ArrayTools.roll(1, [0, 1, 2, 3, 4, 5, 6, 7]); // [ 1, 2, 3, 4, 5, 6, 7, 0 ] | ||
* ArrayTools.roll(4, [0, 1, 2, 3, 4, 5, 6, 7]); // [ 4, 5, 6, 7, 0, 1, 2, 3 ] | ||
* ``` | ||
*/ | ||
export const roll = <T extends unknown>(distance: number, arr: T[]): T[] => [ | ||
...arr.slice(distance % arr.length), | ||
...arr.slice(0, distance % arr.length) | ||
]; | ||
/**<!-- DOCS: ### --> | ||
* sortNumberedText | ||
* | ||
* - `sortNumberedText` | ||
* - `ArrayTools.sortNumberedText` | ||
* | ||
* Alphabetically sorts a list of strings, but keeps multi-digit numbers in numerical order (rather than alphabetical) | ||
* | ||
* ```typescript | ||
* const names = ['name1', 'name10', 'name2', 'foo20', 'foo10', 'foo9']; | ||
* names.sort(); // [ 'foo10', 'foo20', 'foo9', 'name1', 'name10', 'name2' ] | ||
* ArrayTools.sortNumberedText(names); // [ 'foo9', 'foo10', 'foo20', 'name1', 'name2', 'name10' ] | ||
* ``` | ||
*/ | ||
export const sortNumberedText = (texts: string[], ignoreCase: boolean = true): string[] => { | ||
return sortByMapped(texts, partitionNums(ignoreCase), (a, b) => { | ||
for (let i in a) { | ||
const result = sorts.asc(a[i], b[i]); | ||
if (result !== 0) return result; | ||
/**<!-- DOCS: ### --> | ||
* sortNumberedText | ||
* | ||
* - `sortNumberedText` | ||
* - `ArrayTools.sortNumberedText` | ||
* | ||
* Alphabetically sorts a list of strings, but keeps multi-digit numbers in numerical order (rather than alphabetical) | ||
* | ||
* ```typescript | ||
* const names = ['name1', 'name10', 'name2', 'foo20', 'foo10', 'foo9']; | ||
* names.sort(); // [ 'foo10', 'foo20', 'foo9', 'name1', 'name10', 'name2' ] | ||
* ArrayTools.sortNumberedText(names); // [ 'foo9', 'foo10', 'foo20', 'name1', 'name2', 'name10' ] | ||
* ``` | ||
*/ | ||
export const sortNumberedText = (texts: string[], ignoreCase: boolean = true): string[] => { | ||
return sortByMapped(texts, utils.partitionNums(ignoreCase), (a, b) => { | ||
for (let i in a) { | ||
const result = fn.asc(a[i], b[i]); | ||
if (result !== 0) return result; | ||
} | ||
return 0; | ||
}); | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* partition | ||
* | ||
* - `partition` | ||
* - `ArrayTools.partition` | ||
* | ||
* Breaks an array into smaller arrays of a given size | ||
* | ||
* ```typescript | ||
* ArrayTools.partition([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3); // [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ], [ 10 ] ] | ||
* ``` | ||
*/ | ||
export const partition = <T extends unknown>(array: T[], partitionSize: number = Math.ceil(array.length / 2)): T[][] => { | ||
const numParts = Math.ceil(array.length / partitionSize); | ||
const result: T[][] = []; | ||
for (let i = 0; i < numParts; i++) { | ||
result.push(array.slice(i * partitionSize, (i + 1) * partitionSize)); | ||
} | ||
return 0; | ||
}); | ||
}; | ||
return result; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* partition | ||
* | ||
* - `partition` | ||
* - `ArrayTools.partition` | ||
* | ||
* Breaks an array into smaller arrays of a given size | ||
* | ||
* ```typescript | ||
* ArrayTools.partition([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3); // [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ], [ 10 ] ] | ||
* ``` | ||
*/ | ||
export const partition = <T extends unknown>(array: T[], partitionSize: number = Math.ceil(array.length / 2)): T[][] => { | ||
const numParts = Math.ceil(array.length / partitionSize); | ||
const result: T[][] = []; | ||
for (let i = 0; i < numParts; i++) { | ||
result.push(array.slice(i * partitionSize, (i + 1) * partitionSize)); | ||
} | ||
return result; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* groupObj | ||
* | ||
* - `groupObj` | ||
* - `ArrayTools.groupObj` | ||
* | ||
* Group items from an array into an object of arrays, based on a given map function. | ||
* | ||
* ```typescript | ||
* const arr = [ | ||
* { group: 1, name: 'a' }, | ||
* { group: 2, name: 'b' }, | ||
* { group: 1, name: 'c' }, | ||
* ]; | ||
* ArrayTools.groupObj(arr, item => item.id); // { | ||
* // 1: [ { group: 1, name: 'a' }, { group: 1, name: 'c' } ], | ||
* // 2: [ { group: 2, name: 'b' } ] | ||
* // } | ||
* ``` | ||
*/ | ||
export const groupObj = <T extends unknown>( | ||
array: T[], | ||
mapFn: (item: T, index: number, arr: T[]) => string | number | ||
): { [id: string | number]: T[] } => { | ||
const result: { [id: string | number]: T[] } = {}; | ||
/**<!-- DOCS: ### --> | ||
* groupObj | ||
* | ||
* - `groupObj` | ||
* - `ArrayTools.groupObj` | ||
* | ||
* Group items from an array into an object of arrays, based on a given map function. | ||
* | ||
* ```typescript | ||
* const arr = [ | ||
* { group: 1, name: 'a' }, | ||
* { group: 2, name: 'b' }, | ||
* { group: 1, name: 'c' }, | ||
* ]; | ||
* ArrayTools.groupObj(arr, item => item.id); // { | ||
* // 1: [ { group: 1, name: 'a' }, { group: 1, name: 'c' } ], | ||
* // 2: [ { group: 2, name: 'b' } ] | ||
* // } | ||
* ``` | ||
*/ | ||
export const groupObj = <T extends unknown>( | ||
array: T[], | ||
mapFn: (item: T, index: number, arr: T[]) => string | number | ||
): { [id: string | number]: T[] } => { | ||
const result: { [id: string | number]: T[] } = {}; | ||
array.forEach((item, index) => { | ||
const key = mapFn(item, index, array); | ||
array.forEach((item, index) => { | ||
const key = mapFn(item, index, array); | ||
if (key === undefined) return; | ||
if (key === undefined) return; | ||
if (!result[key]) result[key] = []; | ||
result[key].push(item); | ||
}); | ||
if (!result[key]) result[key] = []; | ||
result[key].push(item); | ||
}); | ||
return result; | ||
}; | ||
return result; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* group | ||
* | ||
* - `group` | ||
* - `ArrayTools.group` | ||
* | ||
* Group items from an array into an array of arrays, based on a given map function. | ||
* | ||
* ```typescript | ||
* const arr = [ | ||
* { group: 1, name: 'a' }, | ||
* { group: 2, name: 'b' }, | ||
* { group: 1, name: 'c' }, | ||
* ]; | ||
* ArrayTools.groupObj(arr, item => item.id); // [ | ||
* // [ { group: 1, name: 'a' }, { group: 1, name: 'c' } ], | ||
* // [ { group: 2, name: 'b' } ] | ||
* // ] | ||
* ``` | ||
*/ | ||
export const group = <T extends unknown>(array: T[], mapFn: (item: T, index: number, arr: T[]) => string | number): T[][] => { | ||
const obj = groupObj(array, mapFn); | ||
return Object.values(obj); | ||
}; | ||
} | ||
/**<!-- DOCS: ### --> | ||
* group | ||
* | ||
* - `group` | ||
* - `ArrayTools.group` | ||
* | ||
* Group items from an array into an array of arrays, based on a given map function. | ||
* | ||
* ```typescript | ||
* const arr = [ | ||
* { group: 1, name: 'a' }, | ||
* { group: 2, name: 'b' }, | ||
* { group: 1, name: 'c' }, | ||
* ]; | ||
* ArrayTools.groupObj(arr, item => item.id); // [ | ||
* // [ { group: 1, name: 'a' }, { group: 1, name: 'c' } ], | ||
* // [ { group: 2, name: 'b' } ] | ||
* // ] | ||
* ``` | ||
*/ | ||
export const group = <T extends unknown>(array: T[], mapFn: (item: T, index: number, arr: T[]) => string | number): T[][] => { | ||
const obj = groupObj(array, mapFn); | ||
return Object.values(obj); | ||
}; | ||
export const ArrayTools = { | ||
range, | ||
zip, | ||
zipMax, | ||
sortByMapped, | ||
randomise, | ||
reverse, | ||
entries, | ||
repeat, | ||
roll, | ||
sortNumberedText, | ||
partition, | ||
groupObj, | ||
group, | ||
utils: { | ||
isNumString, | ||
partitionNums | ||
} | ||
}; | ||
/** ALIAS - range */ | ||
export const range = ArrayTools.range; | ||
/** ALIAS - zip */ | ||
export const zip = ArrayTools.zip; | ||
/** ALIAS - zipMax */ | ||
export const zipMax = ArrayTools.zipMax; | ||
/** ALIAS - sortByMapped */ | ||
export const sortByMapped = ArrayTools.sortByMapped; | ||
/** ALIAS - randomise */ | ||
export const randomise = ArrayTools.randomise; | ||
/** ALIAS - reverse */ | ||
export const reverse = ArrayTools.reverse; | ||
/** ALIAS - entries */ | ||
export const entries = ArrayTools.entries; | ||
/** ALIAS - repeat */ | ||
export const repeat = ArrayTools.repeat; | ||
/** ALIAS - roll */ | ||
export const roll = ArrayTools.roll; | ||
/** ALIAS - sortNumberedText */ | ||
export const sortNumberedText = ArrayTools.sortNumberedText; | ||
/** ALIAS - partition */ | ||
export const partition = ArrayTools.partition; | ||
/** ALIAS - groupObj */ | ||
export const groupObj = ArrayTools.groupObj; | ||
/** ALIAS - group */ | ||
export const group = ArrayTools.group; |
@@ -1,2 +0,2 @@ | ||
import { fixFloat } from './MathsTools'; | ||
import { MathsTools } from './MathsTools'; | ||
@@ -9,545 +9,799 @@ //<!-- DOCS: 150 --> | ||
*/ | ||
export namespace ColourTools { | ||
/**<!-- DOCS: ### --> | ||
* ColourValues | ||
* | ||
* - `ColourTools.ColourValues` | ||
* | ||
* A type with 3 numbers: | ||
* - red [0-255] | ||
* - green [0-255] | ||
* - blue [0-255] | ||
*/ | ||
export type ColourValues = [number, number, number]; | ||
/**<!-- DOCS: ### --> | ||
* ColourValues | ||
* | ||
* - `ColourTools.ColourValues` | ||
* | ||
* A type with 3 numbers: | ||
* - red [0-255] | ||
* - green [0-255] | ||
* - blue [0-255] | ||
*/ | ||
export type ColourValues = [number, number, number]; | ||
/**<!-- DOCS: ### --> | ||
* HSLValues | ||
* | ||
* - `ColourTools.HSLValues` | ||
* | ||
* A type with 3 numbers: | ||
* - hue [0-360] | ||
* - saturation [0-100] | ||
* - lightness [0-100] | ||
*/ | ||
export type HSLValues = [number, number, number]; | ||
/**<!-- DOCS: ### --> | ||
* HSLValues | ||
* | ||
* - `ColourTools.HSLValues` | ||
* | ||
* A type with 3 numbers: | ||
* - hue [0-360] | ||
* - saturation [0-100] | ||
* - lightness [0-100] | ||
*/ | ||
export type HSLValues = [number, number, number]; | ||
/**<!-- DOCS: ### --> | ||
* namedColours | ||
* | ||
* - `ColourTools.namedColours` | ||
* | ||
* A dictionary of different colour names and their RGB values | ||
* | ||
* | Name | RGB | Hex | | ||
* | -------------------- | ------------- | ------- | | ||
* | aliceblue | 240, 248, 255 | #f0f8ff | | ||
* | antiquewhite | 250, 235, 215 | #faebd7 | | ||
* | aqua | 0, 255, 255 | #00ffff | | ||
* | aquamarine | 127, 255, 212 | #7fffd4 | | ||
* | azure | 240, 255, 255 | #f0ffff | | ||
* | beige | 245, 245, 220 | #f5f5dc | | ||
* | bisque | 255, 228, 196 | #ffe4c4 | | ||
* | black | 0, 0, 0 | #000000 | | ||
* | blanchedalmond | 255, 235, 205 | #ffebcd | | ||
* | blue | 0, 0, 255 | #0000ff | | ||
* | blueviolet | 138, 43, 226 | #8a2be2 | | ||
* | brown | 165, 42, 42 | #a52a2a | | ||
* | burlywood | 222, 184, 135 | #deb887 | | ||
* | cadetblue | 95, 158, 160 | #5f9ea0 | | ||
* | chartreuse | 127, 255, 0 | #7fff00 | | ||
* | chocolate | 210, 105, 30 | #d2691e | | ||
* | coral | 255, 127, 80 | #ff7f50 | | ||
* | cornflowerblue | 100, 149, 237 | #6495ed | | ||
* | cornsilk | 255, 248, 220 | #fff8dc | | ||
* | crimson | 220, 20, 60 | #dc143c | | ||
* | cyan | 0, 255, 255 | #00ffff | | ||
* | darkblue | 0, 0, 139 | #00008b | | ||
* | darkcyan | 0, 139, 139 | #008b8b | | ||
* | darkgoldenrod | 184, 134, 11 | #b8860b | | ||
* | darkgray | 169, 169, 169 | #a9a9a9 | | ||
* | darkgreen | 0, 100, 0 | #006400 | | ||
* | darkgrey | 169, 169, 169 | #a9a9a9 | | ||
* | darkkhaki | 189, 183, 107 | #bdb76b | | ||
* | darkmagenta | 139, 0, 139 | #8b008b | | ||
* | darkolivegreen | 85, 107, 47 | #556b2f | | ||
* | darkorange | 255, 140, 0 | #ff8c00 | | ||
* | darkorchid | 153, 50, 204 | #9932cc | | ||
* | darkred | 139, 0, 0 | #8b0000 | | ||
* | darksalmon | 233, 150, 122 | #e9967a | | ||
* | darkseagreen | 143, 188, 143 | #8fbc8f | | ||
* | darkslateblue | 72, 61, 139 | #483d8b | | ||
* | darkslategray | 47, 79, 79 | #2f4f4f | | ||
* | darkslategrey | 47, 79, 79 | #2f4f4f | | ||
* | darkturquoise | 0, 206, 209 | #00ced1 | | ||
* | darkviolet | 148, 0, 211 | #9400d3 | | ||
* | deeppink | 255, 20, 147 | #ff1493 | | ||
* | deepskyblue | 0, 191, 255 | #00bfff | | ||
* | dimgray | 105, 105, 105 | #696969 | | ||
* | dimgrey | 105, 105, 105 | #696969 | | ||
* | dodgerblue | 30, 144, 255 | #1e90ff | | ||
* | firebrick | 178, 34, 34 | #b22222 | | ||
* | floralwhite | 255, 250, 240 | #fffaf0 | | ||
* | forestgreen | 34, 139, 34 | #228b22 | | ||
* | fractal | 128, 128, 128 | #808080 | | ||
* | fuchsia | 255, 0, 255 | #ff00ff | | ||
* | gainsboro | 220, 220, 220 | #dcdcdc | | ||
* | ghostwhite | 248, 248, 255 | #f8f8ff | | ||
* | gold | 255, 215, 0 | #ffd700 | | ||
* | goldenrod | 218, 165, 32 | #daa520 | | ||
* | gray0 | 0, 0, 0 | #000000 | | ||
* | gray1 | 3, 3, 3 | #030303 | | ||
* | gray2 | 5, 5, 5 | #050505 | | ||
* | gray3 | 8, 8, 8 | #080808 | | ||
* | gray4 | 10, 10, 10 | #0a0a0a | | ||
* | gray5 | 13, 13, 13 | #0d0d0d | | ||
* | gray6 | 15, 15, 15 | #0f0f0f | | ||
* | gray7 | 18, 18, 18 | #121212 | | ||
* | gray8 | 20, 20, 20 | #141414 | | ||
* | gray9 | 23, 23, 23 | #171717 | | ||
* | gray10 | 26, 26, 26 | #1a1a1a | | ||
* | gray11 | 28, 28, 28 | #1c1c1c | | ||
* | gray12 | 31, 31, 31 | #1f1f1f | | ||
* | gray13 | 33, 33, 33 | #212121 | | ||
* | gray14 | 36, 36, 36 | #242424 | | ||
* | gray15 | 38, 38, 38 | #262626 | | ||
* | gray16 | 41, 41, 41 | #292929 | | ||
* | gray17 | 43, 43, 43 | #2b2b2b | | ||
* | gray18 | 46, 46, 46 | #2e2e2e | | ||
* | gray19 | 48, 48, 48 | #303030 | | ||
* | gray20 | 51, 51, 51 | #333333 | | ||
* | gray21 | 54, 54, 54 | #363636 | | ||
* | gray22 | 56, 56, 56 | #383838 | | ||
* | gray23 | 59, 59, 59 | #3b3b3b | | ||
* | gray24 | 61, 61, 61 | #3d3d3d | | ||
* | gray25 | 64, 64, 64 | #404040 | | ||
* | gray26 | 66, 66, 66 | #424242 | | ||
* | gray27 | 69, 69, 69 | #454545 | | ||
* | gray28 | 71, 71, 71 | #474747 | | ||
* | gray29 | 74, 74, 74 | #4a4a4a | | ||
* | gray30 | 77, 77, 77 | #4d4d4d | | ||
* | gray31 | 79, 79, 79 | #4f4f4f | | ||
* | gray32 | 82, 82, 82 | #525252 | | ||
* | gray33 | 84, 84, 84 | #545454 | | ||
* | gray34 | 87, 87, 87 | #575757 | | ||
* | gray35 | 89, 89, 89 | #595959 | | ||
* | gray36 | 92, 92, 92 | #5c5c5c | | ||
* | gray37 | 94, 94, 94 | #5e5e5e | | ||
* | gray38 | 97, 97, 97 | #616161 | | ||
* | gray39 | 99, 99, 99 | #636363 | | ||
* | gray40 | 102, 102, 102 | #666666 | | ||
* | gray41 | 105, 105, 105 | #696969 | | ||
* | gray42 | 107, 107, 107 | #6b6b6b | | ||
* | gray43 | 110, 110, 110 | #6e6e6e | | ||
* | gray44 | 112, 112, 112 | #707070 | | ||
* | gray45 | 115, 115, 115 | #737373 | | ||
* | gray46 | 117, 117, 117 | #757575 | | ||
* | gray47 | 120, 120, 120 | #787878 | | ||
* | gray48 | 122, 122, 122 | #7a7a7a | | ||
* | gray49 | 125, 125, 125 | #7d7d7d | | ||
* | gray50 | 127, 127, 127 | #7f7f7f | | ||
* | gray51 | 130, 130, 130 | #828282 | | ||
* | gray52 | 133, 133, 133 | #858585 | | ||
* | gray53 | 135, 135, 135 | #878787 | | ||
* | gray54 | 138, 138, 138 | #8a8a8a | | ||
* | gray55 | 140, 140, 140 | #8c8c8c | | ||
* | gray56 | 143, 143, 143 | #8f8f8f | | ||
* | gray57 | 145, 145, 145 | #919191 | | ||
* | gray58 | 148, 148, 148 | #949494 | | ||
* | gray59 | 150, 150, 150 | #969696 | | ||
* | gray60 | 153, 153, 153 | #999999 | | ||
* | gray61 | 156, 156, 156 | #9c9c9c | | ||
* | gray62 | 158, 158, 158 | #9e9e9e | | ||
* | gray63 | 161, 161, 161 | #a1a1a1 | | ||
* | gray64 | 163, 163, 163 | #a3a3a3 | | ||
* | gray65 | 166, 166, 166 | #a6a6a6 | | ||
* | gray66 | 168, 168, 168 | #a8a8a8 | | ||
* | gray67 | 171, 171, 171 | #ababab | | ||
* | gray68 | 173, 173, 173 | #adadad | | ||
* | gray69 | 176, 176, 176 | #b0b0b0 | | ||
* | gray70 | 179, 179, 179 | #b3b3b3 | | ||
* | gray71 | 181, 181, 181 | #b5b5b5 | | ||
* | gray72 | 184, 184, 184 | #b8b8b8 | | ||
* | gray73 | 186, 186, 186 | #bababa | | ||
* | gray74 | 189, 189, 189 | #bdbdbd | | ||
* | gray75 | 191, 191, 191 | #bfbfbf | | ||
* | gray76 | 194, 194, 194 | #c2c2c2 | | ||
* | gray77 | 196, 196, 196 | #c4c4c4 | | ||
* | gray78 | 199, 199, 199 | #c7c7c7 | | ||
* | gray79 | 201, 201, 201 | #c9c9c9 | | ||
* | gray80 | 204, 204, 204 | #cccccc | | ||
* | gray81 | 207, 207, 207 | #cfcfcf | | ||
* | gray82 | 209, 209, 209 | #d1d1d1 | | ||
* | gray83 | 212, 212, 212 | #d4d4d4 | | ||
* | gray84 | 214, 214, 214 | #d6d6d6 | | ||
* | gray85 | 217, 217, 217 | #d9d9d9 | | ||
* | gray86 | 219, 219, 219 | #dbdbdb | | ||
* | gray87 | 222, 222, 222 | #dedede | | ||
* | gray88 | 224, 224, 224 | #e0e0e0 | | ||
* | gray89 | 227, 227, 227 | #e3e3e3 | | ||
* | gray90 | 229, 229, 229 | #e5e5e5 | | ||
* | gray91 | 232, 232, 232 | #e8e8e8 | | ||
* | gray92 | 235, 235, 235 | #ebebeb | | ||
* | gray93 | 237, 237, 237 | #ededed | | ||
* | gray94 | 240, 240, 240 | #f0f0f0 | | ||
* | gray95 | 242, 242, 242 | #f2f2f2 | | ||
* | gray96 | 245, 245, 245 | #f5f5f5 | | ||
* | gray97 | 247, 247, 247 | #f7f7f7 | | ||
* | gray98 | 250, 250, 250 | #fafafa | | ||
* | gray99 | 252, 252, 252 | #fcfcfc | | ||
* | gray100 | 255, 255, 255 | #ffffff | | ||
* | gray | 126, 126, 126 | #7e7e7e | | ||
* | green | 0, 128, 0 | #008000 | | ||
* | greenyellow | 173, 255, 47 | #adff2f | | ||
* | grey | 128, 128, 128 | #808080 | | ||
* | honeydew | 240, 255, 240 | #f0fff0 | | ||
* | hotpink | 255, 105, 180 | #ff69b4 | | ||
* | indianred | 205, 92, 92 | #cd5c5c | | ||
* | indigo | 75, 0, 130 | #4b0082 | | ||
* | ivory | 255, 255, 240 | #fffff0 | | ||
* | khaki | 240, 230, 140 | #f0e68c | | ||
* | lavender | 230, 230, 250 | #e6e6fa | | ||
* | lavenderblush | 255, 240, 245 | #fff0f5 | | ||
* | lawngreen | 124, 252, 0 | #7cfc00 | | ||
* | lemonchiffon | 255, 250, 205 | #fffacd | | ||
* | lightblue | 173, 216, 230 | #add8e6 | | ||
* | lightcoral | 240, 128, 128 | #f08080 | | ||
* | lightcyan | 224, 255, 255 | #e0ffff | | ||
* | lightgoldenrodyellow | 250, 250, 210 | #fafad2 | | ||
* | lightgray | 211, 211, 211 | #d3d3d3 | | ||
* | lightgreen | 144, 238, 144 | #90ee90 | | ||
* | lightgrey | 211, 211, 211 | #d3d3d3 | | ||
* | lightpink | 255, 182, 193 | #ffb6c1 | | ||
* | lightsalmon | 255, 160, 122 | #ffa07a | | ||
* | lightseagreen | 32, 178, 170 | #20b2aa | | ||
* | lightskyblue | 135, 206, 250 | #87cefa | | ||
* | lightslategray | 119, 136, 153 | #778899 | | ||
* | lightslategrey | 119, 136, 153 | #778899 | | ||
* | lightsteelblue | 176, 196, 222 | #b0c4de | | ||
* | lightyellow | 255, 255, 224 | #ffffe0 | | ||
* | lime | 0, 255, 0 | #00ff00 | | ||
* | limegreen | 50, 205, 50 | #32cd32 | | ||
* | linen | 250, 240, 230 | #faf0e6 | | ||
* | magenta | 255, 0, 255 | #ff00ff | | ||
* | maroon | 128, 0, 0 | #800000 | | ||
* | mediumaquamarine | 102, 205, 170 | #66cdaa | | ||
* | mediumblue | 0, 0, 205 | #0000cd | | ||
* | mediumorchid | 186, 85, 211 | #ba55d3 | | ||
* | mediumpurple | 147, 112, 219 | #9370db | | ||
* | mediumseagreen | 60, 179, 113 | #3cb371 | | ||
* | mediumslateblue | 123, 104, 238 | #7b68ee | | ||
* | mediumspringgreen | 0, 250, 154 | #00fa9a | | ||
* | mediumturquoise | 72, 209, 204 | #48d1cc | | ||
* | mediumvioletred | 199, 21, 133 | #c71585 | | ||
* | midnightblue | 25, 25, 112 | #191970 | | ||
* | mintcream | 245, 255, 250 | #f5fffa | | ||
* | mistyrose | 255, 228, 225 | #ffe4e1 | | ||
* | moccasin | 255, 228, 181 | #ffe4b5 | | ||
* | navajowhite | 255, 222, 173 | #ffdead | | ||
* | navy | 0, 0, 128 | #000080 | | ||
* | none | 0, 0, 0 | #000000 | | ||
* | oldlace | 253, 245, 230 | #fdf5e6 | | ||
* | olive | 128, 128, 0 | #808000 | | ||
* | olivedrab | 107, 142, 35 | #6b8e23 | | ||
* | orange | 255, 165, 0 | #ffa500 | | ||
* | orangered | 255, 69, 0 | #ff4500 | | ||
* | orchid | 218, 112, 214 | #da70d6 | | ||
* | palegoldenrod | 238, 232, 170 | #eee8aa | | ||
* | palegreen | 152, 251, 152 | #98fb98 | | ||
* | paleturquoise | 175, 238, 238 | #afeeee | | ||
* | palevioletred | 219, 112, 147 | #db7093 | | ||
* | papayawhip | 255, 239, 213 | #ffefd5 | | ||
* | peachpuff | 255, 218, 185 | #ffdab9 | | ||
* | peru | 205, 133, 63 | #cd853f | | ||
* | pink | 255, 192, 203 | #ffc0cb | | ||
* | plum | 221, 160, 221 | #dda0dd | | ||
* | powderblue | 176, 224, 230 | #b0e0e6 | | ||
* | purple | 128, 0, 128 | #800080 | | ||
* | red | 255, 0, 0 | #ff0000 | | ||
* | rosybrown | 188, 143, 143 | #bc8f8f | | ||
* | royalblue | 65, 105, 225 | #4169e1 | | ||
* | saddlebrown | 139, 69, 19 | #8b4513 | | ||
* | salmon | 250, 128, 114 | #fa8072 | | ||
* | sandybrown | 244, 164, 96 | #f4a460 | | ||
* | seagreen | 46, 139, 87 | #2e8b57 | | ||
* | seashell | 255, 245, 238 | #fff5ee | | ||
* | sienna | 160, 82, 45 | #a0522d | | ||
* | silver | 192, 192, 192 | #c0c0c0 | | ||
* | skyblue | 135, 206, 235 | #87ceeb | | ||
* | slateblue | 106, 90, 205 | #6a5acd | | ||
* | slategray | 112, 128, 144 | #708090 | | ||
* | slategrey | 112, 128, 144 | #708090 | | ||
* | snow | 255, 250, 250 | #fffafa | | ||
* | springgreen | 0, 255, 127 | #00ff7f | | ||
* | steelblue | 70, 130, 180 | #4682b4 | | ||
* | tan | 210, 180, 140 | #d2b48c | | ||
* | teal | 0, 128, 128 | #008080 | | ||
* | thistle | 216, 191, 216 | #d8bfd8 | | ||
* | tomato | 255, 99, 71 | #ff6347 | | ||
* | turquoise | 64, 224, 208 | #40e0d0 | | ||
* | violet | 238, 130, 238 | #ee82ee | | ||
* | wheat | 245, 222, 179 | #f5deb3 | | ||
* | white | 255, 255, 255 | #ffffff | | ||
* | whitesmoke | 245, 245, 245 | #f5f5f5 | | ||
* | yellow | 255, 255, 0 | #ffff00 | | ||
* | yellowgreen | 154, 205, 50 | #9acd32 | | ||
* | ||
* ```typescript | ||
* ColourTools.namedColours.blue // [0, 0, 255] | ||
* ColourTools.namedColours.red // [255, 0, 0] | ||
* ColourTools.namedColours.green // [0, 255, 0] | ||
* | ||
* ColourTools.namedColours.azure // [240, 255, 255] | ||
* ColourTools.namedColours.darkorange // [255, 140, 0] | ||
* ColourTools.namedColours.dodgerblue // [30, 144, 255] | ||
* ``` | ||
*/ | ||
export const namedColours = { | ||
aliceblue: [240, 248, 255], | ||
antiquewhite: [250, 235, 215], | ||
aqua: [0, 255, 255], | ||
aquamarine: [127, 255, 212], | ||
azure: [240, 255, 255], | ||
beige: [245, 245, 220], | ||
bisque: [255, 228, 196], | ||
black: [0, 0, 0], | ||
blanchedalmond: [255, 235, 205], | ||
blue: [0, 0, 255], | ||
blueviolet: [138, 43, 226], | ||
brown: [165, 42, 42], | ||
burlywood: [222, 184, 135], | ||
cadetblue: [95, 158, 160], | ||
chartreuse: [127, 255, 0], | ||
chocolate: [210, 105, 30], | ||
coral: [255, 127, 80], | ||
cornflowerblue: [100, 149, 237], | ||
cornsilk: [255, 248, 220], | ||
crimson: [220, 20, 60], | ||
cyan: [0, 255, 255], | ||
darkblue: [0, 0, 139], | ||
darkcyan: [0, 139, 139], | ||
darkgoldenrod: [184, 134, 11], | ||
darkgray: [169, 169, 169], | ||
darkgreen: [0, 100, 0], | ||
darkgrey: [169, 169, 169], | ||
darkkhaki: [189, 183, 107], | ||
darkmagenta: [139, 0, 139], | ||
darkolivegreen: [85, 107, 47], | ||
darkorange: [255, 140, 0], | ||
darkorchid: [153, 50, 204], | ||
darkred: [139, 0, 0], | ||
darksalmon: [233, 150, 122], | ||
darkseagreen: [143, 188, 143], | ||
darkslateblue: [72, 61, 139], | ||
darkslategray: [47, 79, 79], | ||
darkslategrey: [47, 79, 79], | ||
darkturquoise: [0, 206, 209], | ||
darkviolet: [148, 0, 211], | ||
deeppink: [255, 20, 147], | ||
deepskyblue: [0, 191, 255], | ||
dimgray: [105, 105, 105], | ||
dimgrey: [105, 105, 105], | ||
dodgerblue: [30, 144, 255], | ||
firebrick: [178, 34, 34], | ||
floralwhite: [255, 250, 240], | ||
forestgreen: [34, 139, 34], | ||
fractal: [128, 128, 128], | ||
fuchsia: [255, 0, 255], | ||
gainsboro: [220, 220, 220], | ||
ghostwhite: [248, 248, 255], | ||
gold: [255, 215, 0], | ||
goldenrod: [218, 165, 32], | ||
gray0: [0, 0, 0], | ||
gray1: [3, 3, 3], | ||
gray2: [5, 5, 5], | ||
gray3: [8, 8, 8], | ||
gray4: [10, 10, 10], | ||
gray5: [13, 13, 13], | ||
gray6: [15, 15, 15], | ||
gray7: [18, 18, 18], | ||
gray8: [20, 20, 20], | ||
gray9: [23, 23, 23], | ||
gray10: [26, 26, 26], | ||
gray11: [28, 28, 28], | ||
gray12: [31, 31, 31], | ||
gray13: [33, 33, 33], | ||
gray14: [36, 36, 36], | ||
gray15: [38, 38, 38], | ||
gray16: [41, 41, 41], | ||
gray17: [43, 43, 43], | ||
gray18: [46, 46, 46], | ||
gray19: [48, 48, 48], | ||
gray20: [51, 51, 51], | ||
gray21: [54, 54, 54], | ||
gray22: [56, 56, 56], | ||
gray23: [59, 59, 59], | ||
gray24: [61, 61, 61], | ||
gray25: [64, 64, 64], | ||
gray26: [66, 66, 66], | ||
gray27: [69, 69, 69], | ||
gray28: [71, 71, 71], | ||
gray29: [74, 74, 74], | ||
gray30: [77, 77, 77], | ||
gray31: [79, 79, 79], | ||
gray32: [82, 82, 82], | ||
gray33: [84, 84, 84], | ||
gray34: [87, 87, 87], | ||
gray35: [89, 89, 89], | ||
gray36: [92, 92, 92], | ||
gray37: [94, 94, 94], | ||
gray38: [97, 97, 97], | ||
gray39: [99, 99, 99], | ||
gray40: [102, 102, 102], | ||
gray41: [105, 105, 105], | ||
gray42: [107, 107, 107], | ||
gray43: [110, 110, 110], | ||
gray44: [112, 112, 112], | ||
gray45: [115, 115, 115], | ||
gray46: [117, 117, 117], | ||
gray47: [120, 120, 120], | ||
gray48: [122, 122, 122], | ||
gray49: [125, 125, 125], | ||
gray50: [127, 127, 127], | ||
gray51: [130, 130, 130], | ||
gray52: [133, 133, 133], | ||
gray53: [135, 135, 135], | ||
gray54: [138, 138, 138], | ||
gray55: [140, 140, 140], | ||
gray56: [143, 143, 143], | ||
gray57: [145, 145, 145], | ||
gray58: [148, 148, 148], | ||
gray59: [150, 150, 150], | ||
gray60: [153, 153, 153], | ||
gray61: [156, 156, 156], | ||
gray62: [158, 158, 158], | ||
gray63: [161, 161, 161], | ||
gray64: [163, 163, 163], | ||
gray65: [166, 166, 166], | ||
gray66: [168, 168, 168], | ||
gray67: [171, 171, 171], | ||
gray68: [173, 173, 173], | ||
gray69: [176, 176, 176], | ||
gray70: [179, 179, 179], | ||
gray71: [181, 181, 181], | ||
gray72: [184, 184, 184], | ||
gray73: [186, 186, 186], | ||
gray74: [189, 189, 189], | ||
gray75: [191, 191, 191], | ||
gray76: [194, 194, 194], | ||
gray77: [196, 196, 196], | ||
gray78: [199, 199, 199], | ||
gray79: [201, 201, 201], | ||
gray80: [204, 204, 204], | ||
gray81: [207, 207, 207], | ||
gray82: [209, 209, 209], | ||
gray83: [212, 212, 212], | ||
gray84: [214, 214, 214], | ||
gray85: [217, 217, 217], | ||
gray86: [219, 219, 219], | ||
gray87: [222, 222, 222], | ||
gray88: [224, 224, 224], | ||
gray89: [227, 227, 227], | ||
gray90: [229, 229, 229], | ||
gray91: [232, 232, 232], | ||
gray92: [235, 235, 235], | ||
gray93: [237, 237, 237], | ||
gray94: [240, 240, 240], | ||
gray95: [242, 242, 242], | ||
gray96: [245, 245, 245], | ||
gray97: [247, 247, 247], | ||
gray98: [250, 250, 250], | ||
gray99: [252, 252, 252], | ||
gray100: [255, 255, 255], | ||
gray: [126, 126, 126], | ||
green: [0, 128, 0], | ||
greenyellow: [173, 255, 47], | ||
grey: [128, 128, 128], | ||
honeydew: [240, 255, 240], | ||
hotpink: [255, 105, 180], | ||
indianred: [205, 92, 92], | ||
indigo: [75, 0, 130], | ||
ivory: [255, 255, 240], | ||
khaki: [240, 230, 140], | ||
lavender: [230, 230, 250], | ||
lavenderblush: [255, 240, 245], | ||
lawngreen: [124, 252, 0], | ||
lemonchiffon: [255, 250, 205], | ||
lightblue: [173, 216, 230], | ||
lightcoral: [240, 128, 128], | ||
lightcyan: [224, 255, 255], | ||
lightgoldenrodyellow: [250, 250, 210], | ||
lightgray: [211, 211, 211], | ||
lightgreen: [144, 238, 144], | ||
lightgrey: [211, 211, 211], | ||
lightpink: [255, 182, 193], | ||
lightsalmon: [255, 160, 122], | ||
lightseagreen: [32, 178, 170], | ||
lightskyblue: [135, 206, 250], | ||
lightslategray: [119, 136, 153], | ||
lightslategrey: [119, 136, 153], | ||
lightsteelblue: [176, 196, 222], | ||
lightyellow: [255, 255, 224], | ||
lime: [0, 255, 0], | ||
limegreen: [50, 205, 50], | ||
linen: [250, 240, 230], | ||
magenta: [255, 0, 255], | ||
maroon: [128, 0, 0], | ||
mediumaquamarine: [102, 205, 170], | ||
mediumblue: [0, 0, 205], | ||
mediumorchid: [186, 85, 211], | ||
mediumpurple: [147, 112, 219], | ||
mediumseagreen: [60, 179, 113], | ||
mediumslateblue: [123, 104, 238], | ||
mediumspringgreen: [0, 250, 154], | ||
mediumturquoise: [72, 209, 204], | ||
mediumvioletred: [199, 21, 133], | ||
midnightblue: [25, 25, 112], | ||
mintcream: [245, 255, 250], | ||
mistyrose: [255, 228, 225], | ||
moccasin: [255, 228, 181], | ||
navajowhite: [255, 222, 173], | ||
navy: [0, 0, 128], | ||
none: [0, 0, 0], | ||
oldlace: [253, 245, 230], | ||
olive: [128, 128, 0], | ||
olivedrab: [107, 142, 35], | ||
orange: [255, 165, 0], | ||
orangered: [255, 69, 0], | ||
orchid: [218, 112, 214], | ||
palegoldenrod: [238, 232, 170], | ||
palegreen: [152, 251, 152], | ||
paleturquoise: [175, 238, 238], | ||
palevioletred: [219, 112, 147], | ||
papayawhip: [255, 239, 213], | ||
peachpuff: [255, 218, 185], | ||
peru: [205, 133, 63], | ||
pink: [255, 192, 203], | ||
plum: [221, 160, 221], | ||
powderblue: [176, 224, 230], | ||
purple: [128, 0, 128], | ||
red: [255, 0, 0], | ||
rosybrown: [188, 143, 143], | ||
royalblue: [65, 105, 225], | ||
saddlebrown: [139, 69, 19], | ||
salmon: [250, 128, 114], | ||
sandybrown: [244, 164, 96], | ||
seagreen: [46, 139, 87], | ||
seashell: [255, 245, 238], | ||
sienna: [160, 82, 45], | ||
silver: [192, 192, 192], | ||
skyblue: [135, 206, 235], | ||
slateblue: [106, 90, 205], | ||
slategray: [112, 128, 144], | ||
slategrey: [112, 128, 144], | ||
snow: [255, 250, 250], | ||
springgreen: [0, 255, 127], | ||
steelblue: [70, 130, 180], | ||
tan: [210, 180, 140], | ||
teal: [0, 128, 128], | ||
thistle: [216, 191, 216], | ||
tomato: [255, 99, 71], | ||
turquoise: [64, 224, 208], | ||
violet: [238, 130, 238], | ||
wheat: [245, 222, 179], | ||
white: [255, 255, 255], | ||
whitesmoke: [245, 245, 245], | ||
yellow: [255, 255, 0], | ||
yellowgreen: [154, 205, 50] | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* namedColours | ||
* | ||
* - `ColourTools.namedColours` | ||
* | ||
* A dictionary of different colour names and their RGB values | ||
* | ||
* ```typescript | ||
* ColourTools.namedColours.blue // [0, 0, 255] | ||
* ColourTools.namedColours.red // [255, 0, 0] | ||
* ColourTools.namedColours.green // [0, 255, 0] | ||
* | ||
* ColourTools.namedColours.azure // [240, 255, 255] | ||
* ColourTools.namedColours.darkorange // [255, 140, 0] | ||
* ColourTools.namedColours.dodgerblue // [30, 144, 255] | ||
* ``` | ||
*/ | ||
export const namedColours = { | ||
aliceblue: [240, 248, 255], | ||
antiquewhite: [250, 235, 215], | ||
aqua: [0, 255, 255], | ||
aquamarine: [127, 255, 212], | ||
azure: [240, 255, 255], | ||
beige: [245, 245, 220], | ||
bisque: [255, 228, 196], | ||
black: [0, 0, 0], | ||
blanchedalmond: [255, 235, 205], | ||
blue: [0, 0, 255], | ||
blueviolet: [138, 43, 226], | ||
brown: [165, 42, 42], | ||
burlywood: [222, 184, 135], | ||
cadetblue: [95, 158, 160], | ||
chartreuse: [127, 255, 0], | ||
chocolate: [210, 105, 30], | ||
coral: [255, 127, 80], | ||
cornflowerblue: [100, 149, 237], | ||
cornsilk: [255, 248, 220], | ||
crimson: [220, 20, 60], | ||
cyan: [0, 255, 255], | ||
darkblue: [0, 0, 139], | ||
darkcyan: [0, 139, 139], | ||
darkgoldenrod: [184, 134, 11], | ||
darkgray: [169, 169, 169], | ||
darkgreen: [0, 100, 0], | ||
darkgrey: [169, 169, 169], | ||
darkkhaki: [189, 183, 107], | ||
darkmagenta: [139, 0, 139], | ||
darkolivegreen: [85, 107, 47], | ||
darkorange: [255, 140, 0], | ||
darkorchid: [153, 50, 204], | ||
darkred: [139, 0, 0], | ||
darksalmon: [233, 150, 122], | ||
darkseagreen: [143, 188, 143], | ||
darkslateblue: [72, 61, 139], | ||
darkslategray: [47, 79, 79], | ||
darkslategrey: [47, 79, 79], | ||
darkturquoise: [0, 206, 209], | ||
darkviolet: [148, 0, 211], | ||
deeppink: [255, 20, 147], | ||
deepskyblue: [0, 191, 255], | ||
dimgray: [105, 105, 105], | ||
dimgrey: [105, 105, 105], | ||
dodgerblue: [30, 144, 255], | ||
firebrick: [178, 34, 34], | ||
floralwhite: [255, 250, 240], | ||
forestgreen: [34, 139, 34], | ||
fractal: [128, 128, 128], | ||
fuchsia: [255, 0, 255], | ||
gainsboro: [220, 220, 220], | ||
ghostwhite: [248, 248, 255], | ||
gold: [255, 215, 0], | ||
goldenrod: [218, 165, 32], | ||
gray0: [0, 0, 0], | ||
gray1: [3, 3, 3], | ||
gray2: [5, 5, 5], | ||
gray3: [8, 8, 8], | ||
gray4: [10, 10, 10], | ||
gray5: [13, 13, 13], | ||
gray6: [15, 15, 15], | ||
gray7: [18, 18, 18], | ||
gray8: [20, 20, 20], | ||
gray9: [23, 23, 23], | ||
gray10: [26, 26, 26], | ||
gray11: [28, 28, 28], | ||
gray12: [31, 31, 31], | ||
gray13: [33, 33, 33], | ||
gray14: [36, 36, 36], | ||
gray15: [38, 38, 38], | ||
gray16: [41, 41, 41], | ||
gray17: [43, 43, 43], | ||
gray18: [46, 46, 46], | ||
gray19: [48, 48, 48], | ||
gray20: [51, 51, 51], | ||
gray21: [54, 54, 54], | ||
gray22: [56, 56, 56], | ||
gray23: [59, 59, 59], | ||
gray24: [61, 61, 61], | ||
gray25: [64, 64, 64], | ||
gray26: [66, 66, 66], | ||
gray27: [69, 69, 69], | ||
gray28: [71, 71, 71], | ||
gray29: [74, 74, 74], | ||
gray30: [77, 77, 77], | ||
gray31: [79, 79, 79], | ||
gray32: [82, 82, 82], | ||
gray33: [84, 84, 84], | ||
gray34: [87, 87, 87], | ||
gray35: [89, 89, 89], | ||
gray36: [92, 92, 92], | ||
gray37: [94, 94, 94], | ||
gray38: [97, 97, 97], | ||
gray39: [99, 99, 99], | ||
gray40: [102, 102, 102], | ||
gray41: [105, 105, 105], | ||
gray42: [107, 107, 107], | ||
gray43: [110, 110, 110], | ||
gray44: [112, 112, 112], | ||
gray45: [115, 115, 115], | ||
gray46: [117, 117, 117], | ||
gray47: [120, 120, 120], | ||
gray48: [122, 122, 122], | ||
gray49: [125, 125, 125], | ||
gray50: [127, 127, 127], | ||
gray51: [130, 130, 130], | ||
gray52: [133, 133, 133], | ||
gray53: [135, 135, 135], | ||
gray54: [138, 138, 138], | ||
gray55: [140, 140, 140], | ||
gray56: [143, 143, 143], | ||
gray57: [145, 145, 145], | ||
gray58: [148, 148, 148], | ||
gray59: [150, 150, 150], | ||
gray60: [153, 153, 153], | ||
gray61: [156, 156, 156], | ||
gray62: [158, 158, 158], | ||
gray63: [161, 161, 161], | ||
gray64: [163, 163, 163], | ||
gray65: [166, 166, 166], | ||
gray66: [168, 168, 168], | ||
gray67: [171, 171, 171], | ||
gray68: [173, 173, 173], | ||
gray69: [176, 176, 176], | ||
gray70: [179, 179, 179], | ||
gray71: [181, 181, 181], | ||
gray72: [184, 184, 184], | ||
gray73: [186, 186, 186], | ||
gray74: [189, 189, 189], | ||
gray75: [191, 191, 191], | ||
gray76: [194, 194, 194], | ||
gray77: [196, 196, 196], | ||
gray78: [199, 199, 199], | ||
gray79: [201, 201, 201], | ||
gray80: [204, 204, 204], | ||
gray81: [207, 207, 207], | ||
gray82: [209, 209, 209], | ||
gray83: [212, 212, 212], | ||
gray84: [214, 214, 214], | ||
gray85: [217, 217, 217], | ||
gray86: [219, 219, 219], | ||
gray87: [222, 222, 222], | ||
gray88: [224, 224, 224], | ||
gray89: [227, 227, 227], | ||
gray90: [229, 229, 229], | ||
gray91: [232, 232, 232], | ||
gray92: [235, 235, 235], | ||
gray93: [237, 237, 237], | ||
gray94: [240, 240, 240], | ||
gray95: [242, 242, 242], | ||
gray96: [245, 245, 245], | ||
gray97: [247, 247, 247], | ||
gray98: [250, 250, 250], | ||
gray99: [252, 252, 252], | ||
gray100: [255, 255, 255], | ||
gray: [126, 126, 126], | ||
green: [0, 128, 0], | ||
greenyellow: [173, 255, 47], | ||
grey: [128, 128, 128], | ||
honeydew: [240, 255, 240], | ||
hotpink: [255, 105, 180], | ||
indianred: [205, 92, 92], | ||
indigo: [75, 0, 130], | ||
ivory: [255, 255, 240], | ||
khaki: [240, 230, 140], | ||
lavender: [230, 230, 250], | ||
lavenderblush: [255, 240, 245], | ||
lawngreen: [124, 252, 0], | ||
lemonchiffon: [255, 250, 205], | ||
lightblue: [173, 216, 230], | ||
lightcoral: [240, 128, 128], | ||
lightcyan: [224, 255, 255], | ||
lightgoldenrodyellow: [250, 250, 210], | ||
lightgray: [211, 211, 211], | ||
lightgreen: [144, 238, 144], | ||
lightgrey: [211, 211, 211], | ||
lightpink: [255, 182, 193], | ||
lightsalmon: [255, 160, 122], | ||
lightseagreen: [32, 178, 170], | ||
lightskyblue: [135, 206, 250], | ||
lightslategray: [119, 136, 153], | ||
lightslategrey: [119, 136, 153], | ||
lightsteelblue: [176, 196, 222], | ||
lightyellow: [255, 255, 224], | ||
lime: [0, 255, 0], | ||
limegreen: [50, 205, 50], | ||
linen: [250, 240, 230], | ||
magenta: [255, 0, 255], | ||
maroon: [128, 0, 0], | ||
mediumaquamarine: [102, 205, 170], | ||
mediumblue: [0, 0, 205], | ||
mediumorchid: [186, 85, 211], | ||
mediumpurple: [147, 112, 219], | ||
mediumseagreen: [60, 179, 113], | ||
mediumslateblue: [123, 104, 238], | ||
mediumspringgreen: [0, 250, 154], | ||
mediumturquoise: [72, 209, 204], | ||
mediumvioletred: [199, 21, 133], | ||
midnightblue: [25, 25, 112], | ||
mintcream: [245, 255, 250], | ||
mistyrose: [255, 228, 225], | ||
moccasin: [255, 228, 181], | ||
navajowhite: [255, 222, 173], | ||
navy: [0, 0, 128], | ||
none: [0, 0, 0], | ||
oldlace: [253, 245, 230], | ||
olive: [128, 128, 0], | ||
olivedrab: [107, 142, 35], | ||
orange: [255, 165, 0], | ||
orangered: [255, 69, 0], | ||
orchid: [218, 112, 214], | ||
palegoldenrod: [238, 232, 170], | ||
palegreen: [152, 251, 152], | ||
paleturquoise: [175, 238, 238], | ||
palevioletred: [219, 112, 147], | ||
papayawhip: [255, 239, 213], | ||
peachpuff: [255, 218, 185], | ||
peru: [205, 133, 63], | ||
pink: [255, 192, 203], | ||
plum: [221, 160, 221], | ||
powderblue: [176, 224, 230], | ||
purple: [128, 0, 128], | ||
red: [255, 0, 0], | ||
rosybrown: [188, 143, 143], | ||
royalblue: [65, 105, 225], | ||
saddlebrown: [139, 69, 19], | ||
salmon: [250, 128, 114], | ||
sandybrown: [244, 164, 96], | ||
seagreen: [46, 139, 87], | ||
seashell: [255, 245, 238], | ||
sienna: [160, 82, 45], | ||
silver: [192, 192, 192], | ||
skyblue: [135, 206, 235], | ||
slateblue: [106, 90, 205], | ||
slategray: [112, 128, 144], | ||
slategrey: [112, 128, 144], | ||
snow: [255, 250, 250], | ||
springgreen: [0, 255, 127], | ||
steelblue: [70, 130, 180], | ||
tan: [210, 180, 140], | ||
teal: [0, 128, 128], | ||
thistle: [216, 191, 216], | ||
tomato: [255, 99, 71], | ||
turquoise: [64, 224, 208], | ||
violet: [238, 130, 238], | ||
wheat: [245, 222, 179], | ||
white: [255, 255, 255], | ||
whitesmoke: [245, 245, 245], | ||
yellow: [255, 255, 0], | ||
yellowgreen: [154, 205, 50] | ||
}; | ||
const limitValue = (val: number) => Math.max(0, Math.min(255, val)); | ||
const roundMinMax = (value: number, min: number = 0, max: number = 255) => Math.min(max, Math.max(min, Math.round(value))); | ||
const limitValue = (val: number) => Math.max(0, Math.min(255, val)); | ||
const roundMinMax = (value: number, min: number = 0, max: number = 255) => Math.min(max, Math.max(min, Math.round(value))); | ||
/**<!-- DOCS: ### --> | ||
* parse | ||
* | ||
* - `ColourTools.parse` | ||
* | ||
* Parse a string into a colour object (RGB array) | ||
* Not extensive. Currently limited to: | ||
* - 3 char hexes | ||
* - 6 char hexes | ||
* - comma separated RGB values | ||
* - named colours (from namedColours dictionary) | ||
* | ||
* ```typescript | ||
* ColourTools.parse('#FF0000') // [255, 0, 0] | ||
* ColourTools.parse('rgb(255, 0, 0)') // [255, 0, 0] | ||
* ColourTools.parse('red') // [255, 0, 0] | ||
* ``` | ||
*/ | ||
export const parse = (input: string): ColourValues => { | ||
const trimmed = (input + '').trim(); | ||
/**<!-- DOCS: ### --> | ||
* parse | ||
* | ||
* - `ColourTools.parse` | ||
* | ||
* Parse a string into a colour object (RGB array) | ||
* Not extensive. Currently limited to: | ||
* - 3 char hexes | ||
* - 6 char hexes | ||
* - comma separated RGB values | ||
* - named colours (from namedColours dictionary) | ||
* | ||
* ```typescript | ||
* ColourTools.parse('#FF0000') // [255, 0, 0] | ||
* ColourTools.parse('rgb(255, 0, 0)') // [255, 0, 0] | ||
* ColourTools.parse('red') // [255, 0, 0] | ||
* ``` | ||
*/ | ||
export const parse = (input: string): ColourValues => { | ||
const trimmed = (input + '').trim(); | ||
if (namedColours[trimmed]) { | ||
return namedColours[trimmed]; | ||
} | ||
if (namedColours[trimmed]) { | ||
return namedColours[trimmed]; | ||
} | ||
if (/^rgb/.test(trimmed) || /([0-9]{1,3}(,|\s|\/)+){2}[0-9]{1,3}/.test(trimmed)) { | ||
const stripped = trimmed.replace(/[^0-9,/]/g, ''); | ||
const [r, g, b] = [...stripped.split(/[^0-9]/).map(Number), 0, 0, 0].map(limitValue); | ||
return [r, g, b]; | ||
} | ||
if (/^#/.test(trimmed) || /^([0-9A-F]{3}|[0-9A-F]{6})$/.test(trimmed)) { | ||
const stripped = trimmed.toUpperCase().replace(/[^0-9A-F]/g, ''); | ||
let hexs: number[] = []; | ||
if (/^[0-9A-F]{3}$/.test(stripped)) { | ||
hexs = [...(stripped.match(/[0-9A-F]{1}/g) || [])].map((hex) => parseInt(hex, 16)); | ||
hexs = hexs.map((val) => val * 17); | ||
if (/^rgb/.test(trimmed) || /([0-9]{1,3}(,|\s|\/)+){2}[0-9]{1,3}/.test(trimmed)) { | ||
const stripped = trimmed.replace(/[^0-9,/]/g, ''); | ||
const [r, g, b] = [...stripped.split(/[^0-9]/).map(Number), 0, 0, 0].map(limitValue); | ||
return [r, g, b]; | ||
} | ||
if (/^[0-9A-F]{6}$/.test(stripped)) { | ||
hexs = [...(stripped.match(/[0-9A-F]{2}/g) || [])].map((hex) => parseInt(hex, 16)); | ||
if (/^#/.test(trimmed) || /^([0-9A-F]{3}|[0-9A-F]{6})$/.test(trimmed)) { | ||
const stripped = trimmed.toUpperCase().replace(/[^0-9A-F]/g, ''); | ||
let hexs: number[] = []; | ||
if (/^[0-9A-F]{3}$/.test(stripped)) { | ||
hexs = [...(stripped.match(/[0-9A-F]{1}/g) || [])].map((hex) => parseInt(hex, 16)); | ||
hexs = hexs.map((val) => val * 17); | ||
} | ||
if (/^[0-9A-F]{6}$/.test(stripped)) { | ||
hexs = [...(stripped.match(/[0-9A-F]{2}/g) || [])].map((hex) => parseInt(hex, 16)); | ||
} | ||
const [r, g, b] = hexs.map(limitValue); | ||
return [r, g, b]; | ||
} | ||
const [r, g, b] = hexs.map(limitValue); | ||
return [r, g, b]; | ||
} | ||
return [0, 0, 0]; | ||
}; | ||
return [0, 0, 0]; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* toHex | ||
* | ||
* - `ColourTools.toHex` | ||
* | ||
* Convert a colour object (RGB array) to a hex string | ||
* | ||
* ```typescript | ||
* ColourTools.toHex([255, 0, 0]) // '#FF0000' | ||
* ``` | ||
*/ | ||
export const toHex = (colour: ColourValues): string => { | ||
const hexs = colour.map((val) => (val ?? 0).toString(16).padStart(2, '0')); | ||
return `#${hexs.join('')}`; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* toHex | ||
* | ||
* - `ColourTools.toHex` | ||
* | ||
* Convert a colour object (RGB array) to a hex string | ||
* | ||
* ```typescript | ||
* ColourTools.toHex([255, 0, 0]) // '#FF0000' | ||
* ``` | ||
*/ | ||
export const toHex = (colour: ColourValues): string => { | ||
const hexs = colour.map((val) => (val ?? 0).toString(16).padStart(2, '0')); | ||
return `#${hexs.join('')}`; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* getLuminance | ||
* | ||
* - `ColourTools.getLuminance` | ||
* | ||
* IMPORTANT: This is not the same as the HSL luminance value. | ||
* | ||
* Get the luminance value of a given colour. | ||
* | ||
* Between 0 and 255. Calculated using the formula: | ||
* (RED × 0.299) + (GREEN × 0.587) + (BLUE × 0.114) | ||
* | ||
* Is the Y (Luma) component of the YUV444 color model. | ||
* | ||
* ```typescript | ||
* ColourTools.getLuminance([255, 0, 0]); // 76.245 | ||
* ColourTools.getLuminance([0, 255, 0]); // 149.685 | ||
* ColourTools.getLuminance([0, 0, 255]); // 29.07 | ||
* ``` | ||
*/ | ||
export const getLuminance = ([r, g, b]: ColourValues): number => { | ||
const [y, u, v] = toYUV([r, g, b]); | ||
return y; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* getLuminance | ||
* | ||
* - `ColourTools.getLuminance` | ||
* | ||
* IMPORTANT: This is not the same as the HSL luminance value. | ||
* | ||
* Get the luminance value of a given colour. | ||
* | ||
* Between 0 and 255. Calculated using the formula: | ||
* (RED × 0.299) + (GREEN × 0.587) + (BLUE × 0.114) | ||
* | ||
* Is the Y (Luma) component of the YUV444 color model. | ||
* | ||
* ```typescript | ||
* ColourTools.getLuminance([255, 0, 0]); // 76.245 | ||
* ColourTools.getLuminance([0, 255, 0]); // 149.685 | ||
* ColourTools.getLuminance([0, 0, 255]); // 29.07 | ||
* ``` | ||
*/ | ||
export const getLuminance = ([r, g, b]: ColourValues): number => { | ||
const [y, u, v] = toYUV([r, g, b]); | ||
return y; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* toYUV | ||
* | ||
* - `ColourTools.toYUV` | ||
* | ||
* Convert a colour object (RGB array) to a YUV array. | ||
* | ||
* See https://en.wikipedia.org/wiki/YUV#Y%E2%80%B2UV444_to_RGB888_conversion | ||
* | ||
* ```typescript | ||
* ColourTools.toYUV([255, 0, 0]); // [76.245, 112.439, -38.094] | ||
* ``` | ||
*/ | ||
export const toYUV = ([r, g, b]: ColourValues): ColourValues => { | ||
const y = fixFloat(0.299 * (r ?? 0) + 0.587 * (g ?? 0) + 0.114 * (b ?? 0)); | ||
const u = fixFloat(-0.14713 * (r ?? 0) - 0.28886 * (g ?? 0) + 0.436 * (b ?? 0)); | ||
const v = fixFloat(0.615 * (r ?? 0) - 0.51499 * (g ?? 0) - 0.10001 * (b ?? 0)); | ||
return [y, u, v]; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* toYUV | ||
* | ||
* - `ColourTools.toYUV` | ||
* | ||
* Convert a colour object (RGB array) to a YUV array. | ||
* | ||
* See https://en.wikipedia.org/wiki/YUV#Y%E2%80%B2UV444_to_RGB888_conversion | ||
* | ||
* ```typescript | ||
* ColourTools.toYUV([255, 0, 0]); // [76.245, 112.439, -38.094] | ||
* ``` | ||
*/ | ||
export const toYUV = ([r, g, b]: ColourValues): ColourValues => { | ||
const y = MathsTools.fixFloat(0.299 * (r ?? 0) + 0.587 * (g ?? 0) + 0.114 * (b ?? 0)); | ||
const u = MathsTools.fixFloat(-0.14713 * (r ?? 0) - 0.28886 * (g ?? 0) + 0.436 * (b ?? 0)); | ||
const v = MathsTools.fixFloat(0.615 * (r ?? 0) - 0.51499 * (g ?? 0) - 0.10001 * (b ?? 0)); | ||
return [y, u, v]; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* toHSL | ||
* | ||
* - `ColourTools.toHSL` | ||
* | ||
* Convert a RGB array to a HSL array. | ||
* | ||
* Adapted from https://www.30secondsofcode.org/js/s/rgb-to-hsl | ||
* | ||
* ```typescript | ||
* ColourTools.toHSL([255, 0, 0]); // [0, 100, 50] | ||
* ColourTools.toHSL([0, 255, 0]); // [120, 100, 50] | ||
* ``` | ||
*/ | ||
export const toHSL = (colour: ColourValues, round: boolean = true): HSLValues => { | ||
const r = colour[0] / 255; | ||
const g = colour[1] / 255; | ||
const b = colour[2] / 255; | ||
/**<!-- DOCS: ### --> | ||
* toHSL | ||
* | ||
* - `ColourTools.toHSL` | ||
* | ||
* Convert a RGB array to a HSL array. | ||
* | ||
* Adapted from https://www.30secondsofcode.org/js/s/rgb-to-hsl | ||
* | ||
* ```typescript | ||
* ColourTools.toHSL([255, 0, 0]); // [0, 100, 50] | ||
* ColourTools.toHSL([0, 255, 0]); // [120, 100, 50] | ||
* ``` | ||
*/ | ||
export const toHSL = (colour: ColourValues, round: boolean = true): HSLValues => { | ||
const r = colour[0] / 255; | ||
const g = colour[1] / 255; | ||
const b = colour[2] / 255; | ||
const M = Math.max(r, g, b); | ||
const m = M - Math.min(r, g, b); | ||
const M = Math.max(r, g, b); | ||
const m = M - Math.min(r, g, b); | ||
let d = 0; | ||
if (m) { | ||
if (M === r) { | ||
// prodominantly red | ||
d = (g - b) / m; | ||
} else { | ||
if (M === g) { | ||
// prodominantly green | ||
d = 2 + (b - r) / m; | ||
let d = 0; | ||
if (m) { | ||
if (M === r) { | ||
// prodominantly red | ||
d = (g - b) / m; | ||
} else { | ||
// prodominantly blue | ||
d = 4 + (r - g) / m; | ||
if (M === g) { | ||
// prodominantly green | ||
d = 2 + (b - r) / m; | ||
} else { | ||
// prodominantly blue | ||
d = 4 + (r - g) / m; | ||
} | ||
} | ||
} | ||
} | ||
const result: HSLValues = [ | ||
60 * d < 0 ? 60 * d + 360 : 60 * d, | ||
100 * (m ? (M <= 0.5 ? m / (2 * M - m) : m / (2 - (2 * M - m))) : 0), | ||
(100 * (2 * M - m)) / 2 | ||
]; | ||
const result: HSLValues = [ | ||
60 * d < 0 ? 60 * d + 360 : 60 * d, | ||
100 * (m ? (M <= 0.5 ? m / (2 * M - m) : m / (2 - (2 * M - m))) : 0), | ||
(100 * (2 * M - m)) / 2 | ||
]; | ||
if (round) { | ||
return [roundMinMax(result[0], 0, 360), roundMinMax(result[1], 0, 100), roundMinMax(result[2], 0, 100)]; | ||
} | ||
return result; | ||
}; | ||
if (round) { | ||
return [roundMinMax(result[0], 0, 360), roundMinMax(result[1], 0, 100), roundMinMax(result[2], 0, 100)]; | ||
} | ||
return result; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* fromHSL | ||
* | ||
* - `ColourTools.fromHSL` | ||
* | ||
* Convert a HSL array to a RGB array. | ||
* | ||
* Adapted from https://www.30secondsofcode.org/js/s/hsl-to-rgb | ||
* | ||
* ```typescript | ||
* ColourTools.fromHSL([0, 100, 50]); // [255, 0, 0] | ||
* ColourTools.fromHSL([120, 100, 50]); // [0, 255, 0] | ||
* ``` | ||
*/ | ||
export const fromHSL = (hsl: HSLValues, round: boolean = true): ColourValues => { | ||
const h = hsl[0]; | ||
const s = hsl[1] / 100; | ||
const l = hsl[2] / 100; | ||
/**<!-- DOCS: ### --> | ||
* fromHSL | ||
* | ||
* - `ColourTools.fromHSL` | ||
* | ||
* Convert a HSL array to a RGB array. | ||
* | ||
* Adapted from https://www.30secondsofcode.org/js/s/hsl-to-rgb | ||
* | ||
* ```typescript | ||
* ColourTools.fromHSL([0, 100, 50]); // [255, 0, 0] | ||
* ColourTools.fromHSL([120, 100, 50]); // [0, 255, 0] | ||
* ``` | ||
*/ | ||
export const fromHSL = (hsl: HSLValues, round: boolean = true): ColourValues => { | ||
const h = hsl[0]; | ||
const s = hsl[1] / 100; | ||
const l = hsl[2] / 100; | ||
const k = (n) => (n + h / 30) % 12; | ||
const a = s * Math.min(l, 1 - l); | ||
const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1))); | ||
const k = (n) => (n + h / 30) % 12; | ||
const a = s * Math.min(l, 1 - l); | ||
const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1))); | ||
const result: ColourValues = [255 * f(0), 255 * f(8), 255 * f(4)]; | ||
const result: ColourValues = [255 * f(0), 255 * f(8), 255 * f(4)]; | ||
if (round) { | ||
return [roundMinMax(result[0], 0, 255), roundMinMax(result[1], 0, 255), roundMinMax(result[2], 0, 255)]; | ||
} | ||
return result; | ||
}; | ||
if (round) { | ||
return [roundMinMax(result[0], 0, 255), roundMinMax(result[1], 0, 255), roundMinMax(result[2], 0, 255)]; | ||
} | ||
return result; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* invertColour | ||
* | ||
* - `ColourTools.invertColour` | ||
* | ||
* Get the opposite colour of a given colour. | ||
* | ||
* ```typescript | ||
* ColourTools.invertColour([255, 0, 0]); // [0, 255, 255] | ||
* ColourTools.invertColour([0, 255, 0]); // [ 255, 0, 255 ] | ||
* ColourTools.invertColour([0, 0, 255]); // [ 255, 255, 0 ] | ||
* ``` | ||
*/ | ||
export const invertColour = ([r, g, b]: ColourValues): ColourValues => [255 - r, 255 - g, 255 - b]; | ||
/**<!-- DOCS: ### --> | ||
* invertColour | ||
* | ||
* - `ColourTools.invertColour` | ||
* | ||
* Get the opposite colour of a given colour. | ||
* | ||
* ```typescript | ||
* ColourTools.invertColour([255, 0, 0]); // [0, 255, 255] | ||
* ColourTools.invertColour([0, 255, 0]); // [ 255, 0, 255 ] | ||
* ColourTools.invertColour([0, 0, 255]); // [ 255, 255, 0 ] | ||
* ``` | ||
*/ | ||
export const invertColour = ([r, g, b]: ColourValues): ColourValues => [255 - r, 255 - g, 255 - b]; | ||
const white = [255, 255, 255] as ColourValues; | ||
const black = [0, 0, 0] as ColourValues; | ||
const white = [255, 255, 255] as ColourValues; | ||
const black = [0, 0, 0] as ColourValues; | ||
/**<!-- DOCS: ### --> | ||
* getContrastedColour | ||
* | ||
* - `ColourTools.getContrastedColour` | ||
* | ||
* Get the colour that contrasts the most with a given colour. (White or black) | ||
* | ||
* Returned colour can be used as a text colour on top of the provided colour | ||
* | ||
* ```typescript | ||
* ColourTools.getContrastedColour([255, 0, 0]); // [255, 255, 255] | ||
* ColourTools.getContrastedColour([255, 255, 0]); // [0, 0, 0] | ||
* ``` | ||
*/ | ||
export const getContrastedColour = (colour: ColourValues): ColourValues => (getLuminance(colour) > 186 ? black : white); | ||
/**<!-- DOCS: ### --> | ||
* getContrastedColour | ||
* | ||
* - `ColourTools.getContrastedColour` | ||
* | ||
* Get the colour that contrasts the most with a given colour. (White or black) | ||
* | ||
* Returned colour can be used as a text colour on top of the provided colour | ||
* | ||
* ```typescript | ||
* ColourTools.getContrastedColour([255, 0, 0]); // [255, 255, 255] | ||
* ColourTools.getContrastedColour([255, 255, 0]); // [0, 0, 0] | ||
* ``` | ||
*/ | ||
export const getContrastedColour = (colour: ColourValues): ColourValues => (getLuminance(colour) > 186 ? black : white); | ||
/**<!-- DOCS: ### --> | ||
* getLimitedColour | ||
* | ||
* - `ColourTools.getLimitedColour` | ||
* | ||
* Adjust a colour if a certain condition is met. | ||
* Used for lightening/darkening colours that are too light/dark | ||
* | ||
* All values in functions are HSL | ||
* | ||
* ```typescript | ||
* ColourTools.getLimitedColour([255, 255, 255], ([h,s,l]) => l > 90, ([h,s,l]) => [h, s, 90]); // [ 230, 230, 230 ] | ||
* ColourTools.getLimitedColour([128, 128, 128], ([h,s,l]) => l > 90, ([h,s,l]) => [h, s, 90]); // [ 128, 128, 128 ] | ||
* ``` | ||
*/ | ||
export const getLimitedColour = (colour: ColourValues, checkFn: (hsl: HSLValues) => boolean, adjustFn: (hsl: HSLValues) => HSLValues) => { | ||
const hsl = toHSL(colour); | ||
if (checkFn(hsl)) { | ||
const adjusted = adjustFn(hsl); | ||
const out = fromHSL(adjusted); | ||
return out; | ||
} | ||
return colour; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* getLimitedColour | ||
* | ||
* - `ColourTools.getLimitedColour` | ||
* | ||
* Adjust a colour if a certain condition is met. | ||
* Used for lightening/darkening colours that are too light/dark | ||
* | ||
* All values in functions are HSL | ||
* | ||
* ```typescript | ||
* ColourTools.getLimitedColour([255, 255, 255], ([h,s,l]) => l > 90, ([h,s,l]) => [h, s, 90]); // [ 230, 230, 230 ] | ||
* ColourTools.getLimitedColour([128, 128, 128], ([h,s,l]) => l > 90, ([h,s,l]) => [h, s, 90]); // [ 128, 128, 128 ] | ||
* ``` | ||
*/ | ||
export const getLimitedColour = (colour: ColourValues, checkFn: (hsl: HSLValues) => boolean, adjustFn: (hsl: HSLValues) => HSLValues) => { | ||
const hsl = toHSL(colour); | ||
if (checkFn(hsl)) { | ||
const adjusted = adjustFn(hsl); | ||
const out = fromHSL(adjusted); | ||
return out; | ||
} | ||
return colour; | ||
}; | ||
} |
1155
src/tools/fn.ts
@@ -1,2 +0,2 @@ | ||
import { fixFloat } from './MathsTools'; | ||
import { MathsTools } from './MathsTools'; | ||
@@ -9,576 +9,667 @@ //<!-- DOCS: 30 --> | ||
*/ | ||
export namespace fn { | ||
/**<!-- DOCS: ### --> | ||
* noop | ||
* | ||
* - `fn.noop` | ||
* | ||
* No operation. Do nothing, return nothing. | ||
* | ||
* ```typescript | ||
* const run = condition ? doSomething : fn.noop; | ||
* run(); | ||
* ``` | ||
*/ | ||
export const noop = () => {}; | ||
/**<!-- DOCS: ### --> | ||
* noop | ||
* | ||
* - `fn.noop` | ||
* | ||
* No operation. Do nothing, return nothing. | ||
* | ||
* ```typescript | ||
* const run = condition ? doSomething : fn.noop; | ||
* run(); | ||
* ``` | ||
*/ | ||
export const noop = () => {}; | ||
/**<!-- DOCS: ### --> | ||
* noact | ||
* | ||
* - `fn.noact` | ||
* | ||
* No action. Returns the first argument it receives. | ||
* | ||
* ```typescript | ||
* const items = stuff | ||
* .map(condition ? mapSomething : fn.noact) | ||
* ``` | ||
*/ | ||
export const noact = <T = any>(item: T): T => item; | ||
/**<!-- DOCS: ### --> | ||
* noact | ||
* | ||
* - `fn.noact` | ||
* | ||
* No action. Returns the first argument it receives. | ||
* | ||
* ```typescript | ||
* const items = stuff | ||
* .map(condition ? mapSomething : fn.noact) | ||
* ``` | ||
*/ | ||
export const noact = <T = any>(item: T): T => item; | ||
/**<!-- DOCS: ### --> | ||
* result | ||
* | ||
* - `fn.result` | ||
* | ||
* Returns a function that returns a function that returns the first argument. | ||
* | ||
* ```typescript | ||
* const items = stuff | ||
* .filter(condition ? mapSomething : fn.result(true)) | ||
* ``` | ||
*/ | ||
export const result = | ||
<T = any>(item: T) => | ||
(): T => | ||
item; | ||
/**<!-- DOCS: ### --> | ||
* result | ||
* | ||
* - `fn.result` | ||
* | ||
* Returns a function that returns a function that returns the first argument. | ||
* | ||
* ```typescript | ||
* const items = stuff | ||
* .filter(condition ? mapSomething : fn.result(true)) | ||
* ``` | ||
*/ | ||
export const result = | ||
<T = any>(item: T) => | ||
(): T => | ||
item; | ||
/**<!-- DOCS: ### --> | ||
* resolve | ||
* | ||
* - `fn.resolve` | ||
* | ||
* Returns an async function that resolves to the first argument | ||
* | ||
* Like fn.result, but wrapped in a Promise | ||
*/ | ||
export const resolve = | ||
<T = any>(item: T) => | ||
(): Promise<T> => | ||
Promise.resolve(item); | ||
/**<!-- DOCS: ### --> | ||
* resolve | ||
* | ||
* - `fn.resolve` | ||
* | ||
* Returns an async function that resolves to the first argument | ||
* | ||
* Like fn.result, but wrapped in a Promise | ||
*/ | ||
export const resolve = | ||
<T = any>(item: T) => | ||
(): Promise<T> => | ||
Promise.resolve(item); | ||
/**<!-- DOCS: ### --> | ||
* reject | ||
* | ||
* - `fn.reject` | ||
* | ||
* Returns an async function that rejects with the first argument | ||
*/ | ||
export const reject = | ||
<T = any>(item: T) => | ||
(): Promise<T> => | ||
Promise.reject(item); | ||
/**<!-- DOCS: ### --> | ||
* reject | ||
* | ||
* - `fn.reject` | ||
* | ||
* Returns an async function that rejects with the first argument | ||
*/ | ||
export const reject = | ||
<T = any>(item: T) => | ||
(): Promise<T> => | ||
Promise.reject(item); | ||
/**<!-- DOCS: ### --> | ||
* filters | ||
* | ||
* - `fn.filters` | ||
* | ||
* Collection of functions that can be used with Array.filter | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* exists | ||
* | ||
* - `fn.exists` | ||
* - `fn.filters.exists` | ||
* - `filters.exists` | ||
* | ||
* Returns true if item isn't null or undefined. | ||
* | ||
* ```typescript | ||
* [null, 1, undefined, 2].filter(fn.exists); // [1, 2] | ||
* ``` | ||
*/ | ||
export const exists = <T = any>(item: T): boolean => item !== undefined && item !== null; | ||
/**<!-- DOCS: ### --> | ||
* filters | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* exists | ||
* | ||
* - `fn.exists` | ||
* - `fn.filters.exists` | ||
* | ||
* Returns true if item isn't null or undefined. | ||
* | ||
* ```typescript | ||
* [null, 1, undefined, 2].filter(fn.exists); // [1, 2] | ||
* ``` | ||
*/ | ||
export const exists = <T = any>(item: T): boolean => item !== undefined && item !== null; | ||
/**<!-- DOCS: #### --> | ||
* isTruthy | ||
* | ||
* - `fn.isTruthy` | ||
* - `fn.filters.isTruthy` | ||
* - `filters.isTruthy` | ||
* | ||
* Returns true if item is truthy. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].filter(fn.isTruthy); // [1, 2] | ||
* ['', 'a', 'b'].filter(fn.isTruthy); // ['a', 'b'] | ||
* ``` | ||
*/ | ||
export const isTruthy = <T = any>(item: T): boolean => Boolean(item); | ||
/**<!-- DOCS: #### --> | ||
* isTruthy | ||
* | ||
* - `fn.isTruthy` | ||
* - `fn.filters.isTruthy` | ||
* | ||
* Returns true if item is truthy. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].filter(fn.isTruthy); // [1, 2] | ||
* ['', 'a', 'b'].filter(fn.isTruthy); // ['a', 'b'] | ||
* ``` | ||
*/ | ||
export const isTruthy = <T = any>(item: T): boolean => Boolean(item); | ||
/**<!-- DOCS: #### --> | ||
* isFalsy | ||
* | ||
* - `fn.isFalsy` | ||
* - `fn.filters.isFalsy` | ||
* - `filters.isFalsy` | ||
* | ||
* Returns true if item is falsy. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].filter(fn.isFalsy); // [0] | ||
* ['', 'a', 'b'].filter(fn.isFalsy); // [''] | ||
* ``` | ||
*/ | ||
export const isFalsy = <T = any>(item: T): boolean => !Boolean(item); | ||
/**<!-- DOCS: #### --> | ||
* isFalsy | ||
* | ||
* - `fn.isFalsy` | ||
* - `fn.filters.isFalsy` | ||
* | ||
* Returns true if item is falsy. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].filter(fn.isFalsy); // [0] | ||
* ['', 'a', 'b'].filter(fn.isFalsy); // [''] | ||
* ``` | ||
*/ | ||
export const isFalsy = <T = any>(item: T): boolean => !Boolean(item); | ||
/**<!-- DOCS: #### --> | ||
* isEmpty | ||
* | ||
* - `fn.isEmpty` | ||
* - `fn.filters.isEmpty` | ||
* - `filters.isEmpty` | ||
* | ||
* Returns true if item's length is 0 | ||
* | ||
* ```typescript | ||
* ['', 'a', 'b'].filter(fn.isEmpty); // [''] | ||
* [[], [1], [2]].filter(fn.isEmpty); // [[]] | ||
* ``` | ||
*/ | ||
export const isEmpty = <T = any>(item: T[] | string): boolean => Boolean(!item || !item.length); | ||
/**<!-- DOCS: #### --> | ||
* isEmpty | ||
* | ||
* - `fn.isEmpty` | ||
* - `fn.filters.isEmpty` | ||
* | ||
* Returns true if item's length is 0 | ||
* | ||
* ```typescript | ||
* ['', 'a', 'b'].filter(fn.isEmpty); // [''] | ||
* [[], [1], [2]].filter(fn.isEmpty); // [[]] | ||
* ``` | ||
*/ | ||
export const isEmpty = <T = any>(item: T[] | string): boolean => Boolean(!item || !item.length); | ||
/**<!-- DOCS: #### --> | ||
* isNotEmpty | ||
* | ||
* - `fn.isNotEmpty` | ||
* - `fn.filters.isNotEmpty` | ||
* - `filters.isNotEmpty` | ||
* | ||
* Returns true if item's length is 1 or more | ||
* | ||
* ```typescript | ||
* ['', 'a', 'b'].filter(fn.isNotEmpty); // ['a', 'b'] | ||
* [[], [1], [2]].filter(fn.isNotEmpty); // [[1], [2]] | ||
* ``` | ||
*/ | ||
export const isNotEmpty = <T = any>(item: T[] | string): boolean => Boolean(item && item.length); | ||
/**<!-- DOCS: #### --> | ||
* isNotEmpty | ||
* | ||
* - `fn.isNotEmpty` | ||
* - `fn.filters.isNotEmpty` | ||
* | ||
* Returns true if item's length is 1 or more | ||
* | ||
* ```typescript | ||
* ['', 'a', 'b'].filter(fn.isNotEmpty); // ['a', 'b'] | ||
* [[], [1], [2]].filter(fn.isNotEmpty); // [[1], [2]] | ||
* ``` | ||
*/ | ||
export const isNotEmpty = <T = any>(item: T[] | string): boolean => Boolean(item && item.length); | ||
/**<!-- DOCS: #### --> | ||
* isEqual | ||
* | ||
* - `fn.isEqual` | ||
* - `fn.filters.isEqual` | ||
* - `filters.isEqual` | ||
* | ||
* Returns a function that returns true if the item is equal to provided value. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].filter(fn.isEqual(1)); // [1] | ||
* ``` | ||
*/ | ||
export const isEqual = | ||
<T = any>(item: T) => | ||
(other: T) => | ||
Boolean(item === other); | ||
/**<!-- DOCS: #### --> | ||
* isEqual | ||
* | ||
* - `fn.isEqual` | ||
* - `fn.filters.isEqual` | ||
* | ||
* Returns a function that returns true if the item is equal to provided value. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].filter(fn.isEqual(1)); // [1] | ||
* ``` | ||
*/ | ||
export const isEqual = | ||
<T = any>(item: T) => | ||
(other: T) => | ||
Boolean(item === other); | ||
/**<!-- DOCS: #### --> | ||
* isNotEqual | ||
* | ||
* - `fn.isNotEqual` | ||
* - `fn.filters.isNotEqual` | ||
* - `filters.isNotEqual` | ||
* | ||
* Returns a function that returns true if the item is not equal to provided value. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].filter(fn.isNotEqual(1)); // [0, 2] | ||
* ``` | ||
*/ | ||
export const isNotEqual = | ||
<T = any>(item: T) => | ||
(other: T) => | ||
Boolean(item !== other); | ||
/**<!-- DOCS: #### --> | ||
* isNotEqual | ||
* | ||
* - `fn.isNotEqual` | ||
* - `fn.filters.isNotEqual` | ||
* | ||
* Returns a function that returns true if the item is not equal to provided value. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].filter(fn.isNotEqual(1)); // [0, 2] | ||
* ``` | ||
*/ | ||
export const isNotEqual = | ||
<T = any>(item: T) => | ||
(other: T) => | ||
Boolean(item !== other); | ||
/**<!-- DOCS: #### --> | ||
* dedupe | ||
* | ||
* - `fn.dedupe` | ||
* - `fn.filters.dedupe` | ||
* - `filters.dedupe` | ||
* | ||
* Removes duplicate items from an array. | ||
* | ||
* ```typescript | ||
* [0, 1, 2, 1, 0].filter(fn.dedupe); // [0, 1, 2] | ||
* ``` | ||
*/ | ||
export const dedupe = <T extends unknown>(item: T, index: number, array: T[]): boolean => array.indexOf(item) === index; | ||
/**<!-- DOCS: #### --> | ||
* dedupe | ||
* | ||
* - `fn.dedupe` | ||
* - `fn.filters.dedupe` | ||
* | ||
* Removes duplicate items from an array. | ||
* | ||
* ```typescript | ||
* [0, 1, 2, 1, 0].filter(fn.dedupe); // [0, 1, 2] | ||
* ``` | ||
*/ | ||
export const dedupe = <T extends unknown>(item: T, index: number, array: T[]): boolean => array.indexOf(item) === index; | ||
/**<!-- DOCS: #### --> | ||
* dedupeMapped | ||
* | ||
* - `fn.dedupeMapped` | ||
* - `fn.filters.dedupeMapped` | ||
* | ||
* Removes duplicate items from an array based on a mapped value. | ||
* | ||
* ```typescript | ||
* [2, 4, 6, 8, 10, 12].filter(fn.dedupeMapped((v) => v % 3)); // [ 2, 4, 6 ] (maps to [ 2, 1, 0, 2, 1, 0 ]) | ||
* ``` | ||
*/ | ||
export const dedupeMapped = <T extends unknown, U extends unknown>(mapFn: (value: T, index: number, array: T[]) => U) => { | ||
let mapped: U[]; | ||
return (item: T, index: number, array: T[]): boolean => { | ||
if (!mapped) mapped = array.map(mapFn); | ||
return mapped.indexOf(mapped[index]) === index; | ||
/**<!-- DOCS: #### --> | ||
* dedupeMapped | ||
* | ||
* - `fn.dedupeMapped` | ||
* - `fn.filters.dedupeMapped` | ||
* - `filters.dedupeMapped` | ||
* | ||
* Removes duplicate items from an array based on a mapped value. | ||
* | ||
* ```typescript | ||
* [2, 4, 6, 8, 10, 12].filter(fn.dedupeMapped((v) => v % 3)); // [ 2, 4, 6 ] (maps to [ 2, 1, 0, 2, 1, 0 ]) | ||
* ``` | ||
*/ | ||
export const dedupeMapped = <T extends unknown, U extends unknown>(mapFn: (value: T, index: number, array: T[]) => U) => { | ||
let mapped: U[]; | ||
return (item: T, index: number, array: T[]): boolean => { | ||
if (!mapped) mapped = array.map(mapFn); | ||
return mapped.indexOf(mapped[index]) === index; | ||
}; | ||
}; | ||
}; | ||
export const filters = { | ||
exists, | ||
isTruthy, | ||
isFalsy, | ||
isEmpty, | ||
isNotEmpty, | ||
isEqual, | ||
isNotEqual, | ||
dedupe, | ||
dedupeMapped | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* maps | ||
* | ||
* - `fn.maps` | ||
* | ||
* Collection of functions that can be used with Array.map | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* toString | ||
* | ||
* - `fn.toString` | ||
* - `fn.maps.toString` | ||
* - `maps.toString` | ||
* | ||
* Maps the item to a string. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].map(fn.toString); // ['0', '1', '2'] | ||
* ``` | ||
*/ | ||
export const toString = <T = any>(item: T): string => item + ''; | ||
/**<!-- DOCS: ### --> | ||
* maps | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* toString | ||
* | ||
* - `fn.toString` | ||
* - `fn.maps.toString` | ||
* | ||
* Maps the item to a string. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].map(fn.toString); // ['0', '1', '2'] | ||
* ``` | ||
*/ | ||
export const toString = <T = any>(item: T): string => item + ''; | ||
/**<!-- DOCS: #### --> | ||
* toNumber | ||
* | ||
* - `fn.toNumber` | ||
* - `fn.maps.toNumber` | ||
* - `maps.toNumber` | ||
* | ||
* Maps the item to a number. | ||
* | ||
* ```typescript | ||
* ['0', '1', '2'].map(fn.toNumber); // [0, 1, 2] | ||
* ``` | ||
*/ | ||
export const toNumber = <T = any>(item: T): number => Number(item); | ||
/**<!-- DOCS: #### --> | ||
* toNumber | ||
* | ||
* - `fn.toNumber` | ||
* - `fn.maps.toNumber` | ||
* | ||
* Maps the item to a number. | ||
* | ||
* ```typescript | ||
* ['0', '1', '2'].map(fn.toNumber); // [0, 1, 2] | ||
* ``` | ||
*/ | ||
export const toNumber = <T = any>(item: T): number => Number(item); | ||
/**<!-- DOCS: #### --> | ||
* toBool | ||
* | ||
* - `fn.toBool` | ||
* - `fn.maps.toBool` | ||
* - `maps.toBool` | ||
* | ||
* Maps the item to a boolean. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].map(fn.toBool); // [false, true, true] | ||
* ['true', 'false', '', 'text'].map(fn.toBool); // [true, false, false, true] | ||
* ``` | ||
*/ | ||
export const toBool = <T = any>(item: T): boolean => (item as any) !== 'false' && Boolean(item); | ||
/**<!-- DOCS: #### --> | ||
* toBool | ||
* | ||
* - `fn.toBool` | ||
* - `fn.maps.toBool` | ||
* | ||
* Maps the item to a boolean. | ||
* | ||
* ```typescript | ||
* [0, 1, 2].map(fn.toBool); // [false, true, true] | ||
* ['true', 'false', '', 'text'].map(fn.toBool); // [true, false, false, true] | ||
* ``` | ||
*/ | ||
export const toBool = <T = any>(item: T): boolean => (item as any) !== 'false' && Boolean(item); | ||
/**<!-- DOCS: #### --> | ||
* toProp | ||
* | ||
* - `fn.toProp` | ||
* - `fn.maps.toProp` | ||
* - `maps.toProp` | ||
* | ||
* Maps the item to a given property of the item | ||
* | ||
* ```typescript | ||
* [{name: 'Jack'}, {name: 'Jill'}].map(fn.toProp('name')); // ['Jack', 'Jill'] | ||
* ``` | ||
*/ | ||
export const toProp = | ||
<P = string, O = Object>(prop: string) => | ||
(item: O): P => | ||
item && item[prop]; | ||
/**<!-- DOCS: #### --> | ||
* toProp | ||
* | ||
* - `fn.toProp` | ||
* - `fn.maps.toProp` | ||
* | ||
* Maps the item to a given property of the item | ||
* | ||
* ```typescript | ||
* [{name: 'Jack'}, {name: 'Jill'}].map(fn.toProp('name')); // ['Jack', 'Jill'] | ||
* ``` | ||
*/ | ||
export const toProp = | ||
<P = string, O = Object>(prop: string) => | ||
(item: O): P => | ||
item && item[prop]; | ||
/**<!-- DOCS: #### --> | ||
* toFixed | ||
* | ||
* - `fn.toFixed` | ||
* - `fn.maps.toFixed` | ||
* - `maps.toFixed` | ||
* | ||
* Map the items (numbers) of an array to a fixed precision. | ||
* | ||
* ```typescript | ||
* [1.234, 5.678, 9.012].map(fn.toFixed(2)); // [1.23, 5.68, 9.01] | ||
* ``` | ||
*/ | ||
export const toFixed = | ||
(precision: number) => | ||
(num: number): number => | ||
MathsTools.fixFloat(num, precision); | ||
/**<!-- DOCS: #### --> | ||
* toFixed | ||
* | ||
* - `fn.toFixed` | ||
* - `fn.maps.toFixed` | ||
* | ||
* Map the items (numbers) of an array to a fixed precision. | ||
* | ||
* ```typescript | ||
* [1.234, 5.678, 9.012].map(fn.toFixed(2)); // [1.23, 5.68, 9.01] | ||
* ``` | ||
*/ | ||
export const toFixed = | ||
(precision: number) => | ||
(num: number): number => | ||
fixFloat(num, precision); | ||
/**<!-- DOCS: ### --> | ||
* sorts | ||
* | ||
* - `fn.sorts` | ||
* | ||
* Collection of functions that can be used with Array.sort | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* asc | ||
* | ||
* - `fn.asc` | ||
* - `fn.sorts.asc` | ||
* - `sorts.asc` | ||
* | ||
* Sort ascending. | ||
* | ||
* ```typescript | ||
* [2, 4, 3, 1].sort(fn.asc); // [1, 2, 3, 4] | ||
* ``` | ||
*/ | ||
export const asc = (a: any, b: any): number => { | ||
if (a < b) return -1; | ||
if (b < a) return 1; | ||
return 0; | ||
}; | ||
export const maps = { | ||
toString, | ||
toNumber, | ||
toBool, | ||
toProp, | ||
toFixed | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* desc | ||
* | ||
* - `fn.desc` | ||
* - `fn.sorts.desc` | ||
* - `sorts.desc` | ||
* | ||
* Sort descending. | ||
* | ||
* ```typescript | ||
* [2, 4, 3, 1].sort(fn.asc); // [4, 3, 2, 1] | ||
* ``` | ||
*/ | ||
export const desc = (a: any, b: any): number => { | ||
if (a < b) return 1; | ||
if (b < a) return -1; | ||
return 0; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* sorts | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* asc | ||
* | ||
* - `fn.asc` | ||
* - `fn.sorts.asc` | ||
* | ||
* Sort ascending. | ||
* | ||
* ```typescript | ||
* [2, 4, 3, 1].sort(fn.asc); // [1, 2, 3, 4] | ||
* ``` | ||
*/ | ||
export const asc = (a: any, b: any): number => { | ||
if (a < b) return -1; | ||
if (b < a) return 1; | ||
return 0; | ||
}; | ||
type SortFn<T = number> = (a: T, b: T) => number; | ||
/**<!-- DOCS: #### --> | ||
* desc | ||
* | ||
* - `fn.desc` | ||
* - `fn.sorts.desc` | ||
* | ||
* Sort descending. | ||
* | ||
* ```typescript | ||
* [2, 4, 3, 1].sort(fn.asc); // [4, 3, 2, 1] | ||
* ``` | ||
*/ | ||
export const desc = (a: any, b: any): number => { | ||
if (a < b) return 1; | ||
if (b < a) return -1; | ||
return 0; | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* byProp | ||
* | ||
* - `fn.byProp` | ||
* - `fn.sorts.byProp` | ||
* - `sorts.byProp` | ||
* | ||
* Sort by a given property. | ||
* | ||
* ```typescript | ||
* const people = [{age: 2}, {age: 4}, {age: 3}, {age: 1}]; | ||
* people.sort(fn.byProp('age', fn.asc)); // [{age: 1}, {age: 2}, {age: 3}, {age: 4}] | ||
* ``` | ||
*/ | ||
export const byProp = <T = number, O = Object>(propName: string, sortFn: SortFn<T> = asc): SortFn<O> => { | ||
return (a: O, b: O) => sortFn(a[propName] as T, b[propName] as T); | ||
}; | ||
type SortFn<T = number> = (a: T, b: T) => number; | ||
/**<!-- DOCS: #### --> | ||
* nearestTo | ||
* | ||
* - `fn.nearestTo` | ||
* - `fn.sorts.nearestTo` | ||
* - `sorts.nearestTo` | ||
* | ||
* Sort by the nearest value to the given value. | ||
* | ||
* ```typescript | ||
* const people = [2, 4, 3, 1]; | ||
* people.sort(fn.nearestTo(3)); // [3, 2, 4, 1] | ||
* ``` | ||
*/ | ||
export const nearestTo = | ||
<T = number>(target: T) => | ||
(a: any, b: any) => | ||
Math.abs(Number(target) - Number(a)) - Math.abs(Number(target) - Number(b)); | ||
/**<!-- DOCS: #### --> | ||
* byProp | ||
* | ||
* - `fn.byProp` | ||
* - `fn.sorts.byProp` | ||
* | ||
* Sort by a given property. | ||
* | ||
* ```typescript | ||
* const people = [{age: 2}, {age: 4}, {age: 3}, {age: 1}]; | ||
* people.sort(fn.byProp('age', fn.asc)); // [{age: 1}, {age: 2}, {age: 3}, {age: 4}] | ||
* ``` | ||
*/ | ||
export const byProp = <T = number, O = Object>(propName: string, sortFn: SortFn<T> = asc): SortFn<O> => { | ||
return (a: O, b: O) => sortFn(a[propName] as T, b[propName] as T); | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* furthestFrom | ||
* | ||
* - `fn.furthestFrom` | ||
* - `fn.sorts.furthestFrom` | ||
* - `sorts.furthestFrom` | ||
* | ||
* Sort by the furthest value to the given value. | ||
* | ||
* ```typescript | ||
* const people = [2, 4, 3, 1]; | ||
* people.sort(fn.furthestFrom(3)); // [1, 2, 4, 3] | ||
* ``` | ||
*/ | ||
export const furthestFrom = | ||
<T = number>(target: T) => | ||
(a: any, b: any) => | ||
Math.abs(Number(target) - Number(b)) - Math.abs(Number(target) - Number(a)); | ||
/**<!-- DOCS: #### --> | ||
* nearestTo | ||
* | ||
* - `fn.nearestTo` | ||
* - `fn.sorts.nearestTo` | ||
* | ||
* Sort by the nearest value to the given value. | ||
* | ||
* ```typescript | ||
* const people = [2, 4, 3, 1]; | ||
* people.sort(fn.nearestTo(3)); // [3, 2, 4, 1] | ||
* ``` | ||
*/ | ||
export const nearestTo = | ||
<T = number>(target: T) => | ||
(a: any, b: any) => | ||
Math.abs(Number(target) - Number(a)) - Math.abs(Number(target) - Number(b)); | ||
/**<!-- DOCS: #### --> | ||
* arrayAsc | ||
* | ||
* - `fn.arrayAsc` | ||
* - `fn.sorts.arrayAsc` | ||
* - `sorts.arrayAsc` | ||
* | ||
* Sort an array of arrays in ascending order | ||
*/ | ||
export const arrayAsc = (a: any[], b: any[]) => { | ||
for (let i in a) { | ||
const result = asc(a[i], b[i]); | ||
if (result !== 0) return result; | ||
} | ||
return 0; | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* furthestFrom | ||
* | ||
* - `fn.furthestFrom` | ||
* - `fn.sorts.furthestFrom` | ||
* | ||
* Sort by the furthest value to the given value. | ||
* | ||
* ```typescript | ||
* const people = [2, 4, 3, 1]; | ||
* people.sort(fn.furthestFrom(3)); // [1, 2, 4, 3] | ||
* ``` | ||
*/ | ||
export const furthestFrom = | ||
<T = number>(target: T) => | ||
(a: any, b: any) => | ||
Math.abs(Number(target) - Number(b)) - Math.abs(Number(target) - Number(a)); | ||
/**<!-- DOCS: #### --> | ||
* arrayDesc | ||
* | ||
* - `fn.arrayDesc` | ||
* - `fn.sorts.arrayDesc` | ||
* - `sorts.arrayDesc` | ||
* | ||
* Sort an array of arrays in descending order | ||
*/ | ||
export const arrayDesc = (a: any[], b: any[]) => { | ||
for (let i in a) { | ||
const result = desc(a[i], b[i]); | ||
if (result !== 0) return result; | ||
} | ||
return 0; | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* arrayAsc | ||
* | ||
* - `fn.arrayAsc` | ||
* - `fn.sorts.arrayAsc` | ||
* | ||
* Sort an array of arrays in ascending order | ||
*/ | ||
export const arrayAsc = (a: any[], b: any[]) => { | ||
for (let i in a) { | ||
const result = sorts.asc(a[i], b[i]); | ||
if (result !== 0) return result; | ||
} | ||
return 0; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* reduces | ||
* | ||
* - `fn.reduces` | ||
* | ||
* Collection of functions that can be used with Array.reduce | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* arrayDesc | ||
* | ||
* - `fn.arrayDesc` | ||
* - `fn.sorts.arrayDesc` | ||
* | ||
* Sort an array of arrays in descending order | ||
*/ | ||
export const arrayDesc = (a: any[], b: any[]) => { | ||
for (let i in a) { | ||
const result = sorts.desc(a[i], b[i]); | ||
if (result !== 0) return result; | ||
} | ||
return 0; | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* combine | ||
* | ||
* - `fn.combine` | ||
* - `fn.reduces.combine` | ||
* - `reduces.combine` | ||
* | ||
* Adds or concats the items | ||
* | ||
* ```typescript | ||
* [1, 2, 3].reduce(fn.combine); // 6 | ||
* ['a', 'b', 'c'].reduce(fn.combine); // 'abc' | ||
* ``` | ||
*/ | ||
export const combine = (a: any, b: any): any => a + b; | ||
export const sorts = { | ||
asc, | ||
desc, | ||
byProp, | ||
nearestTo, | ||
furthestFrom, | ||
arrayAsc, | ||
arrayDesc | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* combineProp | ||
* | ||
* - `fn.combineProp` | ||
* - `fn.reduces.combineProp` | ||
* - `reduces.combineProp` | ||
* | ||
* Adds or concats the given property of the items | ||
* | ||
* ```typescript | ||
* const people = [{name: 'a', age: 1}, {name: 'b', age: 2}, {name: 'c', age: 3}]; | ||
* people.reduce(fn.combineProp('age')); // 6 | ||
* people.reduce(fn.combineProp('name')); // 'abc' | ||
* ``` | ||
*/ | ||
export const combineProp = | ||
(propName: string) => | ||
(a: any, b: any): any => | ||
a[propName] + b[propName]; | ||
/**<!-- DOCS: ### --> | ||
* reduces | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* combine | ||
* | ||
* - `fn.combine` | ||
* - `fn.reduces.combine` | ||
* | ||
* Adds or concats the items | ||
* | ||
* ```typescript | ||
* [1, 2, 3].reduce(fn.combine); // 6 | ||
* ['a', 'b', 'c'].reduce(fn.combine); // 'abc' | ||
* ``` | ||
*/ | ||
export const combine = (a: any, b: any): any => a + b; | ||
/**<!-- DOCS: #### --> | ||
* mode | ||
* | ||
* - `fn.mode` | ||
* - `fn.reduces.mode` | ||
* - `reduces.mode` | ||
* | ||
* Returns the most common value in an array. | ||
* | ||
* ```typescript | ||
* [1, 2, 3, 2, 1, 1].reduce(fn.mode); // 1 | ||
* ``` | ||
*/ | ||
export const mode = <T extends unknown>(prev: T, curr: T, index: number, arr: T[]): T => { | ||
if (index > 1) { | ||
// First iteration will be index 1, because it will | ||
// skip index 0, and use value at [0] for first 'prev'. | ||
return prev; | ||
} | ||
const unique = arr.filter(dedupe); | ||
const counts = unique.map((item) => arr.filter((i) => i === item)).map((a) => a.length); | ||
const max = Math.max(...counts); | ||
/**<!-- DOCS: #### --> | ||
* combineProp | ||
* | ||
* - `fn.combineProp` | ||
* - `fn.reduces.combineProp` | ||
* | ||
* Adds or concats the given property of the items | ||
* | ||
* ```typescript | ||
* const people = [{name: 'a', age: 1}, {name: 'b', age: 2}, {name: 'c', age: 3}]; | ||
* people.reduce(fn.combineProp('age')); // 6 | ||
* people.reduce(fn.combineProp('name')); // 'abc' | ||
* ``` | ||
*/ | ||
export const combineProp = | ||
(propName: string) => | ||
(a: any, b: any): any => | ||
a[propName] + b[propName]; | ||
return unique[counts.indexOf(max)]; | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* mode | ||
* | ||
* - `fn.mode` | ||
* - `fn.reduces.mode` | ||
* | ||
* Returns the most common value in an array. | ||
* | ||
* ```typescript | ||
* [1, 2, 3, 2, 1, 1].reduce(fn.mode); // 1 | ||
* ``` | ||
*/ | ||
export const mode = <T extends unknown>(prev: T, curr: T, index: number, arr: T[]): T => { | ||
if (index > 1) { | ||
// First iteration will be index 1, because it will | ||
// skip index 0, and use value at [0] for first 'prev'. | ||
return prev; | ||
} | ||
const unique = arr.filter(filters.dedupe); | ||
const counts = unique.map((item) => arr.filter((i) => i === item)).map((a) => a.length); | ||
const max = Math.max(...counts); | ||
/**<!-- DOCS: #### --> | ||
* modeMapped | ||
* | ||
* - `fn.modeMapped` | ||
* - `fn.reduces.modeMapped` | ||
* - `reduces.modeMapped` | ||
* | ||
* Returns the most common value in an array, based on a given map function. | ||
* | ||
* ```typescript | ||
* [2, 4, 6, 8, 9, 12].reduce(fn.modeMapped((v) => v % 3)); // 6 (maps to [ 2, 1, 0, 2, 0, 0 ]) | ||
* ``` | ||
*/ | ||
export const modeMapped = <T extends unknown, U extends unknown>(mapFn: (value: T, index: number, array: T[]) => U) => { | ||
let result: T; | ||
return unique[counts.indexOf(max)]; | ||
}; | ||
return (prev: T, curr: T, index: number, arr: T[]): T => { | ||
if (result) return result; | ||
/**<!-- DOCS: #### --> | ||
* modeMapped | ||
* | ||
* - `fn.modeMapped` | ||
* - `fn.reduces.modeMapped` | ||
* | ||
* Returns the most common value in an array, based on a given map function. | ||
* | ||
* ```typescript | ||
* [2, 4, 6, 8, 9, 12].reduce(fn.modeMapped((v) => v % 3)); // 6 (maps to [ 2, 1, 0, 2, 0, 0 ]) | ||
* ``` | ||
*/ | ||
export const modeMapped = <T extends unknown, U extends unknown>(mapFn: (value: T, index: number, array: T[]) => U) => { | ||
let result: T; | ||
const mapped: U[] = arr.map(mapFn); | ||
return (prev: T, curr: T, index: number, arr: T[]): T => { | ||
if (result) return result; | ||
const uniqueU: U[] = mapped.filter(dedupe); | ||
const uniqueT: T[] = arr.filter(dedupeMapped(mapFn)); | ||
const mapped: U[] = arr.map(mapFn); | ||
const counts: number[] = uniqueU.map((item) => mapped.filter((i) => i === item)).map((a) => a.length); | ||
const max: number = Math.max(...counts); | ||
const uniqueU: U[] = mapped.filter(filters.dedupe); | ||
const uniqueT: T[] = arr.filter(filters.dedupeMapped(mapFn)); | ||
result = uniqueT[counts.indexOf(max)]; | ||
const counts: number[] = uniqueU.map((item) => mapped.filter((i) => i === item)).map((a) => a.length); | ||
const max: number = Math.max(...counts); | ||
return result; | ||
}; | ||
}; | ||
result = uniqueT[counts.indexOf(max)]; | ||
/**<!-- DOCS: ### --> | ||
* everys | ||
* | ||
* - `fn.everys` | ||
* | ||
* Collection of functions that can be used with Array.every | ||
*/ | ||
return result; | ||
}; | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* isAllEqual | ||
* | ||
* - `fn.isAllEqual` | ||
* - `fn.everys.isAllEqual` | ||
* - `everys.isAllEqual` | ||
* | ||
* Returns if all the items are equal to one another. | ||
* | ||
* ```typescript | ||
* [1, 1, 1].every(fn.isAllEqual); // true | ||
* [1, 2, 1].every(fn.isAllEqual); // false | ||
* ``` | ||
*/ | ||
export const isAllEqual = <T = any>(val: T, i, arr: T[]): boolean => val === arr[0]; | ||
export const reduces = { | ||
combine, | ||
combineProp, | ||
mode, | ||
modeMapped | ||
}; | ||
/** ALIAS - filters */ | ||
export namespace filters { | ||
/** ALIAS - filters.exists */ | ||
export const exists = fn.exists; | ||
/** ALIAS - filters.isTruthy */ | ||
export const isTruthy = fn.isTruthy; | ||
/** ALIAS - filters.isFalsy */ | ||
export const isFalsy = fn.isFalsy; | ||
/** ALIAS - filters.isEmpty */ | ||
export const isEmpty = fn.isEmpty; | ||
/** ALIAS - filters.isNotEmpty */ | ||
export const isNotEmpty = fn.isNotEmpty; | ||
/** ALIAS - filters.isEqual */ | ||
export const isEqual = fn.isEqual; | ||
/** ALIAS - filters.isNotEqual */ | ||
export const isNotEqual = fn.isNotEqual; | ||
/** ALIAS - filters.dedupe */ | ||
export const dedupe = fn.dedupe; | ||
/** ALIAS - filters.dedupeMapped */ | ||
export const dedupeMapped = fn.dedupeMapped; | ||
} | ||
/**<!-- DOCS: ### --> | ||
* everys | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* isAllEqual | ||
* | ||
* - `fn.isAllEqual` | ||
* - `fn.everys.isAllEqual` | ||
* | ||
* Returns if all the items are equal to one another. | ||
* | ||
* ```typescript | ||
* [1, 1, 1].every(fn.isAllEqual); // true | ||
* [1, 2, 1].every(fn.isAllEqual); // false | ||
* ``` | ||
*/ | ||
export const isAllEqual = <T = any>(val: T, i, arr: T[]): boolean => val === arr[0]; | ||
/** ALIAS - maps */ | ||
export namespace maps { | ||
/** ALIAS - maps.toString */ | ||
export const toString = fn.toString; | ||
/** ALIAS - maps.toNumber */ | ||
export const toNumber = fn.toNumber; | ||
/** ALIAS - maps.toBool */ | ||
export const toBool = fn.toBool; | ||
/** ALIAS - maps.toProp */ | ||
export const toProp = fn.toProp; | ||
/** ALIAS - maps.toFixed */ | ||
export const toFixed = fn.toFixed; | ||
} | ||
export const everys = { | ||
isAllEqual | ||
}; | ||
/** ALIAS - sorts */ | ||
export namespace sorts { | ||
/** ALIAS - sorts.asc */ | ||
export const asc = fn.asc; | ||
/** ALIAS - sorts.desc */ | ||
export const desc = fn.desc; | ||
/** ALIAS - sorts.byProp */ | ||
export const byProp = fn.byProp; | ||
/** ALIAS - sorts.nearestTo */ | ||
export const nearestTo = fn.nearestTo; | ||
/** ALIAS - sorts.furthestFrom */ | ||
export const furthestFrom = fn.furthestFrom; | ||
/** ALIAS - sorts.arrayAsc */ | ||
export const arrayAsc = fn.arrayAsc; | ||
/** ALIAS - sorts.arrayDesc */ | ||
export const arrayDesc = fn.arrayDesc; | ||
} | ||
/** ALIAS - reduces */ | ||
export namespace reduces { | ||
/** ALIAS - reduces.combine */ | ||
export const combine = fn.combine; | ||
/** ALIAS - reduces.combineProp */ | ||
export const combineProp = fn.combineProp; | ||
/** ALIAS - reduces.mode */ | ||
export const mode = fn.mode; | ||
/** ALIAS - reduces.modeMapped */ | ||
export const modeMapped = fn.modeMapped; | ||
} | ||
/** ALIAS - everys */ | ||
export namespace everys { | ||
/** ALIAS - everys.isAllEqual */ | ||
export const isAllEqual = fn.isAllEqual; | ||
} | ||
} | ||
/** ALIAS - filters */ | ||
export const filters = fn.filters; | ||
/** ALIAS - maps */ | ||
export const maps = fn.maps; | ||
/** ALIAS - sorts */ | ||
export const sorts = fn.sorts; | ||
/** ALIAS - reduces */ | ||
export const reduces = fn.reduces; | ||
/** ALIAS - everys */ | ||
export const everys = fn.everys; |
@@ -1,2 +0,2 @@ | ||
import { zip } from './ArrayTools'; | ||
import { ArrayTools } from './ArrayTools'; | ||
@@ -11,191 +11,211 @@ //<!-- DOCS: 130 --> | ||
*/ | ||
export namespace MathsTools { | ||
/**<!-- DOCS: ### --> | ||
* fixFloat | ||
* | ||
* - `ff` | ||
* - `MathsTools.ff` | ||
* - `MathsTools.fixFloat` | ||
* | ||
* Fixes floating point errors that may occur when adding/subtracting/multiplying/dividing real/float numbers | ||
* | ||
* Can also be used to round numbers to a given precision | ||
* | ||
* > Note: 'fixFloat' is not a great name, but it's what I've always called it, so I'm sticking with it. 'ff' is a shorthand alias. | ||
* | ||
* ```typescript | ||
* 0.1 + 0.2 // 0.30000000000000004 | ||
* MathsTools.fixFloat(0.1 + 0.2) // 0.3 | ||
* ``` | ||
*/ | ||
export const fixFloat = (num: number, precision = 6): number => Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision); | ||
/**<!-- DOCS: ### --> | ||
* fixFloat | ||
* | ||
* - `MathsTools.fixFloat` | ||
* | ||
* Fixes floating point errors that may occur when adding/subtracting/multiplying/dividing real/float numbers | ||
* | ||
* Can also be used to round numbers to a given precision | ||
* | ||
* > Note: It's not a great name, but it's what I've always called it, so I'm sticking with it. May create an alias | ||
* | ||
* ```typescript | ||
* 0.1 + 0.2 // 0.30000000000000004 | ||
* MathsTools.fixFloat(0.1 + 0.2) // 0.3 | ||
* ``` | ||
*/ | ||
export const fixFloat = (num: number, precision = 6): number => Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision); | ||
/** ALIAS - fixFloat */ | ||
export const ff = fixFloat; | ||
/**<!-- DOCS: ### --> | ||
* addAll | ||
* | ||
* - `MathsTools.addAll` | ||
* | ||
* Adds all numbers together. Each argument is a number (use spread operator to pass in an array) similar to Math.min/Math.max | ||
* | ||
* ```typescript | ||
* MathsTools.addAll(1, 2, 3, 4, 5); // 15 | ||
* ``` | ||
*/ | ||
export const addAll = (...args: number[]): number => args.reduce((acc, num) => acc + num, 0); | ||
/**<!-- DOCS: ### --> | ||
* addAll | ||
* | ||
* - `MathsTools.addAll` | ||
* | ||
* Adds all numbers together. Each argument is a number (use spread operator to pass in an array) similar to Math.min/Math.max | ||
* | ||
* ```typescript | ||
* MathsTools.addAll(1, 2, 3, 4, 5); // 15 | ||
* ``` | ||
*/ | ||
export const addAll = (...args: number[]): number => args.reduce((acc, num) => acc + num, 0); | ||
/**<!-- DOCS: ### --> | ||
* round | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* floorTo | ||
* | ||
* - `MathsTools.floorTo` | ||
* - `MathsTools.round.floorTo` | ||
* | ||
* Floors a number down to the nearest multiple of the given number. | ||
* | ||
* ```typescript | ||
* MathsTools.round.floorTo(10, 102); // 100 | ||
* MathsTools.round.floorTo(5, 53); // 50 | ||
* MathsTools.round.floorTo(0.1, 0.25); // 0.2 | ||
* ``` | ||
*/ | ||
export const floorTo = (to: number, value: number) => fixFloat(Math.floor(value / to) * to); | ||
/**<!-- DOCS: ### --> | ||
* round | ||
*/ | ||
/**<!-- DOCS: #### --> | ||
* floorTo | ||
* | ||
* - `MathsTools.floorTo` | ||
* - `MathsTools.round.floorTo` | ||
* | ||
* Floors a number down to the nearest multiple of the given number. | ||
* | ||
* ```typescript | ||
* MathsTools.round.floorTo(10, 102); // 100 | ||
* MathsTools.round.floorTo(5, 53); // 50 | ||
* MathsTools.round.floorTo(0.1, 0.25); // 0.2 | ||
* ``` | ||
*/ | ||
export const floorTo = (to: number, value: number) => fixFloat(Math.floor(value / to) * to); | ||
/**<!-- DOCS: #### --> | ||
* roundTo | ||
* | ||
* - `MathsTools.round.to` | ||
* - `MathsTools.roundTo` | ||
* - `MathsTools.round.roundTo` | ||
* | ||
* Floors a number down to the nearest multiple of the given number. | ||
* | ||
* ```typescript | ||
* MathsTools.round.to(10, 102); // 100 | ||
* MathsTools.round.to(5, 53); // 55 | ||
* MathsTools.round.to(0.1, 0.25); // 0.3 | ||
* ``` | ||
*/ | ||
export const roundTo = (to: number, value: number) => fixFloat(Math.round(value / to) * to); | ||
/**<!-- DOCS: #### --> | ||
* roundTo | ||
* | ||
* - `MathsTools.round.to` | ||
* - `MathsTools.roundTo` | ||
* - `MathsTools.round.roundTo` | ||
* | ||
* Floors a number down to the nearest multiple of the given number. | ||
* | ||
* ```typescript | ||
* MathsTools.round.to(10, 102); // 100 | ||
* MathsTools.round.to(5, 53); // 55 | ||
* MathsTools.round.to(0.1, 0.25); // 0.3 | ||
* ``` | ||
*/ | ||
export const roundTo = (to: number, value: number) => fixFloat(Math.round(value / to) * to); | ||
/**<!-- DOCS: #### --> | ||
* ceilTo | ||
* | ||
* - `MathsTools.ceilTo` | ||
* - `MathsTools.round.ceilTo` | ||
* | ||
* Floors a number down to the nearest multiple of the given number. | ||
* | ||
* ```typescript | ||
* MathsTools.round.ceilTo(10, 102); // 110 | ||
* MathsTools.round.ceilTo(5, 53); // 55 | ||
* MathsTools.round.ceilTo(0.1, 0.25); // 0.3 | ||
* ``` | ||
*/ | ||
export const ceilTo = (to: number, value: number) => fixFloat(Math.ceil(value / to) * to); | ||
/**<!-- DOCS: #### --> | ||
* ceilTo | ||
* | ||
* - `MathsTools.ceilTo` | ||
* - `MathsTools.round.ceilTo` | ||
* | ||
* Floors a number down to the nearest multiple of the given number. | ||
* | ||
* ```typescript | ||
* MathsTools.round.ceilTo(10, 102); // 110 | ||
* MathsTools.round.ceilTo(5, 53); // 55 | ||
* MathsTools.round.ceilTo(0.1, 0.25); // 0.3 | ||
* ``` | ||
*/ | ||
export const ceilTo = (to: number, value: number) => fixFloat(Math.ceil(value / to) * to); | ||
export const round = { | ||
floorTo, | ||
roundTo, | ||
ceilTo, | ||
to: roundTo | ||
}; | ||
/** | ||
* round | ||
* | ||
* - `MathsTools.round` | ||
* | ||
* A collection of rounding functions. | ||
*/ | ||
export namespace round { | ||
/** ALIAS - floorTo */ | ||
export const floorTo = MathsTools.floorTo; | ||
/** ALIAS - roundTo */ | ||
export const roundTo = MathsTools.roundTo; | ||
/** ALIAS - ceilTo */ | ||
export const ceilTo = MathsTools.ceilTo; | ||
/** ALIAS - roundTo */ | ||
export const to = MathsTools.roundTo; | ||
} | ||
/**<!-- DOCS: ### --> | ||
* lerp | ||
* | ||
* - `MathsTools.lerp` | ||
* | ||
* Linearly interpolates between two values. | ||
* | ||
* ```typescript | ||
* MathsTools.lerp(0.5, 0, 10); // 5 | ||
* ``` | ||
*/ | ||
export const lerp = (progress: number, fromVal: number, toVal: number): number => fromVal + (toVal - fromVal) * progress; | ||
/**<!-- DOCS: ### --> | ||
* lerp | ||
* | ||
* - `MathsTools.lerp` | ||
* | ||
* Linearly interpolates between two values. | ||
* | ||
* ```typescript | ||
* MathsTools.lerp(0.5, 0, 10); // 5 | ||
* ``` | ||
*/ | ||
export const lerp = (progress: number, fromVal: number, toVal: number): number => fromVal + (toVal - fromVal) * progress; | ||
/**<!-- DOCS: ### --> | ||
* lerpArray | ||
* | ||
* - `MathsTools.lerpArray` | ||
* | ||
* Linearly interpolates between the values of 2 arrays. | ||
* | ||
* ```typescript | ||
* MathsTools.lerpArray(0.5, [0, 0, 0], [10, 100, 1000]) // [5, 50, 500] | ||
* ``` | ||
*/ | ||
export const lerpArray = (progress: number, fromArr: number[], toArr: number[]): number[] => | ||
zip(fromArr, toArr).map(([fromVal, toVal]) => lerp(progress, fromVal, toVal)); | ||
/**<!-- DOCS: ### --> | ||
* lerpArray | ||
* | ||
* - `MathsTools.lerpArray` | ||
* | ||
* Linearly interpolates between the values of 2 arrays. | ||
* | ||
* ```typescript | ||
* MathsTools.lerpArray(0.5, [0, 0, 0], [10, 100, 1000]) // [5, 50, 500] | ||
* ``` | ||
*/ | ||
export const lerpArray = (progress: number, fromArr: number[], toArr: number[]): number[] => | ||
ArrayTools.zip(fromArr, toArr).map(([fromVal, toVal]) => lerp(progress, fromVal, toVal)); | ||
/**<!-- DOCS: ### --> | ||
* lerpObj | ||
* | ||
* - `MathsTools.lerpObj` | ||
* | ||
* Linearly interpolates between the values of 2 arrays. | ||
* | ||
* ```typescript | ||
* MathsTools.lerpObj(0.5, {'ARS': 0, 'CHE': 0, 'FUL': 0}, {'ARS': 100, 'CHE': 10, 'FUL': 20}) // {'ARS': 50, 'CHE': 5, 'FUL': 10} | ||
* ``` | ||
*/ | ||
export const lerpObj = <T extends object>(progress: number, fromObj: T, toObj: T): T => { | ||
const entries = Object.entries(fromObj); | ||
const lerped = entries.map(([key, fromVal]) => (typeof fromVal === 'number' ? [key, lerp(progress, fromVal, toObj[key])] : [key, fromVal])); | ||
return Object.fromEntries(lerped) as T; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* lerpObj | ||
* | ||
* - `MathsTools.lerpObj` | ||
* | ||
* Linearly interpolates between the values of 2 arrays. | ||
* | ||
* ```typescript | ||
* MathsTools.lerpObj(0.5, {'ARS': 0, 'CHE': 0, 'FUL': 0}, {'ARS': 100, 'CHE': 10, 'FUL': 20}) // {'ARS': 50, 'CHE': 5, 'FUL': 10} | ||
* ``` | ||
*/ | ||
export const lerpObj = <T extends object>(progress: number, fromObj: T, toObj: T): T => { | ||
const entries = Object.entries(fromObj); | ||
const lerped = entries.map(([key, fromVal]) => (typeof fromVal === 'number' ? [key, lerp(progress, fromVal, toObj[key])] : [key, fromVal])); | ||
return Object.fromEntries(lerped) as T; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* clamp | ||
* | ||
* - `MathsTools.clamp` | ||
* | ||
* Clamps a value between a min and max. | ||
* | ||
* ```typescript | ||
* MathsTools.clamp(5, 0, 10); // 5 | ||
* MathsTools.clamp(-5, 0, 10); // 0 | ||
* ``` | ||
*/ | ||
export const clamp = (value: number, min: number, max: number) => Math.max(Math.min(min, max), Math.min(value, Math.max(min, max))); | ||
/**<!-- DOCS: ### --> | ||
* clamp | ||
* | ||
* - `MathsTools.clamp` | ||
* | ||
* Clamps a value between a min and max. | ||
* | ||
* ```typescript | ||
* MathsTools.clamp(5, 0, 10); // 5 | ||
* MathsTools.clamp(-5, 0, 10); // 0 | ||
* ``` | ||
*/ | ||
export const clamp = (value: number, min: number, max: number) => Math.max(Math.min(min, max), Math.min(value, Math.max(min, max))); | ||
/**<!-- DOCS: ### --> | ||
* getOridinal | ||
* | ||
* - `MathsTools.getOridinal` | ||
* | ||
* Gets the ordinal suffix for a number. | ||
* | ||
* ```typescript | ||
* MathsTools.getOridinal(1); // 'st' | ||
* MathsTools.getOridinal(2); // 'nd' | ||
* MathsTools.getOridinal(3); // 'rd' | ||
* MathsTools.getOridinal(4); // 'th' | ||
* | ||
* MathsTools.getOridinal(11); // 'th' | ||
* MathsTools.getOridinal(12); // 'th' | ||
* MathsTools.getOridinal(13); // 'th' | ||
* MathsTools.getOridinal(14); // 'th' | ||
* | ||
* MathsTools.getOridinal(21); // 'st' | ||
* MathsTools.getOridinal(22); // 'nd' | ||
* MathsTools.getOridinal(23); // 'rd' | ||
* MathsTools.getOridinal(24); // 'th' | ||
* ``` | ||
*/ | ||
export const getOrdinal = (num: number = 0) => { | ||
const lastDigit = num % 10; | ||
/**<!-- DOCS: ### --> | ||
* getOrdinal | ||
* | ||
* - `MathsTools.getOrdinal` | ||
* | ||
* Gets the ordinal suffix for a number. | ||
* | ||
* ```typescript | ||
* MathsTools.getOrdinal(1); // 'st' | ||
* MathsTools.getOrdinal(2); // 'nd' | ||
* MathsTools.getOrdinal(3); // 'rd' | ||
* MathsTools.getOrdinal(4); // 'th' | ||
* | ||
* MathsTools.getOrdinal(11); // 'th' | ||
* MathsTools.getOrdinal(12); // 'th' | ||
* MathsTools.getOrdinal(13); // 'th' | ||
* MathsTools.getOrdinal(14); // 'th' | ||
* | ||
* MathsTools.getOrdinal(21); // 'st' | ||
* MathsTools.getOrdinal(22); // 'nd' | ||
* MathsTools.getOrdinal(23); // 'rd' | ||
* MathsTools.getOrdinal(24); // 'th' | ||
* ``` | ||
*/ | ||
export const getOrdinal = (num: number = 0) => { | ||
const lastDigit = num % 10; | ||
if ([11, 12, 13].includes(num)) { | ||
if ([11, 12, 13].includes(num)) { | ||
return 'th'; | ||
} | ||
if (lastDigit === 1) { | ||
return 'st'; | ||
} | ||
if (lastDigit === 2) { | ||
return 'nd'; | ||
} | ||
if (lastDigit === 3) { | ||
return 'rd'; | ||
} | ||
return 'th'; | ||
} | ||
if (lastDigit === 1) { | ||
return 'st'; | ||
} | ||
if (lastDigit === 2) { | ||
return 'nd'; | ||
} | ||
if (lastDigit === 3) { | ||
return 'rd'; | ||
} | ||
return 'th'; | ||
}; | ||
}; | ||
} | ||
/** ALIAS - fixFloat */ | ||
export const ff = MathsTools.fixFloat; |
@@ -9,116 +9,111 @@ import { OfType } from './types'; | ||
*/ | ||
export namespace ObjectTools { | ||
/**<!-- DOCS: ### --> | ||
* remodel | ||
* | ||
* - `ObjectTools.remodel` | ||
* | ||
* Apply a function to the entries of an object | ||
* | ||
* ```typescript | ||
* const input = {'foo': 2, 'bar': 1, 'baz': 4} | ||
* ObjectTools.remodel(input, (entries) => entries.filter(([k, v]) => v % 2 === 0)) // { foo: 2, baz: 4 } | ||
* ``` | ||
*/ | ||
export const remodel = <T extends Object = Object, V extends any = any, W extends any = any, O extends any = OfType<T, W>>( | ||
obj: T, | ||
func: (entries: [string, V][]) => [string, W][] | ||
): O => Object.fromEntries(func(Object.entries(obj)) ?? Object.entries(obj)) as O; | ||
/**<!-- DOCS: ### --> | ||
* remodel | ||
* | ||
* - `ObjectTools.remodel` | ||
* | ||
* Apply a function to the entries of an object | ||
* | ||
* ```typescript | ||
* const input = {'foo': 2, 'bar': 1, 'baz': 4} | ||
* ObjectTools.remodel(input, (entries) => entries.filter(([k, v]) => v % 2 === 0)) // { foo: 2, baz: 4 } | ||
* ``` | ||
*/ | ||
const remodel = <T extends Object = Object, V extends any = any, W extends any = any, O extends any = OfType<T, W>>( | ||
obj: T, | ||
func: (entries: [string, V][]) => [string, W][] | ||
): O => Object.fromEntries(func(Object.entries(obj)) ?? Object.entries(obj)) as O; | ||
/**<!-- DOCS: ### --> | ||
* remodelEach | ||
* | ||
* - `ObjectTools.remodelEach` | ||
* | ||
* Apply a function to each of the entries of an object | ||
* | ||
* Note: similar to ObjectTools.map, but the function parameters are different. Prefer ObjectTools.map where possible. | ||
* | ||
* ```typescript | ||
* const input = {'foo': 2, 'bar': 1, 'baz': 4} | ||
* ObjectTools.remodelEach(input, ([k, v]) => [k, v * 2]) // { foo: 4, bar: 2, baz: 8 } | ||
* ``` | ||
*/ | ||
export const remodelEach = <T extends Object = Object, V extends any = any, W extends any = any, O extends any = OfType<T, W>>( | ||
obj: T, | ||
func: (entry: [string, V], index: number, entries: [string, V][]) => [string, W] | ||
): O => Object.fromEntries(Object.entries(obj).map((entry, index, entries) => func(entry, index, entries) ?? entry)) as O; | ||
/**<!-- DOCS: ### --> | ||
* remodelEach | ||
* | ||
* - `ObjectTools.remodelEach` | ||
* | ||
* Apply a function to each of the entries of an object | ||
* | ||
* Note: similar to ObjectTools.map, but the function parameters are different. Prefer ObjectTools.map where possible. | ||
* | ||
* ```typescript | ||
* const input = {'foo': 2, 'bar': 1, 'baz': 4} | ||
* ObjectTools.remodelEach(input, ([k, v]) => [k, v * 2]) // { foo: 4, bar: 2, baz: 8 } | ||
* ``` | ||
*/ | ||
const remodelEach = <T extends Object = Object, V extends any = any, W extends any = any, O extends any = OfType<T, W>>( | ||
obj: T, | ||
func: (entry: [string, V], index: number, entries: [string, V][]) => [string, W] | ||
): O => Object.fromEntries(Object.entries(obj).map((entry, index, entries) => func(entry, index, entries) ?? entry)) as O; | ||
/**<!-- DOCS: ### --> | ||
* map | ||
* | ||
* - `ObjectTools.map` | ||
* | ||
* Maps the keys and values of an object in a similar way to Array.map | ||
* | ||
* ```typescript | ||
* ObjectTools.map({a: 1, b: 2, c: 3}, (key, value) => [key, key + value]); // {a: 'a1', b: 'b2', c: 'c3'} | ||
* ``` | ||
*/ | ||
export const map = <T extends Object, V extends any, W extends any>( | ||
obj: T, | ||
func: (key: string, value: V, index: number) => [string, W] | ||
): OfType<T, W> => remodel(obj, (entries) => entries.map(([key, value], index) => func(key, value, index))) as OfType<T, W>; | ||
/**<!-- DOCS: ### --> | ||
* map | ||
* | ||
* - `ObjectTools.map` | ||
* | ||
* Maps the keys and values of an object in a similar way to Array.map | ||
* | ||
* ```typescript | ||
* ObjectTools.map({a: 1, b: 2, c: 3}, (key, value) => [key, key + value]); // {a: 'a1', b: 'b2', c: 'c3'} | ||
* ``` | ||
*/ | ||
const map = <T extends Object, V extends any, W extends any>(obj: T, func: (key: string, value: V, index: number) => [string, W]): OfType<T, W> => | ||
remodel(obj, (entries) => entries.map(([key, value], index) => func(key, value, index))) as OfType<T, W>; | ||
/**<!-- DOCS: ### --> | ||
* mapValues | ||
* | ||
* - `ObjectTools.mapValues` | ||
* | ||
* Maps the values of an object in a similar way to Array.map | ||
* | ||
* ```typescript | ||
* ObjectTools.map({a: 1, b: 2, c: 3}, (key, value) => key.repeat(value)); // {a: 'a', b: 'bb', c: 'ccc'} | ||
* ``` | ||
*/ | ||
export const mapValues = <T extends Object, V extends any, W extends any>( | ||
obj: T, | ||
func: (key: string, value: V, index: number) => W | ||
): OfType<T, W> => remodel(obj, (entries) => entries.map(([key, value], index) => [key, func(key, value, index)])) as OfType<T, W>; | ||
/**<!-- DOCS: ### --> | ||
* mapValues | ||
* | ||
* - `ObjectTools.mapValues` | ||
* | ||
* Maps the values of an object in a similar way to Array.map | ||
* | ||
* ```typescript | ||
* ObjectTools.map({a: 1, b: 2, c: 3}, (key, value) => key.repeat(value)); // {a: 'a', b: 'bb', c: 'ccc'} | ||
* ``` | ||
*/ | ||
const mapValues = <T extends Object, V extends any, W extends any>(obj: T, func: (key: string, value: V, index: number) => W): OfType<T, W> => | ||
remodel(obj, (entries) => entries.map(([key, value], index) => [key, func(key, value, index)])) as OfType<T, W>; | ||
/**<!-- DOCS: ### --> | ||
* mapKeys | ||
* | ||
* - `ObjectTools.mapKeys` | ||
* | ||
* Maps the values of an object in a similar way to Array.map | ||
* | ||
* ```typescript | ||
* ObjectTools.map({a: 1, b: 2, c: 3}, (key, value) => key.repeat(value)); // {a: 1, bb: 2, ccc: 3} | ||
* ``` | ||
*/ | ||
export const mapKeys = <T extends Object, V extends any>(obj: T, func: (key: string, value: V, index: number) => string): T => | ||
remodel(obj, (entries) => entries.map(([key, value], index) => [func(key, value, index), value])) as T; | ||
/**<!-- DOCS: ### --> | ||
* mapKeys | ||
* | ||
* - `ObjectTools.mapKeys` | ||
* | ||
* Maps the values of an object in a similar way to Array.map | ||
* | ||
* ```typescript | ||
* ObjectTools.map({a: 1, b: 2, c: 3}, (key, value) => key.repeat(value)); // {a: 1, bb: 2, ccc: 3} | ||
* ``` | ||
*/ | ||
const mapKeys = <T extends Object, V extends any>(obj: T, func: (key: string, value: V, index: number) => string): T => | ||
remodel(obj, (entries) => entries.map(([key, value], index) => [func(key, value, index), value])) as T; | ||
/**<!-- DOCS: ### --> | ||
* filter | ||
* | ||
* - `ObjectTools.filter` | ||
* | ||
* Removes entries from an object based on a predicate function | ||
* | ||
* ```typescript | ||
* ObjectTools.filter({a: 1, b: 2, c: 3}, (k, v) => v % 2 === 0) // { b: 2 } | ||
* ``` | ||
*/ | ||
export const filter = <T extends Object, V extends any, O extends Partial<T>>(obj: T, func: (key: string, value: V, index: number) => boolean): O => | ||
remodel(obj, (entries) => entries.filter(([key, value], index) => func(key, value, index))) as O; | ||
/**<!-- DOCS: ### --> | ||
* filter | ||
* | ||
* - `ObjectTools.filter` | ||
* | ||
* Removes entries from an object based on a predicate function | ||
* | ||
* ```typescript | ||
* ObjectTools.filter({a: 1, b: 2, c: 3}, (k, v) => v % 2 === 0) // { b: 2 } | ||
* ``` | ||
*/ | ||
const filter = <T extends Object, V extends any, O extends Partial<T>>(obj: T, func: (key: string, value: V, index: number) => boolean): O => | ||
remodel(obj, (entries) => entries.filter(([key, value], index) => func(key, value, index))) as O; | ||
/**<!-- DOCS: ### --> | ||
* clean | ||
* | ||
* - `ObjectTools.clean` | ||
* | ||
* Removes properties with undefined values | ||
* | ||
* ```typescript | ||
* ObjectTools.clean({a: 1, b: undefined, c: 3}) // { a: 1, c: 3 } | ||
* ``` | ||
*/ | ||
const clean = <T extends Object, O extends Partial<T>>(obj: T): O => filter(obj, (key, value) => value !== undefined) as O; | ||
export const ObjectTools = { | ||
remodel, | ||
remodelEach, | ||
map, | ||
mapValues, | ||
mapKeys, | ||
filter, | ||
clean | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* clean | ||
* | ||
* - `ObjectTools.clean` | ||
* | ||
* Removes properties with undefined values | ||
* | ||
* ```typescript | ||
* ObjectTools.clean({a: 1, b: undefined, c: 3}) // { a: 1, c: 3 } | ||
* ``` | ||
*/ | ||
export const clean = <T extends Object, O extends Partial<T>>(obj: T): O => filter(obj, (key, value) => value !== undefined) as O; | ||
} |
@@ -1,310 +0,326 @@ | ||
import * as fn from './fn'; | ||
import { fn } from './fn'; | ||
//<!-- DOCS: 600 --> | ||
/**<!-- DOCS: ## --> | ||
* progressBar | ||
* | ||
* A progress bar that can be used in the terminal. | ||
* | ||
* > NOTE: This is eventually be moved to `swiss-node` | ||
*/ | ||
/**<!-- DOCS: ### --> | ||
* printLn | ||
* | ||
* - `printLn` | ||
* - `progressBar.printLn` | ||
* | ||
* Can use instead of console.log | ||
* | ||
* Overwrites the previous line if possible (i.e. node); | ||
* | ||
* Usage | ||
* ```javascript | ||
* import { printLn } from 'swiss-ak'; | ||
* | ||
* printLn('A'); | ||
* printLn('B'); // Replaces the 'A' line | ||
* printLn('C'); // Replaces the 'B' line | ||
* printLn(); // Jumps a line | ||
* printLn('D'); // Replaces the empty line | ||
* ``` | ||
* | ||
* Output | ||
* ``` | ||
* C | ||
* D | ||
* ``` | ||
*/ | ||
export const printLn = (...text: any[]) => { | ||
if (process?.stdout?.clearLine && process?.stdout?.cursorTo) { | ||
if (!text.length) { | ||
process.stdout.write('\n'); | ||
} else { | ||
const output = text.map((item) => item.toString()).join(' '); | ||
process.stdout.clearLine(0); | ||
process.stdout.cursorTo(0); | ||
process.stdout.moveCursor(0, -1); | ||
process.stdout.clearLine(0); | ||
process.stdout.write(output); | ||
process.stdout.write('\n'); | ||
} | ||
} else { | ||
console.log(...text); | ||
} | ||
}; | ||
const print = (text?: string, wrapperFn: any = fn.noact) => { | ||
const wrapped = wrapperFn(text || ''); | ||
printLn(wrapped); | ||
}; | ||
const getCharWidth = (num: number, max: number, width: number) => Math.round(width * (Math.max(0, Math.min(num / max, 1)) / 1)); | ||
const getBarString = (current: number, max: number, width: number, opts: ProgressBarOptionsFull) => { | ||
const { progChar, emptyChar, startChar, endChar, showCurrent, currentChar } = opts; | ||
const numProgChars = getCharWidth(current, max, width); | ||
const numNextChars = getCharWidth(current + 1, max, width); | ||
const numCurrentChars = showCurrent ? numNextChars - numProgChars : 0; | ||
const numEmptyChars = width - numProgChars - numCurrentChars; | ||
const prog = opts.barProgWrapFn(progChar.repeat(numProgChars)); | ||
const curr = opts.barCurrentWrapFn(currentChar.repeat(numCurrentChars)); | ||
const empt = opts.barEmptyWrapFn(emptyChar.repeat(numEmptyChars)); | ||
const body = opts.barWrapFn(`${prog}${curr}${empt}`); | ||
return `${startChar}${body}${endChar}`; | ||
}; | ||
const getSuffix = (current: number, maxNum: number, isMaxKnown: boolean, opts: ProgressBarOptionsFull) => { | ||
let items = ['']; | ||
if (opts.showCount) { | ||
const pad = Math.max(maxNum.toString().length, opts.countWidth); | ||
items.push(`[${current.toString().padStart(pad, ' ')} / ${(isMaxKnown ? maxNum.toString() : '?').padStart(pad, ' ')}]`); | ||
} | ||
if (opts.showPercent) { | ||
const percent = Math.round((current / Math.max(1, maxNum)) * 100); | ||
items.push(`(${percent.toString().padStart('100'.toString().length, ' ')}%)`); | ||
} | ||
const joined = items.filter((x) => x).join(' '); | ||
return joined.length ? ' ' + joined : ''; | ||
}; | ||
interface ProgressBarOptionsFull { | ||
prefix: string; | ||
prefixWidth: number; | ||
maxWidth: number; | ||
wrapperFn: any; | ||
barWrapFn: any; | ||
barProgWrapFn: any; | ||
barCurrentWrapFn: any; | ||
barEmptyWrapFn: any; | ||
showCount: boolean; | ||
showPercent: boolean; | ||
countWidth: number; | ||
progChar: string; | ||
emptyChar: string; | ||
startChar: string; | ||
endChar: string; | ||
showCurrent: boolean; | ||
currentChar: string; | ||
} | ||
/**<!-- DOCS: ### --> | ||
* Options | ||
* | ||
* - `ProgressBarOptions` | ||
* - `progressBar.ProgressBarOptions` | ||
* | ||
* All options are optional. | ||
* | ||
* | Property | Default | Description | | ||
* | ---------------- | --------------------------------- | ------------------------------------------------------ | | ||
* | prefix | `''` | String to show to left of progress bar | | ||
* | prefixWidth | `1` | Min width of prefix - `10` => `Example˽˽˽` | | ||
* | maxWidth | `process.stdout.columns` or `100` | The maximum width the entire string may extend | | ||
* | wrapperFn | nothing | function to wrap the printed string (eg `chalk.cyan)` | | ||
* | barWrapFn | nothing | function to wrap the bar | | ||
* | barProgWrapFn | nothing | function to wrap the 'complete' segment of the bar | | ||
* | barCurrentWrapFn | nothing | function to wrap the 'current' segment of the bar | | ||
* | barEmptyWrapFn | nothing | function to wrap the empty/track part of the line | | ||
* | showCount | `true` | Show numerical values of the count - `[11 / 15]` | | ||
* | showPercent | `false` | Show percentage completed - `( 69%)` | | ||
* | countWidth | `0` | Min width of nums for showCount - `3` => `[˽˽1 / ˽15]` | | ||
* | progChar | `'█'` | Character to use for progress section of bar | | ||
* | emptyChar | `' '` | Character to use for empty (rail) section of bar | | ||
* | startChar | `'▕'` | Character to start the progress bar with | | ||
* | endChar | `'▏'` | Character to end the progress bar with | | ||
* | showCurrent | `'▏'` | Show the 'current' segment of the bar seperately | | ||
* | currentChar | `'▏'` | Character to use the the 'current' segment | | ||
*/ | ||
export type ProgressBarOptions = Partial<ProgressBarOptionsFull>; | ||
const getFullOptions = (opts: ProgressBarOptions = {}): ProgressBarOptionsFull => ({ | ||
prefix: '', | ||
prefixWidth: 1, | ||
maxWidth: process?.stdout?.columns ? process.stdout.columns : 100, | ||
wrapperFn: fn.noact, | ||
barWrapFn: fn.noact, | ||
barProgWrapFn: fn.noact, | ||
barCurrentWrapFn: fn.noact, | ||
barEmptyWrapFn: fn.noact, | ||
showCount: true, | ||
showPercent: false, | ||
countWidth: 0, | ||
progChar: '█', | ||
emptyChar: ' ', | ||
startChar: '▕', | ||
endChar: '▏', | ||
showCurrent: false, | ||
currentChar: '▞', | ||
...opts | ||
}); | ||
export interface ProgressBar { | ||
/** ALIAS - ProgressBar.next */ | ||
next: () => string; | ||
/** ALIAS - ProgressBar.set */ | ||
set: (newCurrent: number) => string; | ||
/** ALIAS - ProgressBar.reset */ | ||
reset: () => string; | ||
/** ALIAS - ProgressBar.update */ | ||
update: () => string; | ||
/** ALIAS - ProgressBar.start */ | ||
start: () => string; | ||
/** ALIAS - ProgressBar.finish */ | ||
finish: () => string; | ||
/** ALIAS - ProgressBar.max */ | ||
readonly max: number; | ||
} | ||
/**<!-- DOCS: ### --> | ||
* getProgressBar | ||
/**<!-- DOCS: ## --> | ||
* progressBar | ||
* | ||
* - `getProgressBar` | ||
* - `progressBar.getProgressBar` | ||
* A progress bar that can be used in the terminal. | ||
* | ||
* Usage: | ||
* ```typescript | ||
* import chalk from 'chalk' | ||
* import {getProgressBar} from 'swiss-ak'; | ||
* | ||
* console.log('-'.repeat(20) + ' < 20 Chars'); | ||
* | ||
* const progress = getProgressBar(5, { | ||
* prefix: 'ABC', | ||
* maxWidth: 20, | ||
* chalk, | ||
* wrapperFn: chalk.green | ||
* }); | ||
* for (let i = 1; i <= 5; i++) { | ||
* progress.set(i); | ||
* } | ||
* progress.finish(); | ||
* ``` | ||
* | ||
* Output: | ||
* ``` | ||
* -------------------- < 20 Chars | ||
* ABC ▕ ▏ [0 / 5] | ||
* ABC ▕█ ▏ [1 / 5] | ||
* ABC ▕██ ▏ [2 / 5] | ||
* ABC ▕████ ▏ [3 / 5] | ||
* ABC ▕█████ ▏ [4 / 5] | ||
* ABC ▕██████▏ [5 / 5] | ||
* ``` | ||
* > NOTE: This is eventually be moved to `swiss-node` | ||
*/ | ||
export const getProgressBar = (max: number, options: ProgressBarOptions = {}): ProgressBar => { | ||
const opts = getFullOptions(options); | ||
const { prefix, prefixWidth, maxWidth, wrapperFn, startChar, endChar } = opts; | ||
let current = 0; | ||
let finished = false; | ||
const maxNum = typeof max === 'number' ? max : 1; | ||
const isMaxKnown = typeof max === 'number'; | ||
/**<!-- DOCS: #### --> | ||
* update | ||
export namespace progressBar { | ||
/**<!-- DOCS: ### --> | ||
* printLn | ||
* | ||
* - `getProgressBar().update` | ||
* - `printLn` | ||
* - `progressBar.printLn` | ||
* | ||
* Trigger the progress bar to update/rerender | ||
* Can use instead of console.log | ||
* | ||
* Overwrites the previous line if possible (i.e. node); | ||
* | ||
* Usage | ||
* ```javascript | ||
* import { printLn } from 'swiss-ak'; | ||
* | ||
* printLn('A'); | ||
* printLn('B'); // Replaces the 'A' line | ||
* printLn('C'); // Replaces the 'B' line | ||
* printLn(); // Jumps a line | ||
* printLn('D'); // Replaces the empty line | ||
* ``` | ||
* | ||
* Output | ||
* ``` | ||
* C | ||
* D | ||
* ``` | ||
*/ | ||
const update = () => { | ||
const suffix = getSuffix(current, maxNum, isMaxKnown, opts); | ||
const fullPrefix = prefix.padEnd(prefixWidth); | ||
const output = `${fullPrefix}${getBarString( | ||
current, | ||
Math.max(1, maxNum), | ||
Math.max(0, maxWidth - [fullPrefix, suffix, startChar, endChar].join('').length), | ||
opts | ||
)}${suffix}`; | ||
export const printLn = (...text: any[]) => { | ||
if (process?.stdout?.clearLine && process?.stdout?.cursorTo) { | ||
if (!text.length) { | ||
process.stdout.write('\n'); | ||
} else { | ||
const output = text.map((item) => item.toString()).join(' '); | ||
process.stdout.clearLine(0); | ||
process.stdout.cursorTo(0); | ||
process.stdout.moveCursor(0, -1); | ||
process.stdout.clearLine(0); | ||
process.stdout.write(output); | ||
process.stdout.write('\n'); | ||
} | ||
} else { | ||
console.log(...text); | ||
} | ||
}; | ||
print(output, wrapperFn); | ||
return output; | ||
const print = (text?: string, wrapperFn: any = fn.noact) => { | ||
const wrapped = wrapperFn(text || ''); | ||
printLn(wrapped); | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* next | ||
* | ||
* - `getProgressBar().next` | ||
* | ||
* Set the progress bar to the next value | ||
*/ | ||
const next = (): string => { | ||
if (finished) return ''; | ||
current++; | ||
return update(); | ||
const getCharWidth = (num: number, max: number, width: number) => Math.round(width * (Math.max(0, Math.min(num / max, 1)) / 1)); | ||
const getBarString = (current: number, max: number, width: number, opts: ProgressBarOptionsFull) => { | ||
const { progChar, emptyChar, startChar, endChar, showCurrent, currentChar } = opts; | ||
const numProgChars = getCharWidth(current, max, width); | ||
const numNextChars = getCharWidth(current + 1, max, width); | ||
const numCurrentChars = showCurrent ? numNextChars - numProgChars : 0; | ||
const numEmptyChars = width - numProgChars - numCurrentChars; | ||
const prog = opts.barProgWrapFn(progChar.repeat(numProgChars)); | ||
const curr = opts.barCurrentWrapFn(currentChar.repeat(numCurrentChars)); | ||
const empt = opts.barEmptyWrapFn(emptyChar.repeat(numEmptyChars)); | ||
const body = opts.barWrapFn(`${prog}${curr}${empt}`); | ||
return `${startChar}${body}${endChar}`; | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* set | ||
* | ||
* - `getProgressBar().set` | ||
* | ||
* Set the progress bar to a specific value | ||
*/ | ||
const set = (newCurrent: number): string => { | ||
if (finished) return ''; | ||
current = newCurrent; | ||
return update(); | ||
const getSuffix = (current: number, maxNum: number, isMaxKnown: boolean, opts: ProgressBarOptionsFull) => { | ||
let items = ['']; | ||
if (opts.showCount) { | ||
const pad = Math.max(maxNum.toString().length, opts.countWidth); | ||
items.push(`[${current.toString().padStart(pad, ' ')} / ${(isMaxKnown ? maxNum.toString() : '?').padStart(pad, ' ')}]`); | ||
} | ||
if (opts.showPercent) { | ||
const percent = Math.round((current / Math.max(1, maxNum)) * 100); | ||
items.push(`(${percent.toString().padStart('100'.toString().length, ' ')}%)`); | ||
} | ||
const joined = items.filter((x) => x).join(' '); | ||
return joined.length ? ' ' + joined : ''; | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* reset | ||
interface ProgressBarOptionsFull { | ||
prefix: string; | ||
prefixWidth: number; | ||
maxWidth: number; | ||
wrapperFn: any; | ||
barWrapFn: any; | ||
barProgWrapFn: any; | ||
barCurrentWrapFn: any; | ||
barEmptyWrapFn: any; | ||
showCount: boolean; | ||
showPercent: boolean; | ||
countWidth: number; | ||
progChar: string; | ||
emptyChar: string; | ||
startChar: string; | ||
endChar: string; | ||
showCurrent: boolean; | ||
currentChar: string; | ||
} | ||
/**<!-- DOCS: ### --> | ||
* Options | ||
* | ||
* - `getProgressBar().reset` | ||
* - `ProgressBarOptions` | ||
* - `progressBar.ProgressBarOptions` | ||
* | ||
* Set the progress bar to 0 | ||
* All options are optional. | ||
* | ||
* | Property | Default | Description | | ||
* | ---------------- | --------------------------------- | ------------------------------------------------------ | | ||
* | prefix | `''` | String to show to left of progress bar | | ||
* | prefixWidth | `1` | Min width of prefix - `10` => `Example˽˽˽` | | ||
* | maxWidth | `process.stdout.columns` or `100` | The maximum width the entire string may extend | | ||
* | wrapperFn | nothing | function to wrap the printed string (eg `chalk.cyan)` | | ||
* | barWrapFn | nothing | function to wrap the bar | | ||
* | barProgWrapFn | nothing | function to wrap the 'complete' segment of the bar | | ||
* | barCurrentWrapFn | nothing | function to wrap the 'current' segment of the bar | | ||
* | barEmptyWrapFn | nothing | function to wrap the empty/track part of the line | | ||
* | showCount | `true` | Show numerical values of the count - `[11 / 15]` | | ||
* | showPercent | `false` | Show percentage completed - `( 69%)` | | ||
* | countWidth | `0` | Min width of nums for showCount - `3` => `[˽˽1 / ˽15]` | | ||
* | progChar | `'█'` | Character to use for progress section of bar | | ||
* | emptyChar | `' '` | Character to use for empty (rail) section of bar | | ||
* | startChar | `'▕'` | Character to start the progress bar with | | ||
* | endChar | `'▏'` | Character to end the progress bar with | | ||
* | showCurrent | `'▏'` | Show the 'current' segment of the bar seperately | | ||
* | currentChar | `'▏'` | Character to use the the 'current' segment | | ||
*/ | ||
const reset = (): string => { | ||
return set(0); | ||
}; | ||
export type ProgressBarOptions = Partial<ProgressBarOptionsFull>; | ||
const getFullOptions = (opts: ProgressBarOptions = {}): ProgressBarOptionsFull => ({ | ||
prefix: '', | ||
prefixWidth: 1, | ||
maxWidth: process?.stdout?.columns ? process.stdout.columns : 100, | ||
wrapperFn: fn.noact, | ||
barWrapFn: fn.noact, | ||
barProgWrapFn: fn.noact, | ||
barCurrentWrapFn: fn.noact, | ||
barEmptyWrapFn: fn.noact, | ||
showCount: true, | ||
showPercent: false, | ||
countWidth: 0, | ||
progChar: '█', | ||
emptyChar: ' ', | ||
startChar: '▕', | ||
endChar: '▏', | ||
showCurrent: false, | ||
currentChar: '▞', | ||
...opts | ||
}); | ||
/**<!-- DOCS: #### --> | ||
* start | ||
/**<!-- DOCS: ### --> | ||
* getProgressBar | ||
* | ||
* - `getProgressBar().start` | ||
* - `getProgressBar` | ||
* - `progressBar.getProgressBar` | ||
* | ||
* Start displaying the progress bar | ||
*/ | ||
const start = (): string => { | ||
printLn(); // blank/new line | ||
return update(); | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* finish | ||
* Usage: | ||
* ```typescript | ||
* import chalk from 'chalk' | ||
* import {getProgressBar} from 'swiss-ak'; | ||
* | ||
* - `getProgressBar().finish` | ||
* console.log('-'.repeat(20) + ' < 20 Chars'); | ||
* | ||
* Stop displaying the progress bar | ||
* const progress = getProgressBar(5, { | ||
* prefix: 'ABC', | ||
* maxWidth: 20, | ||
* chalk, | ||
* wrapperFn: chalk.green | ||
* }); | ||
* for (let i = 1; i <= 5; i++) { | ||
* progress.set(i); | ||
* } | ||
* progress.finish(); | ||
* ``` | ||
* | ||
* Output: | ||
* ``` | ||
* -------------------- < 20 Chars | ||
* ABC ▕ ▏ [0 / 5] | ||
* ABC ▕█ ▏ [1 / 5] | ||
* ABC ▕██ ▏ [2 / 5] | ||
* ABC ▕████ ▏ [3 / 5] | ||
* ABC ▕█████ ▏ [4 / 5] | ||
* ABC ▕██████▏ [5 / 5] | ||
* ``` | ||
*/ | ||
const finish = (): string => { | ||
finished = true; | ||
const output = update(); | ||
printLn(); // blank/new line | ||
return output; | ||
export const getProgressBar = (max: number, options: ProgressBarOptions = {}): ProgressBar => { | ||
const opts = getFullOptions(options); | ||
const { prefix, prefixWidth, maxWidth, wrapperFn, startChar, endChar } = opts; | ||
let current = 0; | ||
let finished = false; | ||
const maxNum = typeof max === 'number' ? max : 1; | ||
const isMaxKnown = typeof max === 'number'; | ||
/**<!-- DOCS: #### --> | ||
* update | ||
* | ||
* - `getProgressBar().update` | ||
* | ||
* Trigger the progress bar to update/rerender | ||
*/ | ||
const update = () => { | ||
const suffix = getSuffix(current, maxNum, isMaxKnown, opts); | ||
const fullPrefix = prefix.padEnd(prefixWidth); | ||
const output = `${fullPrefix}${getBarString( | ||
current, | ||
Math.max(1, maxNum), | ||
Math.max(0, maxWidth - [fullPrefix, suffix, startChar, endChar].join('').length), | ||
opts | ||
)}${suffix}`; | ||
print(output, wrapperFn); | ||
return output; | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* next | ||
* | ||
* - `getProgressBar().next` | ||
* | ||
* Set the progress bar to the next value | ||
*/ | ||
const next = (): string => { | ||
if (finished) return ''; | ||
current++; | ||
return update(); | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* set | ||
* | ||
* - `getProgressBar().set` | ||
* | ||
* Set the progress bar to a specific value | ||
*/ | ||
const set = (newCurrent: number): string => { | ||
if (finished) return ''; | ||
current = newCurrent; | ||
return update(); | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* reset | ||
* | ||
* - `getProgressBar().reset` | ||
* | ||
* Set the progress bar to 0 | ||
*/ | ||
const reset = (): string => { | ||
return set(0); | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* start | ||
* | ||
* - `getProgressBar().start` | ||
* | ||
* Start displaying the progress bar | ||
*/ | ||
const start = (): string => { | ||
printLn(); // blank/new line | ||
return update(); | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* finish | ||
* | ||
* - `getProgressBar().finish` | ||
* | ||
* Stop displaying the progress bar | ||
*/ | ||
const finish = (): string => { | ||
finished = true; | ||
const output = update(); | ||
printLn(); // blank/new line | ||
return output; | ||
}; | ||
return { | ||
next, | ||
set, | ||
reset, | ||
update, | ||
start, | ||
finish, | ||
max | ||
}; | ||
}; | ||
} | ||
return { | ||
next, | ||
set, | ||
reset, | ||
update, | ||
start, | ||
finish, | ||
max | ||
}; | ||
}; | ||
/** ALIAS - ProgressBarOptions */ | ||
export type ProgressBarOptions = progressBar.ProgressBarOptions; | ||
/** ALIAS - printLn */ | ||
export const printLn = progressBar.printLn; | ||
/** ALIAS - getProgressBar */ | ||
export const getProgressBar = progressBar.getProgressBar; |
@@ -7,9 +7,17 @@ //<!-- DOCS: 140 --> | ||
*/ | ||
export interface DeferredPromise<T> { | ||
resolve: (value: T) => Promise<T>; | ||
reject: (value: T) => Promise<T>; | ||
promise: Promise<T>; | ||
} | ||
/**<!-- DOCS: ### --> | ||
export namespace PromiseTools { | ||
/**<!-- DOCS: 141 ### --> | ||
* DeferredPromise | ||
* | ||
* - `DeferredPromise` | ||
* - `PromiseTools.DeferredPromise` | ||
* | ||
* A deferred promise | ||
*/ | ||
export interface DeferredPromise<T> { | ||
resolve: (value: T) => Promise<T>; | ||
reject: (value: T) => Promise<T>; | ||
promise: Promise<T>; | ||
} | ||
/**<!-- DOCS: ### --> | ||
* getDeferred | ||
@@ -40,354 +48,364 @@ * | ||
*/ | ||
export const getDeferred = <T extends unknown>(): DeferredPromise<T> => { | ||
let resolve, reject; | ||
const promise = new Promise<T>((res, rej) => { | ||
resolve = (arg) => { | ||
res(arg); | ||
return promise; | ||
export const getDeferred = <T extends unknown>(): DeferredPromise<T> => { | ||
let resolve, reject; | ||
const promise = new Promise<T>((res, rej) => { | ||
resolve = (arg) => { | ||
res(arg); | ||
return promise; | ||
}; | ||
reject = (...args) => { | ||
rej(...args); | ||
return promise; | ||
}; | ||
}); | ||
return { | ||
resolve, | ||
reject, | ||
promise | ||
}; | ||
reject = (...args) => { | ||
rej(...args); | ||
return promise; | ||
}; | ||
}); | ||
return { | ||
resolve, | ||
reject, | ||
promise | ||
}; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* all | ||
* | ||
* - `all` | ||
* - `PromiseTools.all` | ||
* | ||
* An alias for Promise.all | ||
*/ | ||
export const all = async <T extends unknown>(promises: Promise<T>[]): Promise<any> => { | ||
await Promise.all(promises); | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* all | ||
* | ||
* - `all` | ||
* - `PromiseTools.all` | ||
* | ||
* An alias for Promise.all | ||
*/ | ||
export const all = async <T extends unknown>(promises: Promise<T>[]): Promise<any> => { | ||
await Promise.all(promises); | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* allLimit | ||
* | ||
* - `allLimit` | ||
* - `PromiseTools.allLimit` | ||
* | ||
* Like Promise.all, but limits the numbers of concurrently running items. | ||
* | ||
* Takes an array of functions (that return Promises), rather than an array of Promises | ||
* | ||
* ```typescript | ||
* import { PromiseTools, timer, ms, seconds } from 'swiss-ak'; | ||
* | ||
* const give = async (delay: ms, result: number, label: string) => { | ||
* await waitFor(delay); | ||
* timer.end(label); | ||
* return result; | ||
* }; | ||
* | ||
* timer.start('allLimit', 'a', 'b', 'c', 'd'); | ||
* | ||
* const results = PromiseTools.allLimit<number>(2, [ | ||
* give(seconds(5), 1, 'a'), | ||
* give(seconds(5), 2, 'b'), | ||
* give(seconds(5), 3, 'c'), | ||
* give(seconds(5), 4, 'd') | ||
* ]); | ||
* | ||
* timer.end('allLimit'); | ||
* | ||
* console.log(results); // [ 1, 2, 3, 4 ] | ||
* | ||
* timer.log(); | ||
* // Times: | ||
* // allLimit: 10s | ||
* // a: 5s | ||
* // b: 5s | ||
* // c: 10s | ||
* // d: 10s | ||
* ``` | ||
*/ | ||
export const allLimit = <T extends unknown>(limit: number, items: ((index: number) => Promise<T>)[], noThrow: boolean = false): Promise<T[]> => { | ||
let runningCount: number = 0; | ||
let errors: any[] = []; | ||
let remaining: ((index: number) => Promise<T>)[] = [...items]; | ||
const result: T[] = []; | ||
const deferred = getDeferred<T[]>(); | ||
/**<!-- DOCS: ### --> | ||
* allLimit | ||
* | ||
* - `allLimit` | ||
* - `PromiseTools.allLimit` | ||
* | ||
* Like Promise.all, but limits the numbers of concurrently running items. | ||
* | ||
* Takes an array of functions (that return Promises), rather than an array of Promises | ||
* | ||
* ```typescript | ||
* import { PromiseTools, timer, ms, seconds } from 'swiss-ak'; | ||
* | ||
* const give = async (delay: ms, result: number, label: string) => { | ||
* await waitFor(delay); | ||
* timer.end(label); | ||
* return result; | ||
* }; | ||
* | ||
* timer.start('allLimit', 'a', 'b', 'c', 'd'); | ||
* | ||
* const results = PromiseTools.allLimit<number>(2, [ | ||
* give(seconds(5), 1, 'a'), | ||
* give(seconds(5), 2, 'b'), | ||
* give(seconds(5), 3, 'c'), | ||
* give(seconds(5), 4, 'd') | ||
* ]); | ||
* | ||
* timer.end('allLimit'); | ||
* | ||
* console.log(results); // [ 1, 2, 3, 4 ] | ||
* | ||
* timer.log(); | ||
* // Times: | ||
* // allLimit: 10s | ||
* // a: 5s | ||
* // b: 5s | ||
* // c: 10s | ||
* // d: 10s | ||
* ``` | ||
*/ | ||
export const allLimit = <T extends unknown>(limit: number, items: ((index: number) => Promise<T>)[], noThrow: boolean = false): Promise<T[]> => { | ||
let runningCount: number = 0; | ||
let errors: any[] = []; | ||
let remaining: ((index: number) => Promise<T>)[] = [...items]; | ||
const result: T[] = []; | ||
const deferred = getDeferred<T[]>(); | ||
const update = () => { | ||
if (remaining.length === 0 && runningCount === 0) { | ||
if (errors.length && !noThrow) { | ||
deferred.reject(errors); | ||
const update = () => { | ||
if (remaining.length === 0 && runningCount === 0) { | ||
if (errors.length && !noThrow) { | ||
deferred.reject(errors); | ||
return; | ||
} | ||
deferred.resolve(result); | ||
return; | ||
} | ||
if (runningCount < limit && remaining.length) { | ||
const next = remaining.shift() as (index: number) => Promise<T>; | ||
const index = items.indexOf(next); | ||
run(next, index); | ||
} | ||
}; | ||
const run = async (prom: (index: number) => Promise<T>, index: number) => { | ||
runningCount++; | ||
try { | ||
result[index] = await prom(index); | ||
} catch (err) { | ||
errors.push(err); | ||
} | ||
runningCount--; | ||
update(); | ||
}; | ||
for (let i = 0; i < Math.min(limit, items.length); i++) { | ||
update(); | ||
} | ||
if (!items || items.length === 0) { | ||
deferred.resolve(result); | ||
return; | ||
} | ||
if (runningCount < limit && remaining.length) { | ||
const next = remaining.shift() as (index: number) => Promise<T>; | ||
const index = items.indexOf(next); | ||
run(next, index); | ||
} | ||
return deferred.promise; | ||
}; | ||
const run = async (prom: (index: number) => Promise<T>, index: number) => { | ||
runningCount++; | ||
try { | ||
result[index] = await prom(index); | ||
} catch (err) { | ||
errors.push(err); | ||
} | ||
runningCount--; | ||
update(); | ||
/**<!-- DOCS: ### --> | ||
* each | ||
* | ||
* - `each` | ||
* - `PromiseTools.each` | ||
* | ||
* Run an async function against each item in an array | ||
* | ||
* ```typescript | ||
* import { PromiseTools, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* await PromiseTools.each<number>(arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* sendToSomewhere(val); | ||
* }); | ||
* console.log(''); // after 2 seconds | ||
* ``` | ||
*/ | ||
export const each = async <Ti extends unknown>(items: Ti[], func: (item: Ti, index: number, array: Ti[]) => Promise<any>): Promise<any> => { | ||
await Promise.all(items.map((item: Ti, index: number, array: Ti[]) => func(item, index, array))); | ||
}; | ||
for (let i = 0; i < Math.min(limit, items.length); i++) { | ||
update(); | ||
} | ||
/**<!-- DOCS: ### --> | ||
* eachLimit | ||
* | ||
* - `eachLimit` | ||
* - `PromiseTools.eachLimit` | ||
* | ||
* Run an async function against each item in an array, limiting the number of items that can run concurrently. | ||
* | ||
* See PromiseTools.allLimit for information about limited functions. | ||
* | ||
* ```typescript | ||
* import { PromiseTools, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* await PromiseTools.eachLimit<number>(2, arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* sendToSomewhere(val); | ||
* }); | ||
* console.log(''); // after 4 seconds | ||
* ``` | ||
*/ | ||
export const eachLimit = async <Ti extends unknown>( | ||
limit: number, | ||
items: Ti[], | ||
func: (item: Ti, index: number, array: Ti[]) => Promise<any> | ||
): Promise<any> => { | ||
await allLimit( | ||
limit, | ||
items.map((item: Ti, index: number, array: Ti[]) => () => func(item, index, array)) | ||
); | ||
}; | ||
if (!items || items.length === 0) { | ||
deferred.resolve(result); | ||
} | ||
/**<!-- DOCS: ### --> | ||
* map | ||
* | ||
* - `map` | ||
* - `PromiseTools.map` | ||
* | ||
* Run an async map function against each item in an array, mapping the results to a returned array | ||
* | ||
* ```typescript | ||
* import { PromiseTools, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* const mapped = await PromiseTools.map<number>(arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* return val * 2; | ||
* }); | ||
* | ||
* console.log(mapped); // [2, 4, 6, 8] (after 2 seconds) | ||
* ``` | ||
*/ | ||
export const map = async <Ti extends unknown, To extends unknown>( | ||
items: Ti[], | ||
func: (item: Ti, index: number, array: Ti[]) => Promise<To> | ||
): Promise<To[]> => { | ||
const result: To[] = []; | ||
return deferred.promise; | ||
}; | ||
await Promise.all( | ||
items.map(async (item: Ti, index: number, array: Ti[]) => { | ||
const res = await func(item, index, array); | ||
result[index] = res; | ||
}) | ||
); | ||
/**<!-- DOCS: ### --> | ||
* each | ||
* | ||
* - `each` | ||
* - `PromiseTools.each` | ||
* | ||
* Run an async function against each item in an array | ||
* | ||
* ```typescript | ||
* import { PromiseTools, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* await PromiseTools.each<number>(arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* sendToSomewhere(val); | ||
* }); | ||
* console.log(''); // after 2 seconds | ||
* ``` | ||
*/ | ||
export const each = async <Ti extends unknown>(items: Ti[], func: (item: Ti, index: number, array: Ti[]) => Promise<any>): Promise<any> => { | ||
await Promise.all(items.map((item: Ti, index: number, array: Ti[]) => func(item, index, array))); | ||
}; | ||
return result; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* eachLimit | ||
* | ||
* - `eachLimit` | ||
* - `PromiseTools.eachLimit` | ||
* | ||
* Run an async function against each item in an array, limiting the number of items that can run concurrently. | ||
* | ||
* See PromiseTools.allLimit for information about limited functions. | ||
* | ||
* ```typescript | ||
* import { PromiseTools, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* await PromiseTools.eachLimit<number>(2, arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* sendToSomewhere(val); | ||
* }); | ||
* console.log(''); // after 4 seconds | ||
* ``` | ||
*/ | ||
export const eachLimit = async <Ti extends unknown>( | ||
limit: number, | ||
items: Ti[], | ||
func: (item: Ti, index: number, array: Ti[]) => Promise<any> | ||
): Promise<any> => { | ||
await allLimit( | ||
limit, | ||
items.map((item: Ti, index: number, array: Ti[]) => () => func(item, index, array)) | ||
); | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* mapLimit | ||
* | ||
* - `mapLimit` | ||
* - `PromiseTools.mapLimit` | ||
* | ||
* Run an async map function against each item in an array, mapping the results to a returned array, and limiting the number of items that can run concurrently. | ||
* | ||
* See PromiseTools.allLimit for information about limited functions. | ||
* | ||
* ```typescript | ||
* import { PromiseTools, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* const mapped = await PromiseTools.mapLimit<number>(2, arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* return val * 2; | ||
* }); | ||
* | ||
* console.log(mapped); // [2, 4, 6, 8] (after 4 seconds) | ||
* ``` | ||
*/ | ||
export const mapLimit = async <Ti extends unknown, To extends unknown>( | ||
limit: number, | ||
items: Ti[], | ||
func: (item: Ti, index: number, array: Ti[]) => Promise<To> | ||
): Promise<To[]> => | ||
await allLimit( | ||
limit, | ||
items.map((item: Ti, index: number, array: Ti[]) => () => { | ||
const res = func(item, index, array); | ||
return res; | ||
}) | ||
); | ||
/**<!-- DOCS: ### --> | ||
* map | ||
* | ||
* - `map` | ||
* - `PromiseTools.map` | ||
* | ||
* Run an async map function against each item in an array, mapping the results to a returned array | ||
* | ||
* ```typescript | ||
* import { PromiseTools, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* const mapped = await PromiseTools.map<number>(arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* return val * 2; | ||
* }); | ||
* | ||
* console.log(mapped); // [2, 4, 6, 8] (after 2 seconds) | ||
* ``` | ||
*/ | ||
export const map = async <Ti extends unknown, To extends unknown>( | ||
items: Ti[], | ||
func: (item: Ti, index: number, array: Ti[]) => Promise<To> | ||
): Promise<To[]> => { | ||
const result: To[] = []; | ||
const objectify = async <T extends Object>(func: Function, input: T): Promise<UnWrapPromiseObject<T>> => { | ||
const keys = Object.keys(input); | ||
const results = await func(Object.values(input)); | ||
return Object.fromEntries(keys.map((key, index) => [key, results[index]])) as UnWrapPromiseObject<T>; | ||
}; | ||
await Promise.all( | ||
items.map(async (item: Ti, index: number, array: Ti[]) => { | ||
const res = await func(item, index, array); | ||
result[index] = res; | ||
}) | ||
); | ||
type UnWrapPromise<T> = T extends Promise<infer U> ? U : T; | ||
type UnWrapPromiseObject<T> = { [K in keyof T]: UnWrapPromise<T[K]> }; | ||
/**<!-- DOCS: ### --> | ||
* allObj | ||
* | ||
* - `allObj` | ||
* - `PromiseTools.allObj` | ||
* | ||
* Like Promise.all, but pass/receive objects rather than arrays | ||
* | ||
* ```typescript | ||
* import { PromiseTools, timer, ms, seconds } from 'swiss-ak'; | ||
* | ||
* const give = async (delay: ms, result: number, label: string) => { | ||
* await waitFor(delay); | ||
* timer.end(label); | ||
* return result; | ||
* }; | ||
* | ||
* timer.start('allObj', 'a', 'b', 'c'); | ||
* | ||
* const results = PromiseTools.allObj<number>({ | ||
* a: give(seconds(10), 1, 'a'), | ||
* b: give(seconds(15), 2, 'b'), | ||
* c: give(seconds(20), 3, 'c') | ||
* }); | ||
* | ||
* timer.end('allObj'); | ||
* | ||
* console.log(results); // { a: 1, b: 2, c: 3 } | ||
* | ||
* timer.log(); | ||
* // Times: | ||
* // allObj: 20s | ||
* // a: 10s | ||
* // b: 15s | ||
* // c: 20s | ||
* ``` | ||
*/ | ||
export const allObj = async <T extends Object>(input: T): Promise<UnWrapPromiseObject<T>> => { | ||
return objectify((arr) => Promise.all(arr), input); | ||
}; | ||
return result; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* allLimitObj | ||
* | ||
* - `allLimitObj` | ||
* - `PromiseTools.allLimitObj` | ||
* | ||
* A mix of allObj and allLimit. | ||
* | ||
* Takes an array of functions (that return Promises), and limits the numbers of concurrently running items. | ||
* | ||
* ```typescript | ||
* import { PromiseTools, timer, ms, seconds } from 'swiss-ak'; | ||
* | ||
* const give = async (delay: ms, result: number, label: string) => { | ||
* await waitFor(delay); | ||
* timer.end(label); | ||
* return result; | ||
* }; | ||
* | ||
* timer.start('allLimitObj', 'a', 'b', 'c', 'd'); | ||
* | ||
* const results = PromiseTools.allLimitObj<number>(2, { | ||
* a: give(seconds(5), 1, 'a'), | ||
* b: give(seconds(5), 2, 'b'), | ||
* c: give(seconds(5), 3, 'c'), | ||
* d: give(seconds(5), 4, 'd') | ||
* }); | ||
* | ||
* timer.end('allLimitObj'); | ||
* | ||
* console.log(results); // { a: 1, b: 2, c: 3, d: 4 } | ||
* | ||
* timer.log(); | ||
* // Times: | ||
* // allLimitObj: 10s | ||
* // a: 5s | ||
* // b: 5s | ||
* // c: 10s | ||
* // d: 10s | ||
* ``` | ||
*/ | ||
export const allLimitObj = async <T extends Object>(limit: number, input: T, noThrow: boolean = false): Promise<UnWrapPromiseObject<T>> => { | ||
return objectify((items: ((index: number) => Promise<T>)[]) => { | ||
return allLimit(limit, items, noThrow); | ||
}, input); | ||
}; | ||
} | ||
/**<!-- DOCS: ### --> | ||
* mapLimit | ||
* | ||
* - `mapLimit` | ||
* - `PromiseTools.mapLimit` | ||
* | ||
* Run an async map function against each item in an array, mapping the results to a returned array, and limiting the number of items that can run concurrently. | ||
* | ||
* See PromiseTools.allLimit for information about limited functions. | ||
* | ||
* ```typescript | ||
* import { PromiseTools, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* const mapped = await PromiseTools.mapLimit<number>(2, arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* return val * 2; | ||
* }); | ||
* | ||
* console.log(mapped); // [2, 4, 6, 8] (after 4 seconds) | ||
* ``` | ||
*/ | ||
export const mapLimit = async <Ti extends unknown, To extends unknown>( | ||
limit: number, | ||
items: Ti[], | ||
func: (item: Ti, index: number, array: Ti[]) => Promise<To> | ||
): Promise<To[]> => | ||
await allLimit( | ||
limit, | ||
items.map((item: Ti, index: number, array: Ti[]) => () => { | ||
const res = func(item, index, array); | ||
return res; | ||
}) | ||
); | ||
const objectify = async <T extends Object>(func: Function, input: T): Promise<UnWrapPromiseObject<T>> => { | ||
const keys = Object.keys(input); | ||
const results = await func(Object.values(input)); | ||
return Object.fromEntries(keys.map((key, index) => [key, results[index]])) as UnWrapPromiseObject<T>; | ||
}; | ||
type UnWrapPromise<T> = T extends Promise<infer U> ? U : T; | ||
type UnWrapPromiseObject<T> = { [K in keyof T]: UnWrapPromise<T[K]> }; | ||
/**<!-- DOCS: ### --> | ||
* allObj | ||
* | ||
* - `allObj` | ||
* - `PromiseTools.allObj` | ||
* | ||
* Like Promise.all, but pass/receive objects rather than arrays | ||
* | ||
* ```typescript | ||
* import { PromiseTools, timer, ms, seconds } from 'swiss-ak'; | ||
* | ||
* const give = async (delay: ms, result: number, label: string) => { | ||
* await waitFor(delay); | ||
* timer.end(label); | ||
* return result; | ||
* }; | ||
* | ||
* timer.start('allObj', 'a', 'b', 'c'); | ||
* | ||
* const results = PromiseTools.allObj<number>({ | ||
* a: give(seconds(10), 1, 'a'), | ||
* b: give(seconds(15), 2, 'b'), | ||
* c: give(seconds(20), 3, 'c') | ||
* }); | ||
* | ||
* timer.end('allObj'); | ||
* | ||
* console.log(results); // { a: 1, b: 2, c: 3 } | ||
* | ||
* timer.log(); | ||
* // Times: | ||
* // allObj: 20s | ||
* // a: 10s | ||
* // b: 15s | ||
* // c: 20s | ||
* ``` | ||
*/ | ||
export const allObj = async <T extends Object>(input: T): Promise<UnWrapPromiseObject<T>> => { | ||
return objectify((arr) => Promise.all(arr), input); | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* allLimitObj | ||
* | ||
* - `allLimitObj` | ||
* - `PromiseTools.allLimitObj` | ||
* | ||
* A mix of allObj and allLimit. | ||
* | ||
* Takes an array of functions (that return Promises), and limits the numbers of concurrently running items. | ||
* | ||
* ```typescript | ||
* import { PromiseTools, timer, ms, seconds } from 'swiss-ak'; | ||
* | ||
* const give = async (delay: ms, result: number, label: string) => { | ||
* await waitFor(delay); | ||
* timer.end(label); | ||
* return result; | ||
* }; | ||
* | ||
* timer.start('allLimitObj', 'a', 'b', 'c', 'd'); | ||
* | ||
* const results = PromiseTools.allLimitObj<number>(2, { | ||
* a: give(seconds(5), 1, 'a'), | ||
* b: give(seconds(5), 2, 'b'), | ||
* c: give(seconds(5), 3, 'c'), | ||
* d: give(seconds(5), 4, 'd') | ||
* }); | ||
* | ||
* timer.end('allLimitObj'); | ||
* | ||
* console.log(results); // { a: 1, b: 2, c: 3, d: 4 } | ||
* | ||
* timer.log(); | ||
* // Times: | ||
* // allLimitObj: 10s | ||
* // a: 5s | ||
* // b: 5s | ||
* // c: 10s | ||
* // d: 10s | ||
* ``` | ||
*/ | ||
export const allLimitObj = async <T extends Object>(limit: number, input: T, noThrow: boolean = false): Promise<UnWrapPromiseObject<T>> => { | ||
return objectify((items: ((index: number) => Promise<T>)[]) => { | ||
return allLimit(limit, items, noThrow); | ||
}, input); | ||
}; | ||
export const PromiseTools = { | ||
getDeferred, | ||
all, | ||
allLimit, | ||
each, | ||
eachLimit, | ||
map, | ||
mapLimit, | ||
allObj, | ||
allLimitObj | ||
}; | ||
/** ALIAS - DeferredPromise */ | ||
export type DeferredPromise<T> = PromiseTools.DeferredPromise<T>; | ||
/** ALIAS - getDeferred */ | ||
export const getDeferred = PromiseTools.getDeferred; | ||
/** ALIAS - all */ | ||
export const all = PromiseTools.all; | ||
/** ALIAS - allLimit */ | ||
export const allLimit = PromiseTools.allLimit; | ||
/** ALIAS - each */ | ||
export const each = PromiseTools.each; | ||
/** ALIAS - eachLimit */ | ||
export const eachLimit = PromiseTools.eachLimit; | ||
/** ALIAS - map */ | ||
export const map = PromiseTools.map; | ||
/** ALIAS - mapLimit */ | ||
export const mapLimit = PromiseTools.mapLimit; | ||
/** ALIAS - allObj */ | ||
export const allObj = PromiseTools.allObj; | ||
/** ALIAS - allLimitObj */ | ||
export const allLimitObj = PromiseTools.allLimitObj; |
@@ -7,443 +7,461 @@ //<!-- DOCS: 120 --> | ||
*/ | ||
/**<!-- DOCS: ### --> | ||
* capitalise | ||
* | ||
* - `StringTools.capitalise` | ||
* | ||
* Capitalises the first letter of each word in a string | ||
* | ||
* ```typescript | ||
* StringTools.capitalise('hello world'); // 'Hello World' | ||
* ``` | ||
*/ | ||
export const capitalise = (input: string = '') => | ||
(input || '') | ||
.split(/\s/) | ||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) | ||
.join(' '); | ||
/**<!-- DOCS: ### --> | ||
* angloise | ||
* | ||
* - `StringTools.angloise` | ||
* | ||
* Remove accents from a string | ||
* | ||
* ```typescript | ||
* StringTools.angloise('éèêë'); // 'eeee' | ||
* ``` | ||
*/ | ||
export const angloise = (input: string): string => input.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); | ||
/**<!-- DOCS: ### --> | ||
* clean | ||
* | ||
* - `StringTools.clean` | ||
* | ||
* Remove accents and non alphanumerics from a string | ||
* | ||
* ```typescript | ||
* StringTools.clean('éèêë_--ab0'); // 'eeeeab0' | ||
* ``` | ||
*/ | ||
export const clean = (input: string = ''): string => | ||
angloise([input].flat().join(' ')) | ||
.replace(/\s{1,}/g, ' ') | ||
.replace(/[^A-Za-z0-9 ]/gi, ''); | ||
export type CaseInput = string | string[]; | ||
type SplittingFn = (input: CaseInput) => string[]; | ||
/**<!-- DOCS: ### --> | ||
* StringCaseHandler | ||
*/ | ||
export interface StringCaseHandler { | ||
/**<!-- DOCS: #### --> | ||
* toLowerCamelCase | ||
export namespace StringTools { | ||
/**<!-- DOCS: ### --> | ||
* capitalise | ||
* | ||
* - `StringTools.toLowerCamelCase` | ||
* - `StringTools.fromSlugCase.toLowerCamelCase` | ||
* - `StringTools.fromSnakeCase.toLowerCamelCase` | ||
* - `StringTools.fromSpaced.toLowerCamelCase` | ||
* - `StringTools.fromCamelCase.toLowerCamelCase` | ||
* - `StringTools.capitalise` | ||
* | ||
* Convert a string to lower camel case (e.g. `thisIsLowerCamelCase`) | ||
*/ | ||
toLowerCamelCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toUpperCamelCase | ||
* Capitalises the first letter of each word in a string | ||
* | ||
* - `StringTools.toUpperCamelCase` | ||
* - `StringTools.fromSlugCase.toUpperCamelCase` | ||
* - `StringTools.fromSnakeCase.toUpperCamelCase` | ||
* - `StringTools.fromSpaced.toUpperCamelCase` | ||
* - `StringTools.fromCamelCase.toUpperCamelCase` | ||
* | ||
* Convert a string to upper camel case (e.g. `ThisIsLowerCamelCase`) | ||
* ```typescript | ||
* StringTools.capitalise('hello world'); // 'Hello World' | ||
* ``` | ||
*/ | ||
toUpperCamelCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toCamelCase | ||
* | ||
* - `StringTools.toCamelCase` | ||
* - `StringTools.fromSlugCase.toCamelCase` | ||
* - `StringTools.fromSnakeCase.toCamelCase` | ||
* - `StringTools.fromSpaced.toCamelCase` | ||
* - `StringTools.fromCamelCase.toCamelCase` | ||
* | ||
* Convert a string to camel case (e.g. `thisIsCamelCase`) | ||
*/ | ||
toCamelCase(input: CaseInput, capitaliseFirst?: boolean): string; | ||
export const capitalise = (input: string = '') => | ||
(input || '') | ||
.split(/\s/) | ||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) | ||
.join(' '); | ||
/**<!-- DOCS: #### --> | ||
* toLowerSlugCase | ||
/**<!-- DOCS: ### --> | ||
* angloise | ||
* | ||
* - `StringTools.toLowerSlugCase` | ||
* - `StringTools.fromSlugCase.toLowerSlugCase` | ||
* - `StringTools.fromSnakeCase.toLowerSlugCase` | ||
* - `StringTools.fromSpaced.toLowerSlugCase` | ||
* - `StringTools.fromCamelCase.toLowerSlugCase` | ||
* - `StringTools.angloise` | ||
* | ||
* Convert a string to lower slug case (e.g. `this-is-lower-slug-case`) | ||
*/ | ||
toLowerSlugCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toUpperSlugCase | ||
* Remove accents from a string | ||
* | ||
* - `StringTools.toUpperSlugCase` | ||
* - `StringTools.fromSlugCase.toUpperSlugCase` | ||
* - `StringTools.fromSnakeCase.toUpperSlugCase` | ||
* - `StringTools.fromSpaced.toUpperSlugCase` | ||
* - `StringTools.fromCamelCase.toUpperSlugCase` | ||
* | ||
* Convert a string to upper camel case (e.g. `THIS-IS-UPPER-SLUG-CASE`) | ||
* ```typescript | ||
* StringTools.angloise('éèêë'); // 'eeee' | ||
* ``` | ||
*/ | ||
toUpperSlugCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toSlugCase | ||
* | ||
* - `StringTools.toSlugCase` | ||
* - `StringTools.fromSlugCase.toSlugCase` | ||
* - `StringTools.fromSnakeCase.toSlugCase` | ||
* - `StringTools.fromSpaced.toSlugCase` | ||
* - `StringTools.fromCamelCase.toSlugCase` | ||
* | ||
* Convert a string to camel case (e.g. `this-is-slug-case`) | ||
*/ | ||
toSlugCase(input: CaseInput, toUpper?: boolean): string; | ||
export const angloise = (input: string): string => input.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); | ||
/**<!-- DOCS: #### --> | ||
* toLowerSnakeCase | ||
/**<!-- DOCS: ### --> | ||
* clean | ||
* | ||
* - `StringTools.toLowerSnakeCase` | ||
* - `StringTools.fromSlugCase.toLowerSnakeCase` | ||
* - `StringTools.fromSnakeCase.toLowerSnakeCase` | ||
* - `StringTools.fromSpaced.toLowerSnakeCase` | ||
* - `StringTools.fromCamelCase.toLowerSnakeCase` | ||
* - `StringTools.clean` | ||
* | ||
* Convert a string to lower snake case (e.g. `this_is_lower_snake_case`) | ||
*/ | ||
toLowerSnakeCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toUpperSnakeCase | ||
* Remove accents and non alphanumerics from a string | ||
* | ||
* - `StringTools.toUpperSnakeCase` | ||
* - `StringTools.fromSlugCase.toUpperSnakeCase` | ||
* - `StringTools.fromSnakeCase.toUpperSnakeCase` | ||
* - `StringTools.fromSpaced.toUpperSnakeCase` | ||
* - `StringTools.fromCamelCase.toUpperSnakeCase` | ||
* | ||
* Convert a string to upper snake case (e.g. `THIS_IS_UPPER_SNAKE_CASE`) | ||
* ```typescript | ||
* StringTools.clean('éèêë_--ab0'); // 'eeeeab0' | ||
* ``` | ||
*/ | ||
toUpperSnakeCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toSnakeCase | ||
* | ||
* - `StringTools.toSnakeCase` | ||
* - `StringTools.fromSlugCase.toSnakeCase` | ||
* - `StringTools.fromSnakeCase.toSnakeCase` | ||
* - `StringTools.fromSpaced.toSnakeCase` | ||
* - `StringTools.fromCamelCase.toSnakeCase` | ||
* | ||
* Convert a string to snake case (e.g. `this_is_snake_case`) | ||
*/ | ||
toSnakeCase(input: CaseInput, toUpper?: boolean): string; | ||
export const clean = (input: string = ''): string => | ||
angloise([input].flat().join(' ')) | ||
.replace(/\s{1,}/g, ' ') | ||
.replace(/[^A-Za-z0-9 ]/gi, ''); | ||
/**<!-- DOCS: #### --> | ||
* toLowerSpaced | ||
* | ||
* - `StringTools.toLowerSpaced` | ||
* - `StringTools.fromSlugCase.toLowerSpaced` | ||
* - `StringTools.fromSnakeCase.toLowerSpaced` | ||
* - `StringTools.fromSpaced.toLowerSpaced` | ||
* - `StringTools.fromCamelCase.toLowerSpaced` | ||
* | ||
* Convert a string to lower spaced case (e.g. `this is lower spaced case`) | ||
*/ | ||
toLowerSpaced(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toUpperSpaced | ||
* | ||
* - `StringTools.toUpperSpaced` | ||
* - `StringTools.fromSlugCase.toUpperSpaced` | ||
* - `StringTools.fromSnakeCase.toUpperSpaced` | ||
* - `StringTools.fromSpaced.toUpperSpaced` | ||
* - `StringTools.fromCamelCase.toUpperSpaced` | ||
* | ||
* Convert a string to upper spaced case (e.g. `THIS IS UPPER SPACED CASE`) | ||
*/ | ||
toUpperSpaced(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toCapitalisedSpaced | ||
* | ||
* - `StringTools.toCapitalisedSpaced` | ||
* - `StringTools.fromSlugCase.toCapitalisedSpaced` | ||
* - `StringTools.fromSnakeCase.toCapitalisedSpaced` | ||
* - `StringTools.fromSpaced.toCapitalisedSpaced` | ||
* - `StringTools.fromCamelCase.toCapitalisedSpaced` | ||
* | ||
* Convert a string to capitalised spaced case (e.g. `This Is Capitalised Spaced Case`) | ||
*/ | ||
toCapitalisedSpaced(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toSpaced | ||
* | ||
* - `StringTools.toSpaced` | ||
* - `StringTools.fromSlugCase.toSpaced` | ||
* - `StringTools.fromSnakeCase.toSpaced` | ||
* - `StringTools.fromSpaced.toSpaced` | ||
* - `StringTools.fromCamelCase.toSpaced` | ||
* | ||
* Convert a string to spaced case (e.g. `this is spaced case`) | ||
*/ | ||
toSpaced(input: CaseInput, toUpper?: boolean): string; | ||
export type CaseInput = string | string[]; | ||
type SplittingFn = (input: CaseInput) => string[]; | ||
/**<!-- DOCS: #### --> | ||
* toCharacterSeparated | ||
* | ||
* - `StringTools.toCharacterSeparated` | ||
* - `StringTools.fromSlugCase.toCharacterSeparated` | ||
* - `StringTools.fromSnakeCase.toCharacterSeparated` | ||
* - `StringTools.fromSpaced.toCharacterSeparated` | ||
* - `StringTools.fromCamelCase.toCharacterSeparated` | ||
* | ||
* Convert a string to text where words are separated by a given character (e.g. `this#is#character#separated`) | ||
/**<!-- DOCS: ### --> | ||
* StringCaseHandler | ||
*/ | ||
toCharacterSeparated(input: CaseInput, char: string, toUpper?: boolean): string; | ||
} | ||
const caseHandler = (overrideSplitter?: SplittingFn): StringCaseHandler => { | ||
const getSplit: SplittingFn = (input: CaseInput = ''): string[] => { | ||
if (overrideSplitter) return overrideSplitter(input); | ||
const arr = [input].flat(); | ||
return arr | ||
.map((s) => clean(s.replace(/-|_/g, ' ')).split(' ')) | ||
.flat() | ||
.filter((s) => s.length); | ||
}; | ||
export interface StringCaseHandler { | ||
/**<!-- DOCS: #### --> | ||
* toLowerCamelCase | ||
* | ||
* - `StringTools.toLowerCamelCase` | ||
* - `StringTools.fromSlugCase.toLowerCamelCase` | ||
* - `StringTools.fromSnakeCase.toLowerCamelCase` | ||
* - `StringTools.fromSpaced.toLowerCamelCase` | ||
* - `StringTools.fromCamelCase.toLowerCamelCase` | ||
* | ||
* Convert a string to lower camel case (e.g. `thisIsLowerCamelCase`) | ||
*/ | ||
toLowerCamelCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toUpperCamelCase | ||
* | ||
* - `StringTools.toUpperCamelCase` | ||
* - `StringTools.fromSlugCase.toUpperCamelCase` | ||
* - `StringTools.fromSnakeCase.toUpperCamelCase` | ||
* - `StringTools.fromSpaced.toUpperCamelCase` | ||
* - `StringTools.fromCamelCase.toUpperCamelCase` | ||
* | ||
* Convert a string to upper camel case (e.g. `ThisIsLowerCamelCase`) | ||
*/ | ||
toUpperCamelCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toCamelCase | ||
* | ||
* - `StringTools.toCamelCase` | ||
* - `StringTools.fromSlugCase.toCamelCase` | ||
* - `StringTools.fromSnakeCase.toCamelCase` | ||
* - `StringTools.fromSpaced.toCamelCase` | ||
* - `StringTools.fromCamelCase.toCamelCase` | ||
* | ||
* Convert a string to camel case (e.g. `thisIsCamelCase`) | ||
*/ | ||
toCamelCase(input: CaseInput, capitaliseFirst?: boolean): string; | ||
const toCamelCase = (input: CaseInput, capitaliseFirst: boolean = false): string => { | ||
const split = getSplit(input); | ||
return split.map((word, index) => (index === 0 && !capitaliseFirst ? word.toLowerCase() : capitalise(word))).join(''); | ||
}; | ||
const toLowerCamelCase = (input: CaseInput): string => toCamelCase(input, false); | ||
const toUpperCamelCase = (input: CaseInput): string => toCamelCase(input, true); | ||
/**<!-- DOCS: #### --> | ||
* toLowerSlugCase | ||
* | ||
* - `StringTools.toLowerSlugCase` | ||
* - `StringTools.fromSlugCase.toLowerSlugCase` | ||
* - `StringTools.fromSnakeCase.toLowerSlugCase` | ||
* - `StringTools.fromSpaced.toLowerSlugCase` | ||
* - `StringTools.fromCamelCase.toLowerSlugCase` | ||
* | ||
* Convert a string to lower slug case (e.g. `this-is-lower-slug-case`) | ||
*/ | ||
toLowerSlugCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toUpperSlugCase | ||
* | ||
* - `StringTools.toUpperSlugCase` | ||
* - `StringTools.fromSlugCase.toUpperSlugCase` | ||
* - `StringTools.fromSnakeCase.toUpperSlugCase` | ||
* - `StringTools.fromSpaced.toUpperSlugCase` | ||
* - `StringTools.fromCamelCase.toUpperSlugCase` | ||
* | ||
* Convert a string to upper camel case (e.g. `THIS-IS-UPPER-SLUG-CASE`) | ||
*/ | ||
toUpperSlugCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toSlugCase | ||
* | ||
* - `StringTools.toSlugCase` | ||
* - `StringTools.fromSlugCase.toSlugCase` | ||
* - `StringTools.fromSnakeCase.toSlugCase` | ||
* - `StringTools.fromSpaced.toSlugCase` | ||
* - `StringTools.fromCamelCase.toSlugCase` | ||
* | ||
* Convert a string to camel case (e.g. `this-is-slug-case`) | ||
*/ | ||
toSlugCase(input: CaseInput, toUpper?: boolean): string; | ||
const toCharacterSeparated = (input: CaseInput, char: string, toUpper: boolean = false) => { | ||
const split = getSplit(input); | ||
return split.map((word, index) => (toUpper ? word.toUpperCase() : word.toLowerCase())).join(char); | ||
}; | ||
/**<!-- DOCS: #### --> | ||
* toLowerSnakeCase | ||
* | ||
* - `StringTools.toLowerSnakeCase` | ||
* - `StringTools.fromSlugCase.toLowerSnakeCase` | ||
* - `StringTools.fromSnakeCase.toLowerSnakeCase` | ||
* - `StringTools.fromSpaced.toLowerSnakeCase` | ||
* - `StringTools.fromCamelCase.toLowerSnakeCase` | ||
* | ||
* Convert a string to lower snake case (e.g. `this_is_lower_snake_case`) | ||
*/ | ||
toLowerSnakeCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toUpperSnakeCase | ||
* | ||
* - `StringTools.toUpperSnakeCase` | ||
* - `StringTools.fromSlugCase.toUpperSnakeCase` | ||
* - `StringTools.fromSnakeCase.toUpperSnakeCase` | ||
* - `StringTools.fromSpaced.toUpperSnakeCase` | ||
* - `StringTools.fromCamelCase.toUpperSnakeCase` | ||
* | ||
* Convert a string to upper snake case (e.g. `THIS_IS_UPPER_SNAKE_CASE`) | ||
*/ | ||
toUpperSnakeCase(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toSnakeCase | ||
* | ||
* - `StringTools.toSnakeCase` | ||
* - `StringTools.fromSlugCase.toSnakeCase` | ||
* - `StringTools.fromSnakeCase.toSnakeCase` | ||
* - `StringTools.fromSpaced.toSnakeCase` | ||
* - `StringTools.fromCamelCase.toSnakeCase` | ||
* | ||
* Convert a string to snake case (e.g. `this_is_snake_case`) | ||
*/ | ||
toSnakeCase(input: CaseInput, toUpper?: boolean): string; | ||
const toSlugCase = (input: CaseInput, toUpper: boolean = false): string => toCharacterSeparated(input, '-', toUpper); | ||
const toLowerSlugCase = (input: CaseInput): string => toSlugCase(input, false); | ||
const toUpperSlugCase = (input: CaseInput): string => toSlugCase(input, true); | ||
/**<!-- DOCS: #### --> | ||
* toLowerSpaced | ||
* | ||
* - `StringTools.toLowerSpaced` | ||
* - `StringTools.fromSlugCase.toLowerSpaced` | ||
* - `StringTools.fromSnakeCase.toLowerSpaced` | ||
* - `StringTools.fromSpaced.toLowerSpaced` | ||
* - `StringTools.fromCamelCase.toLowerSpaced` | ||
* | ||
* Convert a string to lower spaced case (e.g. `this is lower spaced case`) | ||
*/ | ||
toLowerSpaced(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toUpperSpaced | ||
* | ||
* - `StringTools.toUpperSpaced` | ||
* - `StringTools.fromSlugCase.toUpperSpaced` | ||
* - `StringTools.fromSnakeCase.toUpperSpaced` | ||
* - `StringTools.fromSpaced.toUpperSpaced` | ||
* - `StringTools.fromCamelCase.toUpperSpaced` | ||
* | ||
* Convert a string to upper spaced case (e.g. `THIS IS UPPER SPACED CASE`) | ||
*/ | ||
toUpperSpaced(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toCapitalisedSpaced | ||
* | ||
* - `StringTools.toCapitalisedSpaced` | ||
* - `StringTools.fromSlugCase.toCapitalisedSpaced` | ||
* - `StringTools.fromSnakeCase.toCapitalisedSpaced` | ||
* - `StringTools.fromSpaced.toCapitalisedSpaced` | ||
* - `StringTools.fromCamelCase.toCapitalisedSpaced` | ||
* | ||
* Convert a string to capitalised spaced case (e.g. `This Is Capitalised Spaced Case`) | ||
*/ | ||
toCapitalisedSpaced(input: CaseInput): string; | ||
/**<!-- DOCS: #### --> | ||
* toSpaced | ||
* | ||
* - `StringTools.toSpaced` | ||
* - `StringTools.fromSlugCase.toSpaced` | ||
* - `StringTools.fromSnakeCase.toSpaced` | ||
* - `StringTools.fromSpaced.toSpaced` | ||
* - `StringTools.fromCamelCase.toSpaced` | ||
* | ||
* Convert a string to spaced case (e.g. `this is spaced case`) | ||
*/ | ||
toSpaced(input: CaseInput, toUpper?: boolean): string; | ||
const toSnakeCase = (input: CaseInput, toUpper: boolean = false): string => toCharacterSeparated(input, '_', toUpper); | ||
const toLowerSnakeCase = (input: CaseInput): string => toSnakeCase(input, false); | ||
const toUpperSnakeCase = (input: CaseInput): string => toSnakeCase(input, true); | ||
/**<!-- DOCS: #### --> | ||
* toCharacterSeparated | ||
* | ||
* - `StringTools.toCharacterSeparated` | ||
* - `StringTools.fromSlugCase.toCharacterSeparated` | ||
* - `StringTools.fromSnakeCase.toCharacterSeparated` | ||
* - `StringTools.fromSpaced.toCharacterSeparated` | ||
* - `StringTools.fromCamelCase.toCharacterSeparated` | ||
* | ||
* Convert a string to text where words are separated by a given character (e.g. `this#is#character#separated`) | ||
*/ | ||
toCharacterSeparated(input: CaseInput, char: string, toUpper?: boolean): string; | ||
} | ||
const caseHandler = (overrideSplitter?: SplittingFn): StringCaseHandler => { | ||
const getSplit: SplittingFn = (input: CaseInput = ''): string[] => { | ||
if (overrideSplitter) return overrideSplitter(input); | ||
const arr = [input].flat(); | ||
return arr | ||
.map((s) => clean(s.replace(/-|_/g, ' ')).split(' ')) | ||
.flat() | ||
.filter((s) => s.length); | ||
}; | ||
const toSpaced = (input: CaseInput, toUpper: boolean = false): string => toCharacterSeparated(input, ' ', toUpper); | ||
const toLowerSpaced = (input: CaseInput): string => toSpaced(input, false); | ||
const toUpperSpaced = (input: CaseInput): string => toSpaced(input, true); | ||
const toCapitalisedSpaced = (input: CaseInput): string => capitalise(toSpaced(input, false)); | ||
const toCamelCase = (input: CaseInput, capitaliseFirst: boolean = false): string => { | ||
const split = getSplit(input); | ||
return split.map((word, index) => (index === 0 && !capitaliseFirst ? word.toLowerCase() : capitalise(word))).join(''); | ||
}; | ||
const toLowerCamelCase = (input: CaseInput): string => toCamelCase(input, false); | ||
const toUpperCamelCase = (input: CaseInput): string => toCamelCase(input, true); | ||
return { | ||
const toCharacterSeparated = (input: CaseInput, char: string, toUpper: boolean = false) => { | ||
const split = getSplit(input); | ||
return split.map((word, index) => (toUpper ? word.toUpperCase() : word.toLowerCase())).join(char); | ||
}; | ||
const toSlugCase = (input: CaseInput, toUpper: boolean = false): string => toCharacterSeparated(input, '-', toUpper); | ||
const toLowerSlugCase = (input: CaseInput): string => toSlugCase(input, false); | ||
const toUpperSlugCase = (input: CaseInput): string => toSlugCase(input, true); | ||
const toSnakeCase = (input: CaseInput, toUpper: boolean = false): string => toCharacterSeparated(input, '_', toUpper); | ||
const toLowerSnakeCase = (input: CaseInput): string => toSnakeCase(input, false); | ||
const toUpperSnakeCase = (input: CaseInput): string => toSnakeCase(input, true); | ||
const toSpaced = (input: CaseInput, toUpper: boolean = false): string => toCharacterSeparated(input, ' ', toUpper); | ||
const toLowerSpaced = (input: CaseInput): string => toSpaced(input, false); | ||
const toUpperSpaced = (input: CaseInput): string => toSpaced(input, true); | ||
const toCapitalisedSpaced = (input: CaseInput): string => capitalise(toSpaced(input, false)); | ||
return { | ||
toLowerCamelCase, | ||
toUpperCamelCase, | ||
toCamelCase, | ||
toLowerSlugCase, | ||
toUpperSlugCase, | ||
toSlugCase, | ||
toLowerSnakeCase, | ||
toUpperSnakeCase, | ||
toSnakeCase, | ||
toLowerSpaced, | ||
toUpperSpaced, | ||
toCapitalisedSpaced, | ||
toSpaced, | ||
toCharacterSeparated | ||
}; | ||
}; | ||
const standardCaseHandler = caseHandler(); | ||
export const { | ||
/** ALIAS - toLowerCamelCase */ | ||
toLowerCamelCase, | ||
/** ALIAS - toUpperCamelCase */ | ||
toUpperCamelCase, | ||
/** ALIAS - toCamelCase */ | ||
toCamelCase, | ||
/** ALIAS - toLowerSlugCase */ | ||
toLowerSlugCase, | ||
/** ALIAS - toUpperSlugCase */ | ||
toUpperSlugCase, | ||
/** ALIAS - toSlugCase */ | ||
toSlugCase, | ||
/** ALIAS - toLowerSnakeCase */ | ||
toLowerSnakeCase, | ||
/** ALIAS - toUpperSnakeCase */ | ||
toUpperSnakeCase, | ||
/** ALIAS - toSnakeCase */ | ||
toSnakeCase, | ||
/** ALIAS - toLowerSpaced */ | ||
toLowerSpaced, | ||
/** ALIAS - toUpperSpaced */ | ||
toUpperSpaced, | ||
/** ALIAS - toCapitalisedSpaced */ | ||
toCapitalisedSpaced, | ||
/** ALIAS - toSpaced */ | ||
toSpaced, | ||
/** ALIAS - toCharacterSeparated */ | ||
toCharacterSeparated | ||
}; | ||
}; | ||
} = standardCaseHandler; | ||
const standardCaseHandler = caseHandler(); | ||
export const { | ||
toLowerCamelCase, | ||
toUpperCamelCase, | ||
toCamelCase, | ||
/**<!-- DOCS: ### --> | ||
* fromSlugCase | ||
* | ||
* Has the following methods: | ||
* - `StringTools.fromSlugCase.toLowerCamelCase` | ||
* - `StringTools.fromSlugCase.toUpperCamelCase` | ||
* - `StringTools.fromSlugCase.toCamelCase` | ||
* - `StringTools.fromSlugCase.toLowerSlugCase` | ||
* - `StringTools.fromSlugCase.toUpperSlugCase` | ||
* - `StringTools.fromSlugCase.toSlugCase` | ||
* - `StringTools.fromSlugCase.toLowerSnakeCase` | ||
* - `StringTools.fromSlugCase.toUpperSnakeCase` | ||
* - `StringTools.fromSlugCase.toSnakeCase` | ||
* - `StringTools.fromSlugCase.toLowerSpaced` | ||
* - `StringTools.fromSlugCase.toUpperSpaced` | ||
* - `StringTools.fromSlugCase.toCapitalisedSpaced` | ||
* - `StringTools.fromSlugCase.toSpaced` | ||
* - `StringTools.fromSlugCase.toCharacterSeparated` | ||
*/ | ||
export const fromSlugCase = standardCaseHandler; | ||
toLowerSlugCase, | ||
toUpperSlugCase, | ||
toSlugCase, | ||
/**<!-- DOCS: ### --> | ||
* fromSnakeCase | ||
* | ||
* Has the following methods: | ||
* - `StringTools.fromSnakeCase.toLowerCamelCase` | ||
* - `StringTools.fromSnakeCase.toUpperCamelCase` | ||
* - `StringTools.fromSnakeCase.toCamelCase` | ||
* - `StringTools.fromSnakeCase.toLowerSlugCase` | ||
* - `StringTools.fromSnakeCase.toUpperSlugCase` | ||
* - `StringTools.fromSnakeCase.toSlugCase` | ||
* - `StringTools.fromSnakeCase.toLowerSnakeCase` | ||
* - `StringTools.fromSnakeCase.toUpperSnakeCase` | ||
* - `StringTools.fromSnakeCase.toSnakeCase` | ||
* - `StringTools.fromSnakeCase.toLowerSpaced` | ||
* - `StringTools.fromSnakeCase.toUpperSpaced` | ||
* - `StringTools.fromSnakeCase.toCapitalisedSpaced` | ||
* - `StringTools.fromSnakeCase.toSpaced` | ||
* - `StringTools.fromSnakeCase.toCharacterSeparated` | ||
*/ | ||
export const fromSnakeCase = standardCaseHandler; | ||
toLowerSnakeCase, | ||
toUpperSnakeCase, | ||
toSnakeCase, | ||
/**<!-- DOCS: ### --> | ||
* fromSpaced | ||
* | ||
* Has the following methods: | ||
* - `StringTools.fromSpaced.toLowerCamelCase` | ||
* - `StringTools.fromSpaced.toUpperCamelCase` | ||
* - `StringTools.fromSpaced.toCamelCase` | ||
* - `StringTools.fromSpaced.toLowerSlugCase` | ||
* - `StringTools.fromSpaced.toUpperSlugCase` | ||
* - `StringTools.fromSpaced.toSlugCase` | ||
* - `StringTools.fromSpaced.toLowerSnakeCase` | ||
* - `StringTools.fromSpaced.toUpperSnakeCase` | ||
* - `StringTools.fromSpaced.toSnakeCase` | ||
* - `StringTools.fromSpaced.toLowerSpaced` | ||
* - `StringTools.fromSpaced.toUpperSpaced` | ||
* - `StringTools.fromSpaced.toCapitalisedSpaced` | ||
* - `StringTools.fromSpaced.toSpaced` | ||
* - `StringTools.fromSpaced.toCharacterSeparated` | ||
*/ | ||
export const fromSpaced = standardCaseHandler; | ||
toLowerSpaced, | ||
toUpperSpaced, | ||
toCapitalisedSpaced, | ||
toSpaced, | ||
/**<!-- DOCS: ### --> | ||
* fromCamelCase | ||
* | ||
* Has the following methods: | ||
* - `StringTools.fromCamelCase.toLowerCamelCase` | ||
* - `StringTools.fromCamelCase.toUpperCamelCase` | ||
* - `StringTools.fromCamelCase.toCamelCase` | ||
* - `StringTools.fromCamelCase.toLowerSlugCase` | ||
* - `StringTools.fromCamelCase.toUpperSlugCase` | ||
* - `StringTools.fromCamelCase.toSlugCase` | ||
* - `StringTools.fromCamelCase.toLowerSnakeCase` | ||
* - `StringTools.fromCamelCase.toUpperSnakeCase` | ||
* - `StringTools.fromCamelCase.toSnakeCase` | ||
* - `StringTools.fromCamelCase.toLowerSpaced` | ||
* - `StringTools.fromCamelCase.toUpperSpaced` | ||
* - `StringTools.fromCamelCase.toCapitalisedSpaced` | ||
* - `StringTools.fromCamelCase.toSpaced` | ||
* - `StringTools.fromCamelCase.toCharacterSeparated` | ||
*/ | ||
export const fromCamelCase = caseHandler((input: CaseInput) => | ||
[input] | ||
.flat() | ||
.map((s) => clean(s)) | ||
.map((s) => | ||
s | ||
.replace(/([A-Z])/g, ' $1') | ||
.replace(/-|_/g, ' ') | ||
.trim() | ||
) | ||
.map((s) => s.split(' ')) | ||
.flat() | ||
); | ||
toCharacterSeparated | ||
} = standardCaseHandler; | ||
// clx | ||
const processClxArray = (arr: any): string[] => | ||
arr | ||
.filter(Boolean) | ||
.map((item) => { | ||
if (typeof item === 'string') return item; | ||
/**<!-- DOCS: ### --> | ||
* fromSlugCase | ||
* | ||
* Has the following methods: | ||
* - `StringTools.fromSlugCase.toLowerCamelCase` | ||
* - `StringTools.fromSlugCase.toUpperCamelCase` | ||
* - `StringTools.fromSlugCase.toCamelCase` | ||
* - `StringTools.fromSlugCase.toLowerSlugCase` | ||
* - `StringTools.fromSlugCase.toUpperSlugCase` | ||
* - `StringTools.fromSlugCase.toSlugCase` | ||
* - `StringTools.fromSlugCase.toLowerSnakeCase` | ||
* - `StringTools.fromSlugCase.toUpperSnakeCase` | ||
* - `StringTools.fromSlugCase.toSnakeCase` | ||
* - `StringTools.fromSlugCase.toLowerSpaced` | ||
* - `StringTools.fromSlugCase.toUpperSpaced` | ||
* - `StringTools.fromSlugCase.toCapitalisedSpaced` | ||
* - `StringTools.fromSlugCase.toSpaced` | ||
* - `StringTools.fromSlugCase.toCharacterSeparated` | ||
*/ | ||
export const fromSlugCase = standardCaseHandler; | ||
if (item instanceof Array) { | ||
return processClxArray(item); | ||
} | ||
/**<!-- DOCS: ### --> | ||
* fromSnakeCase | ||
* | ||
* Has the following methods: | ||
* - `StringTools.fromSnakeCase.toLowerCamelCase` | ||
* - `StringTools.fromSnakeCase.toUpperCamelCase` | ||
* - `StringTools.fromSnakeCase.toCamelCase` | ||
* - `StringTools.fromSnakeCase.toLowerSlugCase` | ||
* - `StringTools.fromSnakeCase.toUpperSlugCase` | ||
* - `StringTools.fromSnakeCase.toSlugCase` | ||
* - `StringTools.fromSnakeCase.toLowerSnakeCase` | ||
* - `StringTools.fromSnakeCase.toUpperSnakeCase` | ||
* - `StringTools.fromSnakeCase.toSnakeCase` | ||
* - `StringTools.fromSnakeCase.toLowerSpaced` | ||
* - `StringTools.fromSnakeCase.toUpperSpaced` | ||
* - `StringTools.fromSnakeCase.toCapitalisedSpaced` | ||
* - `StringTools.fromSnakeCase.toSpaced` | ||
* - `StringTools.fromSnakeCase.toCharacterSeparated` | ||
*/ | ||
export const fromSnakeCase = standardCaseHandler; | ||
if (typeof item === 'object') { | ||
return Object.keys(item) | ||
.filter((key) => item[key]) | ||
.join(' '); | ||
} | ||
}) | ||
.flat(); | ||
/**<!-- DOCS: ### --> | ||
* fromSpaced | ||
* | ||
* Has the following methods: | ||
* - `StringTools.fromSpaced.toLowerCamelCase` | ||
* - `StringTools.fromSpaced.toUpperCamelCase` | ||
* - `StringTools.fromSpaced.toCamelCase` | ||
* - `StringTools.fromSpaced.toLowerSlugCase` | ||
* - `StringTools.fromSpaced.toUpperSlugCase` | ||
* - `StringTools.fromSpaced.toSlugCase` | ||
* - `StringTools.fromSpaced.toLowerSnakeCase` | ||
* - `StringTools.fromSpaced.toUpperSnakeCase` | ||
* - `StringTools.fromSpaced.toSnakeCase` | ||
* - `StringTools.fromSpaced.toLowerSpaced` | ||
* - `StringTools.fromSpaced.toUpperSpaced` | ||
* - `StringTools.fromSpaced.toCapitalisedSpaced` | ||
* - `StringTools.fromSpaced.toSpaced` | ||
* - `StringTools.fromSpaced.toCharacterSeparated` | ||
*/ | ||
export const fromSpaced = standardCaseHandler; | ||
export type ClxType = string | boolean | { [key: string]: boolean } | ClxType[]; | ||
/**<!-- DOCS: ### --> | ||
* fromCamelCase | ||
* | ||
* Has the following methods: | ||
* - `StringTools.fromCamelCase.toLowerCamelCase` | ||
* - `StringTools.fromCamelCase.toUpperCamelCase` | ||
* - `StringTools.fromCamelCase.toCamelCase` | ||
* - `StringTools.fromCamelCase.toLowerSlugCase` | ||
* - `StringTools.fromCamelCase.toUpperSlugCase` | ||
* - `StringTools.fromCamelCase.toSlugCase` | ||
* - `StringTools.fromCamelCase.toLowerSnakeCase` | ||
* - `StringTools.fromCamelCase.toUpperSnakeCase` | ||
* - `StringTools.fromCamelCase.toSnakeCase` | ||
* - `StringTools.fromCamelCase.toLowerSpaced` | ||
* - `StringTools.fromCamelCase.toUpperSpaced` | ||
* - `StringTools.fromCamelCase.toCapitalisedSpaced` | ||
* - `StringTools.fromCamelCase.toSpaced` | ||
* - `StringTools.fromCamelCase.toCharacterSeparated` | ||
*/ | ||
export const fromCamelCase = caseHandler((input: CaseInput) => | ||
[input] | ||
.flat() | ||
.map((s) => clean(s)) | ||
.map((s) => | ||
s | ||
.replace(/([A-Z])/g, ' $1') | ||
.replace(/-|_/g, ' ') | ||
.trim() | ||
) | ||
.map((s) => s.split(' ')) | ||
.flat() | ||
); | ||
/**<!-- DOCS: ### --> | ||
* clx | ||
* | ||
* - `clx` | ||
* - `StringTools.clx` | ||
* | ||
* Composes a className from a list of strings, conditional objects and arrays. | ||
* | ||
* Accepts the different ways of supplying classes in AngularJS (ng-class) and returns a single string (so suitable for React). | ||
* | ||
* ```typescript | ||
* clx('hello') // 'hello' | ||
* clx('foo', 'bar') // 'foo bar' | ||
* clx('foo', conditionA && 'bar') // 'foo' | ||
* clx('abc', conditionB && 'def') // 'abc def' | ||
* clx({'lorem': conditionA, 'ipsum': conditionB}) // 'ipsum' | ||
* ``` | ||
*/ | ||
export const clx = (...args: ClxType[]) => processClxArray(args).join(' '); | ||
} | ||
// clx | ||
const processClxArray = (arr: any): string[] => | ||
arr | ||
.filter(Boolean) | ||
.map((item) => { | ||
if (typeof item === 'string') return item; | ||
if (item instanceof Array) { | ||
return processClxArray(item); | ||
} | ||
if (typeof item === 'object') { | ||
return Object.keys(item) | ||
.filter((key) => item[key]) | ||
.join(' '); | ||
} | ||
}) | ||
.flat(); | ||
export type ClxType = string | boolean | { [key: string]: boolean } | ClxType[]; | ||
/**<!-- DOCS: ### --> | ||
* clx | ||
* | ||
* - `clx` | ||
* - `StringTools.clx` | ||
* | ||
* Composes a className from a list of strings, conditional objects and arrays. | ||
* | ||
* Accepts the different ways of supplying classes in AngularJS (ng-class) and returns a single string (so suitable for React). | ||
* | ||
* ```typescript | ||
* clx('hello') // 'hello' | ||
* clx('foo', 'bar') // 'foo bar' | ||
* clx('foo', conditionA && 'bar') // 'foo' | ||
* clx('abc', conditionB && 'def') // 'abc def' | ||
* clx({'lorem': conditionA, 'ipsum': conditionB}) // 'ipsum' | ||
* ``` | ||
*/ | ||
export const clx = (...args: ClxType[]) => processClxArray(args).join(' '); | ||
/** ALIAS - clx */ | ||
export const clx = StringTools.clx; |
@@ -1,2 +0,2 @@ | ||
/*<!-- DOCS: ## 10 --> | ||
/**<!-- DOCS: ## 10 --> | ||
* times | ||
@@ -21,37 +21,498 @@ * | ||
*/ | ||
export namespace times { | ||
/**<!-- DOCS: ### -1 --> | ||
* ms | ||
* | ||
* - `ms` | ||
* - `times.ms` | ||
* | ||
* Type for number values in millisecond units | ||
*/ | ||
export type ms = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* second | ||
* | ||
* - `second` | ||
* - `times.second` | ||
* | ||
* Type for number values in second units | ||
*/ | ||
export type second = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* minute | ||
* | ||
* - `minute` | ||
* - `times.minute` | ||
* | ||
* Type for number values in minute units | ||
*/ | ||
export type minute = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* hour | ||
* | ||
* - `hour` | ||
* - `times.hour` | ||
* | ||
* Type for number values in hour units | ||
*/ | ||
export type hour = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* day | ||
* | ||
* - `day` | ||
* - `times.day` | ||
* | ||
* Type for number values in day units | ||
*/ | ||
export type day = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* week | ||
* | ||
* - `week` | ||
* - `times.week` | ||
* | ||
* Type for number values in week units | ||
*/ | ||
export type week = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* month | ||
* | ||
* - `month` | ||
* - `times.month` | ||
* | ||
* Type for number values in month units | ||
*/ | ||
export type month = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* year | ||
* | ||
* - `year` | ||
* - `times.year` | ||
* | ||
* Type for number values in year units | ||
*/ | ||
export type year = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* decade | ||
* | ||
* - `decade` | ||
* - `times.decade` | ||
* | ||
* Type for number values in decade units | ||
*/ | ||
export type decade = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* century | ||
* | ||
* - `century` | ||
* - `times.century` | ||
* | ||
* Type for number values in century units | ||
*/ | ||
export type century = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* millennium | ||
* | ||
* - `millennium` | ||
* - `times.millennium` | ||
* | ||
* Type for number values in millennium units | ||
*/ | ||
export type millennium = number; | ||
/**<!-- DOCS: ### -1 --> | ||
* MILLISECOND | ||
* | ||
* - `MILLISECOND` | ||
* - `times.MILLISECOND` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `1` (1 millisecond) | ||
*/ | ||
export const MILLISECOND = 1; | ||
/**<!-- DOCS: ### -1 --> | ||
* SECOND | ||
* | ||
* - `SECOND` | ||
* - `times.SECOND` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `1000` (1,000 milliseconds) | ||
*/ | ||
export const SECOND = 1000 * MILLISECOND; | ||
/**<!-- DOCS: ### -1 --> | ||
* MINUTE | ||
* | ||
* - `MINUTE` | ||
* - `times.MINUTE` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `60_000` (60 seconds) | ||
*/ | ||
export const MINUTE = 60 * SECOND; | ||
/**<!-- DOCS: ### -1 --> | ||
* HOUR | ||
* | ||
* - `HOUR` | ||
* - `times.HOUR` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `3_600_000` (60 minutes) | ||
*/ | ||
export const HOUR = 60 * MINUTE; | ||
/**<!-- DOCS: ### -1 --> | ||
* DAY | ||
* | ||
* - `DAY` | ||
* - `times.DAY` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `86_400_000` (24 hours) | ||
*/ | ||
export const DAY = 24 * HOUR; | ||
/**<!-- DOCS: ### -1 --> | ||
* WEEK | ||
* | ||
* - `WEEK` | ||
* - `times.WEEK` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `604_800_000` (7 days) | ||
*/ | ||
export const WEEK = 7 * DAY; | ||
/**<!-- DOCS: ### -1 --> | ||
* MONTH | ||
* | ||
* - `MONTH` | ||
* - `times.MONTH` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `2_592_000_000` (30 days) | ||
*/ | ||
export const MONTH = 30 * DAY; | ||
/**<!-- DOCS: ### -1 --> | ||
* YEAR | ||
* | ||
* - `YEAR` | ||
* - `times.YEAR` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `31_557_600_000` (365.25 days) | ||
*/ | ||
export const YEAR = 365.25 * DAY; | ||
/**<!-- DOCS: ### -1 --> | ||
* DECADE | ||
* | ||
* - `DECADE` | ||
* - `times.DECADE` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `315_576_000_000` (10 years / 3,652.5 days) | ||
*/ | ||
export const DECADE = 10 * YEAR; | ||
/**<!-- DOCS: ### -1 --> | ||
* CENTURY | ||
* | ||
* - `CENTURY` | ||
* - `times.CENTURY` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `3_155_760_000_000` (100 years / 36,525 days) | ||
*/ | ||
export const CENTURY = 100 * YEAR; | ||
/**<!-- DOCS: ### -1 --> | ||
* MILLENNIUM | ||
* | ||
* - `MILLENNIUM` | ||
* - `times.MILLENNIUM` | ||
* | ||
* Constant in ms (millisecond) units | ||
* | ||
* Equal to `31_557_600_000_000` (1000 years / 365,250 days) | ||
*/ | ||
export const MILLENNIUM = 1000 * YEAR; | ||
/**<!-- DOCS: ### -1 --> | ||
* milliseconds | ||
* | ||
* - `milliseconds` | ||
* - `times.milliseconds` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of milliseconds | ||
* | ||
* ```typescript | ||
* milliseconds(); // 1 (1 milliseconds) | ||
* milliseconds(1); // 1 (1 milliseconds) | ||
* milliseconds(2); // 2 (2 milliseconds) | ||
* milliseconds(5); // 5 (5 milliseconds) | ||
* ``` | ||
*/ | ||
export const milliseconds = (x: ms = 1): ms => x; | ||
/**<!-- DOCS: ### -1 --> | ||
* seconds | ||
* | ||
* - `seconds` | ||
* - `times.seconds` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of seconds | ||
* | ||
* ```typescript | ||
* seconds(); // 1_000 (1 second) | ||
* seconds(1); // 1_000 (1 second) | ||
* seconds(2); // 2_000 (2 seconds) | ||
* seconds(5); // 5_000 (5 seconds) | ||
* ``` | ||
*/ | ||
export const seconds = (x: second = 1): ms => x * SECOND; | ||
/**<!-- DOCS: ### -1 --> | ||
* minutes | ||
* | ||
* - `minutes` | ||
* - `times.minutes` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of minutes | ||
* | ||
* ```typescript | ||
* minutes(); // 60_000 (60 seconds) | ||
* minutes(1); // 60_000 (60 seconds) | ||
* minutes(2); // 120_000 (120 seconds) | ||
* minutes(5); // 300_000 (300 seconds) | ||
* ``` | ||
*/ | ||
export const minutes = (x: minute = 1): ms => x * MINUTE; | ||
/**<!-- DOCS: ### -1 --> | ||
* hours | ||
* | ||
* - `hours` | ||
* - `times.hours` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of hours | ||
* | ||
* ```typescript | ||
* hours(); // 3_600_000 (1 hour / 60 minutes) | ||
* hours(1); // 3_600_000 (1 hour / 60 minutes) | ||
* hours(2); // 7_200_000 (2 hours / 120 minutes) | ||
* hours(5); // 18_000_000 (5 hours / 300 minutes) | ||
* ``` | ||
*/ | ||
export const hours = (x: hour = 1): ms => x * HOUR; | ||
/**<!-- DOCS: ### -1 --> | ||
* days | ||
* | ||
* - `days` | ||
* - `times.days` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of days | ||
* | ||
* ```typescript | ||
* days(); // 86_400_000 (1 day / 24 hours) | ||
* days(1); // 86_400_000 (1 day / 24 hours) | ||
* days(2); // 172_800_000 (2 days / 48 hours) | ||
* days(5); // 432_000_000 (5 days / 120 hours) | ||
* ``` | ||
*/ | ||
export const days = (x: day = 1): ms => x * DAY; | ||
/**<!-- DOCS: ### -1 --> | ||
* weeks | ||
* | ||
* - `weeks` | ||
* - `times.weeks` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of weeks | ||
* | ||
* ```typescript | ||
* weeks(); // 604_800_000 (7 days / 168 hours) | ||
* weeks(1); // 604_800_000 (7 days / 168 hours) | ||
* weeks(2); // 1_209_600_000 (14 days / 336 hours) | ||
* weeks(5); // 3_024_000_000 (35 days / 840 hours) | ||
* ``` | ||
*/ | ||
export const weeks = (x: week = 1): ms => x * WEEK; | ||
/**<!-- DOCS: ### -1 --> | ||
* months | ||
* | ||
* - `months` | ||
* - `times.months` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of months | ||
* | ||
* ```typescript | ||
* months(); // 2_592_000_000 (30 days) | ||
* months(1); // 2_592_000_000 (30 days) | ||
* months(2); // 5_184_000_000 (60 days) | ||
* months(5); // 12_960_000_000 (150 days) | ||
* ``` | ||
*/ | ||
export const months = (x: month = 1): ms => x * MONTH; | ||
/**<!-- DOCS: ### -1 --> | ||
* years | ||
* | ||
* - `years` | ||
* - `times.years` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of years | ||
* | ||
* ```typescript | ||
* years(); // 31_557_600_000 (1 year / 365.25 days) | ||
* years(1); // 31_557_600_000 (1 year / 365.25 days) | ||
* years(2); // 63_115_200_000 (2 years / 730.5 days) | ||
* years(5); // 157_788_000_000 (5 years / 1,826.25 days) | ||
* ``` | ||
*/ | ||
export const years = (x: year = 1): ms => x * YEAR; | ||
/**<!-- DOCS: ### -1 --> | ||
* decades | ||
* | ||
* - `decades` | ||
* - `times.decades` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of decades | ||
* | ||
* ```typescript | ||
* decades(); // 315_576_000_000 (10 years / 3,652.5 days) | ||
* decades(1); // 315_576_000_000 (10 years / 3,652.5 days) | ||
* decades(2); // 631_152_000_000 (20 years / 7,305 days) | ||
* decades(5); // 1_577_880_000_000 (50 years / 18,262.5 days) | ||
* ``` | ||
*/ | ||
export const decades = (x: decade = 1): ms => x * DECADE; | ||
/**<!-- DOCS: ### -1 --> | ||
* centuries | ||
* | ||
* - `centuries` | ||
* - `times.centuries` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of centuries | ||
* | ||
* ```typescript | ||
* centuries(); // 3_155_760_000_000 (100 years / 36,525 days) | ||
* centuries(1); // 3_155_760_000_000 (100 years / 36,525 days) | ||
* centuries(2); // 6_311_520_000_000 (200 years / 73,050 days) | ||
* centuries(5); // 15_778_800_000_000 (500 years / 182,625 days) | ||
* ``` | ||
*/ | ||
export const centuries = (x: century = 1): ms => x * CENTURY; | ||
/**<!-- DOCS: ### -1 --> | ||
* millenniums | ||
* | ||
* - `millenniums` | ||
* - `times.millenniums` | ||
* | ||
* Function that returns the number of milliseconds equal to a multiple of millenniums | ||
* | ||
* ```typescript | ||
* millenniums(); // 31_557_600_000_000 (1000 years / 365,250 days) | ||
* millenniums(1); // 31_557_600_000_000 (1000 years / 365,250 days) | ||
* millenniums(2); // 63_115_200_000_000 (2000 years / 730,500 days) | ||
* millenniums(5); // 157_788_000_000_000 (5000 years / 1,826,250 days) | ||
* ``` | ||
*/ | ||
export const millenniums = (x: millennium = 1): ms => x * MILLENNIUM; | ||
} | ||
/** ALIAS - ms */ | ||
export type ms = number; | ||
/** ALIAS - second */ | ||
export type second = number; | ||
/** ALIAS - minute */ | ||
export type minute = number; | ||
/** ALIAS - hour */ | ||
export type hour = number; | ||
/** ALIAS - day */ | ||
export type day = number; | ||
/** ALIAS - week */ | ||
export type week = number; | ||
/** ALIAS - month */ | ||
export type month = number; | ||
/** ALIAS - year */ | ||
export type year = number; | ||
/** ALIAS - decade */ | ||
export type decade = number; | ||
/** ALIAS - century */ | ||
export type century = number; | ||
/** ALIAS - millennium */ | ||
export type millennium = number; | ||
export const MILLISECOND = 1; | ||
export const SECOND = 1000 * MILLISECOND; | ||
export const MINUTE = 60 * SECOND; | ||
export const HOUR = 60 * MINUTE; | ||
export const DAY = 24 * HOUR; | ||
export const WEEK = 7 * DAY; | ||
export const MONTH = 30 * DAY; | ||
export const YEAR = 365.25 * DAY; | ||
export const DECADE = 10 * YEAR; | ||
export const CENTURY = 100 * YEAR; | ||
export const MILLENNIUM = 1000 * YEAR; | ||
export const milliseconds = (x: ms = 1): ms => x; | ||
export const seconds = (x: second = 1): ms => x * SECOND; | ||
export const minutes = (x: minute = 1): ms => x * MINUTE; | ||
export const hours = (x: hour = 1): ms => x * HOUR; | ||
export const days = (x: day = 1): ms => x * DAY; | ||
export const weeks = (x: week = 1): ms => x * WEEK; | ||
export const months = (x: month = 1): ms => x * MONTH; | ||
export const years = (x: year = 1): ms => x * YEAR; | ||
export const decades = (x: decade = 1): ms => x * DECADE; | ||
export const centuries = (x: century = 1): ms => x * CENTURY; | ||
export const millenniums = (x: millennium = 1): ms => x * MILLENNIUM; | ||
/** ALIAS - MILLISECOND */ | ||
export const MILLISECOND = times.MILLISECOND; | ||
/** ALIAS - SECOND */ | ||
export const SECOND = times.SECOND; | ||
/** ALIAS - MINUTE */ | ||
export const MINUTE = times.MINUTE; | ||
/** ALIAS - HOUR */ | ||
export const HOUR = times.HOUR; | ||
/** ALIAS - DAY */ | ||
export const DAY = times.DAY; | ||
/** ALIAS - WEEK */ | ||
export const WEEK = times.WEEK; | ||
/** ALIAS - MONTH */ | ||
export const MONTH = times.MONTH; | ||
/** ALIAS - YEAR */ | ||
export const YEAR = times.YEAR; | ||
/** ALIAS - DECADE */ | ||
export const DECADE = times.DECADE; | ||
/** ALIAS - CENTURY */ | ||
export const CENTURY = times.CENTURY; | ||
/** ALIAS - MILLENNIUM */ | ||
export const MILLENNIUM = times.MILLENNIUM; | ||
/** ALIAS - milliseconds */ | ||
export const milliseconds = times.milliseconds; | ||
/** ALIAS - seconds */ | ||
export const seconds = times.seconds; | ||
/** ALIAS - minutes */ | ||
export const minutes = times.minutes; | ||
/** ALIAS - hours */ | ||
export const hours = times.hours; | ||
/** ALIAS - days */ | ||
export const days = times.days; | ||
/** ALIAS - weeks */ | ||
export const weeks = times.weeks; | ||
/** ALIAS - months */ | ||
export const months = times.months; | ||
/** ALIAS - years */ | ||
export const years = times.years; | ||
/** ALIAS - decades */ | ||
export const decades = times.decades; | ||
/** ALIAS - centuries */ | ||
export const centuries = times.centuries; | ||
/** ALIAS - millenniums */ | ||
export const millenniums = times.millenniums; |
import { ms, MILLISECOND, SECOND, MINUTE, HOUR, DAY, MONTH, YEAR, CENTURY, MILLENNIUM } from './times'; | ||
//<!-- DOCS: 160 --> | ||
/**<!-- DOCS: ## --> | ||
* TimeTools | ||
* | ||
* A collection of time-related utility functions. | ||
*/ | ||
@@ -68,59 +63,62 @@ interface DurationUnitLabel { | ||
/**<!-- DOCS: ### --> | ||
* toReadableDuration | ||
/**<!-- DOCS: ## --> | ||
* TimeTools | ||
* | ||
* - `TimeTools.toReadableDuration` | ||
* | ||
* Converts a duration in milliseconds to a human readable string. | ||
* | ||
* ```typescript | ||
* TimeTools.toReadableDuration(20); // '20ms' | ||
* TimeTools.toReadableDuration(seconds(59)); // '59s' | ||
* TimeTools.toReadableDuration(seconds(60)); // '1m' | ||
* TimeTools.toReadableDuration(hours(23)); // '23h' | ||
* TimeTools.toReadableDuration(hours(24)); // '1d' | ||
* TimeTools.toReadableDuration(days(10)); // '10d' | ||
* | ||
* TimeTools.toReadableDuration(20, true) // '20 milliseconds' | ||
* TimeTools.toReadableDuration(seconds(59), true) // '59 seconds' | ||
* TimeTools.toReadableDuration(seconds(60), true) // '1 minute' | ||
* TimeTools.toReadableDuration(hours(23), true) // '23 hours' | ||
* TimeTools.toReadableDuration(hours(24), true) // '1 day' | ||
* TimeTools.toReadableDuration(days(10), true) // '10 days' | ||
* | ||
* const realisticDuration = days(10) + hours(2) + seconds(31) + 512; // 871231512 | ||
* TimeTools.toReadableDuration(realisticDuration, true, 4) // '10 days, 2 hours, 31 seconds & 512 milliseconds' | ||
* TimeTools.toReadableDuration(realisticDuration, true) // '10 days, 2 hours & 31 seconds' | ||
* TimeTools.toReadableDuration(realisticDuration, true, 2) // '10 days & 2 hours' | ||
* ``` | ||
* A collection of time-related utility functions. | ||
*/ | ||
const toReadableDuration = (duration: ms, longNames: boolean = false, maxUnits: number = 3): string => { | ||
if (duration === 0) return ''; | ||
export namespace TimeTools { | ||
/**<!-- DOCS: ### --> | ||
* toReadableDuration | ||
* | ||
* - `TimeTools.toReadableDuration` | ||
* | ||
* Converts a duration in milliseconds to a human readable string. | ||
* | ||
* ```typescript | ||
* TimeTools.toReadableDuration(20); // '20ms' | ||
* TimeTools.toReadableDuration(seconds(59)); // '59s' | ||
* TimeTools.toReadableDuration(seconds(60)); // '1m' | ||
* TimeTools.toReadableDuration(hours(23)); // '23h' | ||
* TimeTools.toReadableDuration(hours(24)); // '1d' | ||
* TimeTools.toReadableDuration(days(10)); // '10d' | ||
* | ||
* TimeTools.toReadableDuration(20, true) // '20 milliseconds' | ||
* TimeTools.toReadableDuration(seconds(59), true) // '59 seconds' | ||
* TimeTools.toReadableDuration(seconds(60), true) // '1 minute' | ||
* TimeTools.toReadableDuration(hours(23), true) // '23 hours' | ||
* TimeTools.toReadableDuration(hours(24), true) // '1 day' | ||
* TimeTools.toReadableDuration(days(10), true) // '10 days' | ||
* | ||
* const realisticDuration = days(10) + hours(2) + seconds(31) + 512; // 871231512 | ||
* TimeTools.toReadableDuration(realisticDuration, true, 4) // '10 days, 2 hours, 31 seconds & 512 milliseconds' | ||
* TimeTools.toReadableDuration(realisticDuration, true) // '10 days, 2 hours & 31 seconds' | ||
* TimeTools.toReadableDuration(realisticDuration, true, 2) // '10 days & 2 hours' | ||
* ``` | ||
*/ | ||
export const toReadableDuration = (duration: ms, longNames: boolean = false, maxUnits: number = 3): string => { | ||
if (duration === 0) return ''; | ||
const allUnitValues = units | ||
.map((unit, index) => { | ||
const previousUnitValue = units[index - 1]?.value ?? Infinity; | ||
const amount = Math.floor((Math.abs(duration) % previousUnitValue) / unit.value); | ||
return { amount, unit }; | ||
}) | ||
.filter(({ amount }) => amount > 0); | ||
const allUnitValues = units | ||
.map((unit, index) => { | ||
const previousUnitValue = units[index - 1]?.value ?? Infinity; | ||
const amount = Math.floor((Math.abs(duration) % previousUnitValue) / unit.value); | ||
return { amount, unit }; | ||
}) | ||
.filter(({ amount }) => amount > 0); | ||
const results: string[] = allUnitValues.slice(0, maxUnits).map(({ amount, unit }) => { | ||
const labelObj = longNames ? unit.long : unit.short; | ||
const label = amount > 1 ? labelObj.plural : labelObj.singular; | ||
return `${amount}${label}`; | ||
}); | ||
const results: string[] = allUnitValues.slice(0, maxUnits).map(({ amount, unit }) => { | ||
const labelObj = longNames ? unit.long : unit.short; | ||
const label = amount > 1 ? labelObj.plural : labelObj.singular; | ||
return `${amount}${label}`; | ||
}); | ||
if (longNames) { | ||
if (results.length <= 1) { | ||
return results.join(''); | ||
if (longNames) { | ||
if (results.length <= 1) { | ||
return results.join(''); | ||
} | ||
return [...results.slice(0, -1), '&&&&', ...results.slice(-1)].join(', ').replace('&&&&,', '&').replace(', &', ' &'); | ||
} | ||
return [...results.slice(0, -1), '&&&&', ...results.slice(-1)].join(', ').replace('&&&&,', '&').replace(', &', ' &'); | ||
} | ||
return results.join(' '); | ||
}; | ||
export const TimeTools = { | ||
toReadableDuration | ||
}; | ||
return results.join(' '); | ||
}; | ||
} |
@@ -5,3 +5,3 @@ import { ms } from './times'; | ||
/*<!-- DOCS: ## --> | ||
/**<!-- DOCS: ## --> | ||
* waiters | ||
@@ -21,146 +21,160 @@ * | ||
*/ | ||
export namespace waiters { | ||
/**<!-- DOCS: ### --> | ||
* wait | ||
* | ||
* - `wait` | ||
* - `waiters.wait` | ||
* | ||
* Standard wait promise (using setTimeout) | ||
* | ||
* ```typescript | ||
* import { wait } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* await wait(minutes(2)); | ||
* console.log(new Date().toTimeString()); // 12:32:10 | ||
* ``` | ||
*/ | ||
export const wait = (time: ms) => new Promise((resolve) => setTimeout(resolve, time)); | ||
/**<!-- DOCS: ### --> | ||
* wait | ||
* | ||
* - `wait` | ||
* - `waiters.wait` | ||
* | ||
* Standard wait promise (using setTimeout) | ||
* | ||
* ```typescript | ||
* import { wait } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* await wait(minutes(2)); | ||
* console.log(new Date().toTimeString()); // 12:32:10 | ||
* ``` | ||
*/ | ||
export const wait = (time: ms) => new Promise((resolve) => setTimeout(resolve, time)); | ||
// a certain percentage of the difference between now and given time | ||
const PING_RATIO = 0.75; | ||
const ROUND_AMOUNT = 1.5; | ||
const getPingDuration = (time: ms, now: ms = Date.now()): ms => Math.ceil(((time - now) * PING_RATIO) / ROUND_AMOUNT) * ROUND_AMOUNT; | ||
// a certain percentage of the difference between now and given time | ||
const PING_RATIO = 0.75; | ||
const ROUND_AMOUNT = 1.5; | ||
const getPingDuration = (time: ms, now: ms = Date.now()): ms => Math.ceil(((time - now) * PING_RATIO) / ROUND_AMOUNT) * ROUND_AMOUNT; | ||
/**<!-- DOCS: ### --> | ||
* waitUntil | ||
* | ||
* - `waitUntil` | ||
* - `waiters.waitUntil` | ||
* | ||
* Accurate (pinged) wait until given time | ||
* | ||
* ```typescript | ||
* import { waitUntil } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* await waitUntil(Date.now() + minutes(10)); | ||
* console.log(new Date().toTimeString()); // 12:40:10 | ||
* ``` | ||
*/ | ||
export const waitUntil = async (time: ms): Promise<null> => { | ||
while (Date.now() < time) { | ||
await wait(getPingDuration(time)); | ||
} | ||
return null; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* waitUntil | ||
* | ||
* - `waitUntil` | ||
* - `waiters.waitUntil` | ||
* | ||
* Accurate (pinged) wait until given time | ||
* | ||
* ```typescript | ||
* import { waitUntil } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* await waitUntil(Date.now() + minutes(10)); | ||
* console.log(new Date().toTimeString()); // 12:40:10 | ||
* ``` | ||
*/ | ||
export const waitUntil = async (time: ms): Promise<null> => { | ||
while (Date.now() < time) { | ||
await wait(getPingDuration(time)); | ||
} | ||
return null; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* waitFor | ||
* | ||
* - `waitFor` | ||
* - `waiters.waitFor` | ||
* | ||
* Accurate (pinged) wait the given ms | ||
* | ||
* ```typescript | ||
* import { waitFor } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* await waitFor(minutes(5)); | ||
* console.log(new Date().toTimeString()); // 12:35:10 | ||
* ``` | ||
*/ | ||
export const waitFor = async (time: ms): Promise<null> => waitUntil(Date.now() + time); | ||
/**<!-- DOCS: ### --> | ||
* waitFor | ||
* | ||
- `waitFor` | ||
- `waiters.waitFor` | ||
* | ||
* Accurate (pinged) wait the given ms | ||
* | ||
* ```typescript | ||
* import { waitFor } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* await waitFor(minutes(5)); | ||
* console.log(new Date().toTimeString()); // 12:35:10 | ||
* ``` | ||
*/ | ||
export const waitFor = async (time: ms): Promise<null> => waitUntil(Date.now() + time); | ||
// get the time (ms) until the next 'every X' event | ||
const getNextEvery = (timing: ms, offset: ms = 0): ms => { | ||
const now = Date.now(); | ||
const result = timing - ((now - offset) % timing); | ||
return result <= 10 ? timing : result; | ||
}; | ||
// get the time (ms) until the next 'every X' event | ||
const getNextEvery = (timing: ms, offset: ms = 0): ms => { | ||
const now = Date.now(); | ||
const result = timing - ((now - offset) % timing); | ||
return result <= 10 ? timing : result; | ||
}; | ||
/**<!-- DOCS: ### --> | ||
* waitEvery | ||
* | ||
* - `waitEvery` | ||
* - `waiters.waitEvery` | ||
* | ||
* Accurate (pinged) wait for next 'every X' event | ||
* | ||
* ```typescript | ||
* import { waitEvery } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* await waitEvery(hours(2)); | ||
* console.log(new Date().toTimeString()); // 14:00:00 | ||
* ``` | ||
*/ | ||
export const waitEvery = (timing: ms, offset?: ms): Promise<null> => waitFor(getNextEvery(timing, offset)); | ||
/**<!-- DOCS: ### --> | ||
* waitEvery | ||
* | ||
- `waitEvery` | ||
- `waiters.waitEvery` | ||
* | ||
* Accurate (pinged) wait for next 'every X' event | ||
* | ||
* ```typescript | ||
* import { waitEvery } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* await waitEvery(hours(2)); | ||
* console.log(new Date().toTimeString()); // 14:00:00 | ||
* ``` | ||
*/ | ||
export const waitEvery = (timing: ms, offset?: ms): Promise<null> => waitFor(getNextEvery(timing, offset)); | ||
const stopped: number[] = []; | ||
/**<!-- DOCS: ### --> | ||
* stopInterval | ||
* | ||
* - `stopInterval` | ||
* - `waiters.stopInterval` | ||
* | ||
* ```typescript | ||
* import { interval, stopInterval } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* interval((intID, count) => { | ||
* console.log(new Date().toTimeString()); // 13:00:00, 14:00:00, 15:00:00 | ||
* if (count === 3) { | ||
* stopInterval(intID); | ||
* } | ||
* }, hours(1)); | ||
* ``` | ||
*/ | ||
export const stopInterval = (intID: number) => stopped.push(intID); | ||
const stopped: number[] = []; | ||
/**<!-- DOCS: ### --> | ||
* stopInterval | ||
* | ||
- `stopInterval` | ||
- `waiters.stopInterval` | ||
* | ||
* ```typescript | ||
* import { interval, stopInterval } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* interval((intID, count) => { | ||
* console.log(new Date().toTimeString()); // 13:00:00, 14:00:00, 15:00:00 | ||
* if (count === 3) { | ||
* stopInterval(intID); | ||
* } | ||
* }, hours(1)); | ||
* ``` | ||
*/ | ||
export const stopInterval = (intID: number) => stopped.push(intID); | ||
/**<!-- DOCS: ### --> | ||
* interval | ||
* | ||
- `interval` | ||
- `waiters.interval` | ||
* | ||
* Accurate (pinged) interval for every 'every X' event | ||
* | ||
* ```typescript | ||
* import { interval, stopInterval } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* interval((intID, count) => { | ||
* console.log(new Date().toTimeString()); // 13:00:00, 14:00:00, 15:00:00 | ||
* if (count === 3) { | ||
* stopInterval(intID); | ||
* } | ||
* }, hours(1)); | ||
* ``` | ||
*/ | ||
export const interval = (action: (intID?: number, count?: number) => any, timing: ms): number => { | ||
const intID: number = Math.floor(Math.random() * Math.pow(10, 10)); | ||
let count: number = 0; | ||
const run = async () => { | ||
await waitEvery(timing); | ||
if (stopped.includes(intID)) { | ||
return; | ||
} | ||
action(intID, ++count); | ||
/**<!-- DOCS: ### --> | ||
* interval | ||
* | ||
* - `interval` | ||
* - `waiters.interval` | ||
* | ||
* Accurate (pinged) interval for every 'every X' event | ||
* | ||
* ```typescript | ||
* import { interval, stopInterval } from 'swiss-ak'; | ||
* | ||
* console.log(new Date().toTimeString()); // 12:30:10 | ||
* interval((intID, count) => { | ||
* console.log(new Date().toTimeString()); // 13:00:00, 14:00:00, 15:00:00 | ||
* if (count === 3) { | ||
* stopInterval(intID); | ||
* } | ||
* }, hours(1)); | ||
* ``` | ||
*/ | ||
export const interval = (action: (intID?: number, count?: number) => any, timing: ms): number => { | ||
const intID: number = Math.floor(Math.random() * Math.pow(10, 10)); | ||
let count: number = 0; | ||
const run = async () => { | ||
await waitEvery(timing); | ||
if (stopped.includes(intID)) { | ||
return; | ||
} | ||
action(intID, ++count); | ||
run(); | ||
}; | ||
run(); | ||
return intID; | ||
}; | ||
run(); | ||
return intID; | ||
}; | ||
} | ||
/** ALIAS - wait */ | ||
export const wait = waiters.wait; | ||
/** ALIAS - waitUntil */ | ||
export const waitUntil = waiters.waitUntil; | ||
/** ALIAS - waitFor */ | ||
export const waitFor = waiters.waitFor; | ||
/** ALIAS - waitEvery */ | ||
export const waitEvery = waiters.waitEvery; | ||
/** ALIAS - stopInterval */ | ||
export const stopInterval = waiters.stopInterval; | ||
/** ALIAS - interval */ | ||
export const interval = waiters.interval; |
@@ -1,235 +0,117 @@ | ||
import { | ||
MILLISECOND, | ||
SECOND, | ||
MINUTE, | ||
HOUR, | ||
DAY, | ||
WEEK, | ||
MONTH, | ||
YEAR, | ||
DECADE, | ||
CENTURY, | ||
MILLENNIUM, | ||
milliseconds, | ||
seconds, | ||
minutes, | ||
hours, | ||
days, | ||
weeks, | ||
months, | ||
years, | ||
decades, | ||
centuries, | ||
millenniums | ||
} from '../src/tools/times'; | ||
import * as swissak from '../'; | ||
describe('MILLISECOND', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(MILLISECOND).toBe(1); | ||
const testConstant = (name: string, value: number, expected: number) => { | ||
describe(name, () => { | ||
it(`${name} - should be correct milliseconds`, () => { | ||
expect(value).toBe(expected); | ||
}); | ||
}); | ||
}); | ||
describe('SECOND', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(SECOND).toBe(1000); | ||
}; | ||
const testMultipliers = (name: string, func: Function, baseValue: number, multipliers: number[] = [0, 2, 5, 10, -1, -100]) => { | ||
describe(name, () => { | ||
it(`${name} - should default input to 1`, () => { | ||
expect(func()).toBe(func(1)); | ||
}); | ||
multipliers.forEach((multiplier) => { | ||
it(`${name} - should handle ${multiplier}x`, () => { | ||
expect(func(multiplier)).toBe(multiplier * baseValue); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('MINUTE', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(MINUTE).toBe(60_000); | ||
}); | ||
}); | ||
describe('HOUR', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(HOUR).toBe(3_600_000); | ||
}); | ||
}); | ||
describe('DAY', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(DAY).toBe(86_400_000); | ||
}); | ||
}); | ||
describe('WEEK', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(WEEK).toBe(604_800_000); | ||
}); | ||
}); | ||
describe('MONTH', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(MONTH).toBe(2_592_000_000); | ||
}); | ||
}); | ||
describe('YEAR', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(YEAR).toBe(31_557_600_000); | ||
}); | ||
}); | ||
describe('DECADE', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(DECADE).toBe(315_576_000_000); | ||
}); | ||
}); | ||
describe('CENTURY', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(CENTURY).toBe(3_155_760_000_000); | ||
}); | ||
}); | ||
describe('MILLENNIUM', () => { | ||
it('should be correct milliseconds', () => { | ||
expect(MILLENNIUM).toBe(31_557_600_000_000); | ||
}); | ||
}); | ||
}; | ||
describe('milliseconds', () => { | ||
it('should default input to 1', () => { | ||
expect(milliseconds()).toBe(milliseconds(1)); | ||
describe('times constants', () => { | ||
describe('milliseconds', () => { | ||
testConstant('MILLISECOND', swissak.MILLISECOND, 1); | ||
testConstant('times.MILLISECOND', swissak.times.MILLISECOND, 1); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(milliseconds(2)).toBe(2 * MILLISECOND); | ||
describe('seconds', () => { | ||
testConstant('SECOND', swissak.SECOND, 1000); | ||
testConstant('times.SECOND', swissak.times.SECOND, 1000); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(milliseconds(5)).toBe(5 * MILLISECOND); | ||
describe('minutes', () => { | ||
testConstant('MINUTE', swissak.MINUTE, 60_000); | ||
testConstant('times.MINUTE', swissak.times.MINUTE, 60_000); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(milliseconds(10)).toBe(10 * MILLISECOND); | ||
describe('hours', () => { | ||
testConstant('HOUR', swissak.HOUR, 3_600_000); | ||
testConstant('times.HOUR', swissak.times.HOUR, 3_600_000); | ||
}); | ||
}); | ||
describe('seconds', () => { | ||
it('should default input to 1', () => { | ||
expect(seconds()).toBe(seconds(1)); | ||
describe('days', () => { | ||
testConstant('DAY', swissak.DAY, 86_400_000); | ||
testConstant('times.DAY', swissak.times.DAY, 86_400_000); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(seconds(2)).toBe(2 * SECOND); | ||
describe('weeks', () => { | ||
testConstant('WEEK', swissak.WEEK, 604_800_000); | ||
testConstant('times.WEEK', swissak.times.WEEK, 604_800_000); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(seconds(5)).toBe(5 * SECOND); | ||
describe('months', () => { | ||
testConstant('MONTH', swissak.MONTH, 2_592_000_000); | ||
testConstant('times.MONTH', swissak.times.MONTH, 2_592_000_000); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(seconds(10)).toBe(10 * SECOND); | ||
describe('years', () => { | ||
testConstant('YEAR', swissak.YEAR, 31_557_600_000); | ||
testConstant('times.YEAR', swissak.times.YEAR, 31_557_600_000); | ||
}); | ||
}); | ||
describe('minutes', () => { | ||
it('should default input to 1', () => { | ||
expect(minutes()).toBe(minutes(1)); | ||
describe('decades', () => { | ||
testConstant('DECADE', swissak.DECADE, 315_576_000_000); | ||
testConstant('times.DECADE', swissak.times.DECADE, 315_576_000_000); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(minutes(2)).toBe(2 * MINUTE); | ||
describe('centuries', () => { | ||
testConstant('CENTURY', swissak.CENTURY, 3_155_760_000_000); | ||
testConstant('times.CENTURY', swissak.times.CENTURY, 3_155_760_000_000); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(minutes(5)).toBe(5 * MINUTE); | ||
describe('millenniums', () => { | ||
testConstant('MILLENNIUM', swissak.MILLENNIUM, 31_557_600_000_000); | ||
testConstant('times.MILLENNIUM', swissak.times.MILLENNIUM, 31_557_600_000_000); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(minutes(10)).toBe(10 * MINUTE); | ||
}); | ||
}); | ||
describe('hours', () => { | ||
it('should default input to 1', () => { | ||
expect(hours()).toBe(hours(1)); | ||
describe('times functions', () => { | ||
describe('milliseconds', () => { | ||
testMultipliers('milliseconds', swissak.milliseconds, swissak.MILLISECOND); | ||
testMultipliers('times.milliseconds', swissak.times.milliseconds, swissak.MILLISECOND); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(hours(2)).toBe(2 * HOUR); | ||
describe('seconds', () => { | ||
testMultipliers('seconds', swissak.seconds, swissak.SECOND); | ||
testMultipliers('times.seconds', swissak.times.seconds, swissak.SECOND); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(hours(5)).toBe(5 * HOUR); | ||
describe('minutes', () => { | ||
testMultipliers('minutes', swissak.minutes, swissak.MINUTE); | ||
testMultipliers('times.minutes', swissak.times.minutes, swissak.MINUTE); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(hours(10)).toBe(10 * HOUR); | ||
describe('hours', () => { | ||
testMultipliers('hours', swissak.hours, swissak.HOUR); | ||
testMultipliers('times.hours', swissak.times.hours, swissak.HOUR); | ||
}); | ||
}); | ||
describe('days', () => { | ||
it('should default input to 1', () => { | ||
expect(days()).toBe(days(1)); | ||
describe('days', () => { | ||
testMultipliers('days', swissak.days, swissak.DAY); | ||
testMultipliers('times.days', swissak.times.days, swissak.DAY); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(days(2)).toBe(2 * DAY); | ||
describe('weeks', () => { | ||
testMultipliers('weeks', swissak.weeks, swissak.WEEK); | ||
testMultipliers('times.weeks', swissak.times.weeks, swissak.WEEK); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(days(5)).toBe(5 * DAY); | ||
describe('months', () => { | ||
testMultipliers('months', swissak.months, swissak.MONTH); | ||
testMultipliers('times.months', swissak.times.months, swissak.MONTH); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(days(10)).toBe(10 * DAY); | ||
describe('years', () => { | ||
testMultipliers('years', swissak.years, swissak.YEAR); | ||
testMultipliers('times.years', swissak.times.years, swissak.YEAR); | ||
}); | ||
}); | ||
describe('weeks', () => { | ||
it('should default input to 1', () => { | ||
expect(weeks()).toBe(weeks(1)); | ||
describe('decades', () => { | ||
testMultipliers('decades', swissak.decades, swissak.DECADE); | ||
testMultipliers('times.decades', swissak.times.decades, swissak.DECADE); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(weeks(2)).toBe(2 * WEEK); | ||
describe('centuries', () => { | ||
testMultipliers('centuries', swissak.centuries, swissak.CENTURY); | ||
testMultipliers('times.centuries', swissak.times.centuries, swissak.CENTURY); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(weeks(5)).toBe(5 * WEEK); | ||
describe('millenniums', () => { | ||
testMultipliers('millenniums', swissak.millenniums, swissak.MILLENNIUM); | ||
testMultipliers('times.millenniums', swissak.times.millenniums, swissak.MILLENNIUM); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(weeks(10)).toBe(10 * WEEK); | ||
}); | ||
}); | ||
describe('months', () => { | ||
it('should default input to 1', () => { | ||
expect(months()).toBe(months(1)); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(months(2)).toBe(2 * MONTH); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(months(5)).toBe(5 * MONTH); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(months(10)).toBe(10 * MONTH); | ||
}); | ||
}); | ||
describe('years', () => { | ||
it('should default input to 1', () => { | ||
expect(years()).toBe(years(1)); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(years(2)).toBe(2 * YEAR); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(years(5)).toBe(5 * YEAR); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(years(10)).toBe(10 * YEAR); | ||
}); | ||
}); | ||
describe('decades', () => { | ||
it('should default input to 1', () => { | ||
expect(decades()).toBe(decades(1)); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(decades(2)).toBe(2 * DECADE); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(decades(5)).toBe(5 * DECADE); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(decades(10)).toBe(10 * DECADE); | ||
}); | ||
}); | ||
describe('centuries', () => { | ||
it('should default input to 1', () => { | ||
expect(centuries()).toBe(centuries(1)); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(centuries(2)).toBe(2 * CENTURY); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(centuries(5)).toBe(5 * CENTURY); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(centuries(10)).toBe(10 * CENTURY); | ||
}); | ||
}); | ||
describe('millenniums', () => { | ||
it('should default input to 1', () => { | ||
expect(millenniums()).toBe(millenniums(1)); | ||
}); | ||
it('should handle 2x', () => { | ||
expect(millenniums(2)).toBe(2 * MILLENNIUM); | ||
}); | ||
it('should handle 5x', () => { | ||
expect(millenniums(5)).toBe(5 * MILLENNIUM); | ||
}); | ||
it('should handle 10x', () => { | ||
expect(millenniums(10)).toBe(10 * MILLENNIUM); | ||
}); | ||
}); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
512304
40
12952
2706