add method for filtering

This commit is contained in:
marlon-erler
2026-09-23 00:09:09 +02:00
parent 3458799a3b
commit 26c99085d2
61 changed files with 1703 additions and 1623 deletions
+136 -136
View File
@@ -85,7 +85,7 @@ export class ListState<T> extends State<Set<T>> {
items.forEach((item) => {
this.value.delete(item);
this.genericRemovalHandlers.forEach(handler => handler(item));
this.genericRemovalHandlers.forEach((handler) => handler(item));
if (!this.removalHandlers.has(item)) return;
this.removalHandlers.get(item)!.forEach((handler) => handler(item));
this.removalHandlers.delete(item);
@@ -146,73 +146,73 @@ export class MapState<T> extends State<Map<string, T>> {
this.value.delete(key);
this.callSubscriptions();
this.genericRemovalHandlers.forEach(handler => handler(item));
if (!this.removalHandlers.has(item)) return;
this.removalHandlers.get(item)!.forEach((handler) => handler(item));
this.removalHandlers.delete(item);
this.genericRemovalHandlers.forEach((handler) => handler(item));
if (!this.removalHandlers.has(item)) return;
this.removalHandlers.get(item)!.forEach((handler) => handler(item));
this.removalHandlers.delete(item);
}
clear() {
[...this.value.keys()].forEach((key) => this.remove(key));
[...this.value.keys()].forEach((key) => this.remove(key));
}
// handlers
handleAddition(handler: AdditionSubscription<T>): void {
this.additionHandlers.add(handler);
[...this.value.values()].forEach(handler);
this.additionHandlers.add(handler);
[...this.value.values()].forEach(handler);
}
handleRemoval(item: T, handler: RemovalSubscription<T>): void {
if (!this.removalHandlers.has(item))
this.removalHandlers.set(item, new Set());
this.removalHandlers.get(item)!.add(handler);
if (!this.removalHandlers.has(item))
this.removalHandlers.set(item, new Set());
this.removalHandlers.get(item)!.add(handler);
}
handleRemovals(handler: RemovalSubscription<T>): void {
this.genericRemovalHandlers.add(handler);
this.genericRemovalHandlers.add(handler);
}
// stringification
toString(): string {
const array = [...this.value.entries()];
const json = JSON.stringify(array);
return json;
const array = [...this.value.entries()];
const json = JSON.stringify(array);
return json;
}
}
// UTILITY
export function createProxyState<T>(
statesToSubscibe: State<any>[],
fn: () => T
fn: () => T,
): State<T> {
const proxyState = new State<T>(fn());
statesToSubscibe.forEach((state) =>
state.subscribeSilent(() => (proxyState.value = fn()))
state.subscribeSilent(() => (proxyState.value = fn())),
);
return proxyState;
}
export function bulkSubscribe(
statesToSubscibe: State<any>[],
fn: () => void
fn: () => void,
): void {
statesToSubscibe.forEach((state) => state.subscribeSilent(fn));
}
function persistState(localStorageKey: string, state: State<any>) {
state.subscribe(() => {
const stringifiedValue = state.toString();
localStorage.setItem(localStorageKey, stringifiedValue);
const stringifiedValue = state.toString();
localStorage.setItem(localStorageKey, stringifiedValue);
});
}
export function restoreState<T>(
localStorageKey: string,
initialStateValue: T
initialStateValue: T,
): State<T> {
const storedString =
localStorage.getItem(localStorageKey) ??
JSON.stringify(initialStateValue);
localStorage.getItem(localStorageKey) ??
JSON.stringify(initialStateValue);
const convertedValue = JSON.parse(storedString);
const state = new State(convertedValue);
@@ -223,14 +223,14 @@ export function restoreState<T>(
export function restoreListState<T>(
localStorageKey: string,
initialItems: any[] = []
initialItems: any[] = [],
): ListState<T> {
const storedString = localStorage.getItem(localStorageKey) ?? "";
try {
const array = JSON.parse(storedString);
if (!Array.isArray(array)) throw "";
initialItems = array;
const array = JSON.parse(storedString);
if (!Array.isArray(array)) throw "";
initialItems = array;
} catch {}
const state = new ListState<T>(initialItems);
@@ -241,14 +241,14 @@ export function restoreListState<T>(
export function restoreMapState<T>(
localStorageKey: string,
initialItems: [string, any][] = []
initialItems: [string, any][] = [],
): MapState<T> {
const storedString = localStorage.getItem(localStorageKey) ?? "";
try {
const array = JSON.parse(storedString);
if (!Array.isArray(array)) throw "";
initialItems = array;
const array = JSON.parse(storedString);
if (!Array.isArray(array)) throw "";
initialItems = array;
} catch {}
const state = new MapState<T>(initialItems);
@@ -271,115 +271,115 @@ export function createElement(
const element = document.createElement(tagName);
if (attributes != null)
Object.entries(attributes).forEach((entry) => {
const [attributename, value] = entry;
const [directiveKey, directiveValue] = attributename.split(":");
Object.entries(attributes).forEach((entry) => {
const [attributename, value] = entry;
const [directiveKey, directiveValue] = attributename.split(":");
switch (directiveKey) {
case "on": {
switch (directiveValue) {
case "enter": {
element.addEventListener("keydown", (e: Event) => {
if (!(e instanceof KeyboardEvent)) return;
if (e.key != "Enter") return;
value(e);
});
break;
}
default: {
element.addEventListener(directiveValue, value);
}
}
break;
}
case "keystroke": {
element.addEventListener("keydown", (e: Event) => {
if (!(e instanceof KeyboardEvent)) return;
if (e.metaKey == false && e.ctrlKey == false) return;
if (e.key != directiveValue) return;
value(e);
})
break;
}
case "subscribe": {
const state = value as State<any>;
state.subscribe(
(newValue) => (element[directiveValue] = newValue)
);
switch (directiveKey) {
case "on": {
switch (directiveValue) {
case "enter": {
element.addEventListener("keydown", (e: Event) => {
if (!(e instanceof KeyboardEvent)) return;
if (e.key != "Enter") return;
value(e);
});
break;
}
default: {
element.addEventListener(directiveValue, value);
}
}
break;
}
case "keystroke": {
element.addEventListener("keydown", (e: Event) => {
if (!(e instanceof KeyboardEvent)) return;
if (e.metaKey == false && e.ctrlKey == false) return;
if (e.key != directiveValue) return;
value(e);
});
break;
}
case "subscribe": {
const state = value as State<any>;
state.subscribe(
(newValue) => (element[directiveValue] = newValue),
);
break;
}
case "bind": {
const state = value as State<any>;
state.subscribe(
(newValue) => (element[directiveValue] = newValue)
);
element.addEventListener(
"input",
() => (state.value = (element as any)[directiveValue])
);
break;
}
case "toggle": {
if (value.subscribe) {
const state = value as State<any>;
state.subscribe((newValue) =>
element.toggleAttribute(directiveValue, newValue)
);
} else {
element.toggleAttribute(directiveValue, value);
}
break;
}
case "set": {
const state = value as State<any>;
state.subscribe((newValue) =>
element.setAttribute(directiveValue, newValue)
);
break;
}
case "children": {
switch (directiveValue) {
case "set": {
const state = value as State<Node | Node[]>;
state.subscribe((newValue) => {
element.innerHTML = "";
element.append(...[newValue].flat());
});
break;
}
case "append":
case "prepend": {
try {
const [listState, toElement] = value as [
listState: ListState<any>,
StateItemConverter<any>
];
break;
}
case "bind": {
const state = value as State<any>;
state.subscribe(
(newValue) => (element[directiveValue] = newValue),
);
element.addEventListener(
"input",
() => (state.value = (element as any)[directiveValue]),
);
break;
}
case "toggle": {
if (value.subscribe) {
const state = value as State<any>;
state.subscribe((newValue) =>
element.toggleAttribute(directiveValue, newValue),
);
} else {
element.toggleAttribute(directiveValue, value);
}
break;
}
case "set": {
const state = value as State<any>;
state.subscribe((newValue) =>
element.setAttribute(directiveValue, newValue),
);
break;
}
case "children": {
switch (directiveValue) {
case "set": {
const state = value as State<Node | Node[]>;
state.subscribe((newValue) => {
element.innerHTML = "";
element.append(...[newValue].flat());
});
break;
}
case "append":
case "prepend": {
try {
const [listState, toElement] = value as [
listState: ListState<any>,
StateItemConverter<any>,
];
listState.handleAddition((newItem) => {
const child = toElement(newItem);
listState.handleRemoval(newItem, () =>
child.remove()
);
listState.handleAddition((newItem) => {
const child = toElement(newItem);
listState.handleRemoval(newItem, () =>
child.remove(),
);
if (directiveValue == "append") {
element.append(child);
} else if (directiveValue == "prepend") {
element.prepend(child);
}
});
} catch (error) {
console.error(error);
throw `error: cannot process subscribe:children directive. \n Usage: "children:append={[list, converter]}"; you can find a more detailed example in the documentation.`;
}
}
}
break;
}
default:
element.setAttribute(attributename, value);
}
});
if (directiveValue == "append") {
element.append(child);
} else if (directiveValue == "prepend") {
element.prepend(child);
}
});
} catch (error) {
console.error(error);
throw `error: cannot process subscribe:children directive. \n Usage: "children:append={[list, converter]}"; you can find a more detailed example in the documentation.`;
}
}
}
break;
}
default:
element.setAttribute(attributename, value);
}
});
children.filter((x) => x).forEach((child) => element.append(child));