vlens
Version:
Data Centric Routing & Rendering Mini-Framework
168 lines (140 loc) • 4.28 kB
text/typescript
// direction: 1 one step down, -1 one step up
export function reorderListItem<T>(list: T[], index: number, direction: number) {
let otherIndex = index + direction;
let item = list[index];
let otherItem = list[otherIndex];
list[index] = otherItem;
list[otherIndex] = item;
}
export function removeListItem<T>(list: T[], item: T) {
let idx = list.indexOf(item)
if (idx !== -1) {
list.splice(idx, 1)
}
}
export function toggleListItem<T>(list: T[], item: T) {
let idx = list.indexOf(item)
if (idx !== -1) {
list.splice(idx, 1)
} else {
list.push(item)
}
}
const CURSOR_DIV_ID = 'global-cursor'
export function setGlobalCursor(cursorStyle: string) {
// create a transparent div with a specific id
// if div already exists, remove it and recrate it.
// I know it's dumb but at least it's more reliable
{
let existing = document.getElementById(CURSOR_DIV_ID);
if (existing) {
existing.remove();
}
}
let div = document.createElement('div');
div.id = CURSOR_DIV_ID;
div.style.position = 'fixed';
div.style.top = '0';
div.style.left = '0';
div.style.right = '0';
div.style.bottom = '0';
div.style.background = 'transparent';
div.style.zIndex = '10000000000';
div.style.cursor = cursorStyle;
document.body.appendChild(div);
}
export function unsetGlobalCursor() {
let existing = document.getElementById(CURSOR_DIV_ID);
if (existing) {
existing.remove();
}
}
export interface ArrayMove {
src: number;
dest: number;
}
function arraySwap<T>(array: T[], swap: ArrayMove) {
let src_item = array[swap.src];
let dest_item = array[swap.dest];
array[swap.src] = dest_item;
array[swap.dest] = src_item;
}
// move item in array without resizing array
// sounds dumb but prevents flicker when array is a list of child nodes
export function arrayBubbleMove<T>(array: T[], swap: ArrayMove) {
if (swap.src < swap.dest) {
for (let i = swap.src; i < swap.dest - 1; i++) {
arraySwap(array, { src: i, dest: i + 1 })
}
} else {
for (let i = swap.src; i > swap.dest; i--) {
arraySwap(array, { src: i, dest: i - 1 })
}
}
}
export function removePrefix(s: string, prefix: string): string {
if (s.startsWith(prefix)) {
return s.substring(prefix.length);
} else {
return s;
}
}
export function intUrlArg(route: string, prefix: string, fallback: number = 0): number {
let arg = removePrefix(route, prefix)
return parseIntOrFallback(arg, fallback);
}
export function numberOrFallback(numValue: number, fallback: number = 0): number {
if (Number.isSafeInteger(numValue)) {
return numValue
} else {
return fallback
}
}
export function parseIntOrFallback(s: string | null, fallback: number = 0): number {
if (s === null) {
return fallback
}
return numberOrFallback(parseInt(s), fallback)
}
export function urlParams(path: string): URLSearchParams {
let idx = path.indexOf('?')
if (idx == 0) {
return new URLSearchParams()
} else {
return new URLSearchParams(path.substring(idx))
}
}
export function intParam(p: URLSearchParams, name: string, fallback: number = 0): number {
let strValue = p.get(name)
return parseIntOrFallback(p.get(name), fallback)
}
export function measure(repeat: number, fn: Function) {
let t1 = performance.now()
for(let i = 0; i < repeat; i++) {
fn()
}
let t2 = performance.now()
let dur = t2 - t1
return dur.toFixed(2) + "ms"
}
export function timeout(ms: number): Promise<void> {
if (ms <= 0) {
return Promise.resolve()
}
return new Promise(r => setTimeout(r, ms))
}
export function timeoutUntil(time: number) {
return timeout(time - Date.now())
}
export async function minimumWait<T>(ms: number, p: Promise<T>): Promise<T> {
let start = Date.now()
let result = await p;
await timeoutUntil(start + ms)
return result
}
export function assert(t: unknown, msg: string) {
if (t === null) {
console.error("assertion error:", msg)
throw new Error("Assertion Error")
}
}