SOURCE / PINNED RELEASE
Made of little things.
Black Site Map Editor
- Release
- 32a57733b7e9…
- Author-recorded commit
- 3f54890f542f…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/black-site-map-editor
Archive hash verified: 7a4b1abbac7db5b0…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import type { ReactiveController, ReactiveControllerHost } from 'lit';
type Listener<S> = (state: S) => void;
/** A minimal observable state container; `set` merges a patch and notifies subscribers. */
export class Store<S extends object> {
#state: S;
#listeners = new Set<Listener<S>>();
constructor(initial: S) {
this.#state = initial;
}
get state(): S {
return this.#state;
}
set(patch: Partial<S>): void {
this.#state = { ...this.#state, ...patch };
for (const listener of this.#listeners) listener(this.#state);
}
subscribe(listener: Listener<S>): () => void {
this.#listeners.add(listener);
return () => this.#listeners.delete(listener);
}
}
function sameValues(a: unknown, b: unknown): boolean {
if (Object.is(a, b)) return true;
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
return a.every((value, index) => Object.is(value, b[index]));
}
/**
* Re-render a Lit host when its selected slice of the store changes.
* Selectors may return a single value or an array of values compared item by item.
*/
export class StoreController<S extends object, T> implements ReactiveController {
value: T;
#unsubscribe?: () => void;
constructor(
private host: ReactiveControllerHost,
private store: Store<S>,
private select: (state: S) => T,
) {
this.value = select(store.state);
host.addController(this);
}
hostConnected(): void {
this.value = this.select(this.store.state);
this.#unsubscribe = this.store.subscribe((state) => {
const next = this.select(state);
if (sameValues(next, this.value)) return;
this.value = next;
this.host.requestUpdate();
});
}
hostDisconnected(): void {
this.#unsubscribe?.();
}
}
