@pippenly/ts-utils
v1.2.0
Published
Library where I implement ideas I have or consolidate utilities/helpers I use often
Maintainers
Readme
TS-Utils
Library where I implement ideas I have or consolidate utilities/helpers I use often
DOM
Resin
A reactive state that can bind to an HTML element to automatically apply updates based on value changes. Has some utilities for controlling visibility, mapping inner values, and passes the default element event listener through so you can use those normally.
const number = resin(5); // Resin<T>;
const numberElment = document.getElementById("number")!; // HTMLElement
const otherNumberElement = document.getElementById("other-number")!; // HTMLElement
const binding = bind(number, numberElment); // BoundResin<T>
const complexBinding = bind(number, otherNumberElement, {
bindTo: "innerText",
map: (n) => n * 2,
tap: (n, el) => { ... },
if: (n) => n > 5,
class: {
"red": (n, el) => n > 10,
"blue": (n, el) => n <= 10
},
attr: {
"data-value": (n, el) => String(n)
}
});
number.value = 15; // Updates number.value, which triggers the effect to update innerText
setTimeout(() => {
console.log("Stop receiving updates for counter binding");
binding.dispose();
}, 5000);Two-way binding
For two way binding, model(sourceResin, element, options?) exists, where element is
type ModelElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement. Provides
throttle / debounce options and custom get/set if needed.
import { resin, bind, model, watchEffect } from "@pippenly/ts-utils/dom";
const name = resin("");
const m = model(name, document.querySelector<HTMLInputElement>("#name")!, {
debounce: 300
});
const checked = resin(false);
const checkedModel = model(checked, document.querySelector<HTMLInputElement>("#check")!, {
event: 'change',
get: (el) => el.checked,
set: (v, el) => { el.checked = v; }
});
const age = resin(0);
const ageModel = model(age, document.querySelector<HTMLInputElement>("#age")!, {
get: (el) => el.valueAsNumber,
set: (v, el) => { el.valueAsNumber = v; }
});Scope
Idea for an IoC container, will keep refining as usages reveal cracks. For now, have a naive usage example:
interface HttpClient {
get(url: string): Promise<string>;
}
interface DbConnection<T> {
query(sql: string): Promise<T>;
}
async function main() {
const HttpClient = defineToken<HttpClient>({
name: "HttpClient",
lifetime: "scoped",
instantiation: "lazy",
build: async () =>
Ok(ENV === "production" ? new ProdHttpClient() : new DevHttpClient()),
});
const DbConnection = defineToken<DbConnection<string>>({
name: "DbConnection",
lifetime: "singleton",
instantiation: "eager",
build: async () =>
Ok(ENV === "production" ? new ProdDbConnection() : new DevDbConnection()),
teardown: async (instance) => { ... },
});
const container = root(HttpClient, DbConnection);
const task = defineTask<typeof container>()({
deps: [HttpClient, DbConnection] as const,
run: async ([httpClient, dbConnection], { url, sql }: TaskArgs) => {
const httpResponse = await httpClient.get(url);
const dbResult = await dbConnection.query(sql);
return Ok<QueryResult>({ data: `${httpResponse} | ${dbResult}` });
},
});
const scopeResult = await container.scope();
if (!scopeResult.ok) {
console.error("Failed to create scope:", scopeResult.error);
return;
}
const result = await scopeResult.value.run(task, {
url: "http://example.com",
sql: "SELECT * FROM users",
});
if (!result.ok) {
console.error("Task failed:", result.error);
} else {
console.log("Task succeeded:", result.value);
}
}Utilities
Result
Small utility library so I can work with error as values and explict Error type declaration
Structures
Queue only so far, it's what I found myself using the most often
Utils
Quality of life things
