refactor: use mutative for entity signals
This commit is contained in:
+25
-62
@@ -1,80 +1,43 @@
|
||||
import {Signal, signal} from "@preact/signals-core";
|
||||
import {Signal, signal, SignalOptions} from "@preact/signals-core";
|
||||
import {create} from 'mutative';
|
||||
|
||||
export type Entity = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type EntityAccessor<T extends Entity> = {
|
||||
id: string;
|
||||
value: T;
|
||||
export class Entity<T> extends Signal<T> {
|
||||
public constructor(public readonly id: string, t?: T, options?: SignalOptions<T>) {
|
||||
super(t, options);
|
||||
}
|
||||
produce(fn: (draft: T) => void) {
|
||||
this.value = create(this.value, fn);
|
||||
}
|
||||
}
|
||||
|
||||
function createReactiveProxy<T extends Entity>(entitySignal: Signal<T>): T {
|
||||
return new Proxy({} as T, {
|
||||
get(_target, prop) {
|
||||
const current = entitySignal.value;
|
||||
const value = current[prop as keyof T];
|
||||
if (typeof value === 'function') {
|
||||
return value.bind(current);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
set(_target, prop, value) {
|
||||
const current = entitySignal.value;
|
||||
entitySignal.value = { ...current, [prop]: value };
|
||||
return true;
|
||||
},
|
||||
ownKeys(_target) {
|
||||
return Reflect.ownKeys(entitySignal.value);
|
||||
},
|
||||
getOwnPropertyDescriptor(_target, prop) {
|
||||
return Reflect.getOwnPropertyDescriptor(entitySignal.value, prop);
|
||||
},
|
||||
});
|
||||
export function entity<T = undefined>(id: string, t?: T, options?: SignalOptions<T>) {
|
||||
return new Entity<T>(id, t, options);
|
||||
}
|
||||
|
||||
function createReactiveAccessor<T extends Entity>(id: string, entitySignal: Signal<T>): EntityAccessor<T> {
|
||||
const proxy = createReactiveProxy(entitySignal);
|
||||
return {
|
||||
id,
|
||||
get value() {
|
||||
return proxy;
|
||||
},
|
||||
set value(value: T) {
|
||||
entitySignal.value = value;
|
||||
}
|
||||
} as EntityAccessor<T>;
|
||||
export type EntityCollection<T> = {
|
||||
collection: Signal<Record<string, Entity<T>>>;
|
||||
remove(...ids: string[]): void;
|
||||
add(...entities: (T & {id: string})[]): void;
|
||||
get(id: string): Entity<T>;
|
||||
}
|
||||
|
||||
export function createEntityCollection<T extends Entity>() {
|
||||
const collection = signal({} as Record<string, Signal<T>>);
|
||||
export function createEntityCollection<T>(): EntityCollection<T> {
|
||||
const collection = signal({} as Record<string, Entity<T>>);
|
||||
const remove = (...ids: string[]) => {
|
||||
collection.value = Object.fromEntries(
|
||||
Object.entries(collection.value).filter(([id]) => !ids.includes(id)),
|
||||
Object.entries(collection.value).filter(([id]) => !ids.includes(id)),
|
||||
);
|
||||
};
|
||||
|
||||
const add = (...entities: T[]) => {
|
||||
|
||||
const add = (...entities: (T & {id: string})[]) => {
|
||||
collection.value = {
|
||||
...collection.value,
|
||||
...Object.fromEntries(entities.map((entity) => [entity.id, signal(entity)])),
|
||||
...Object.fromEntries(entities.map((e) => [e.id, entity(e.id, e)])),
|
||||
};
|
||||
};
|
||||
|
||||
const get = (id: string): EntityAccessor<T> => {
|
||||
const entitySignal = collection.value[id];
|
||||
if (!entitySignal) {
|
||||
return {
|
||||
id,
|
||||
get value() {
|
||||
return undefined as unknown as T;
|
||||
},
|
||||
set value(_value: T) {}
|
||||
} as EntityAccessor<T>;
|
||||
}
|
||||
return createReactiveAccessor(id, entitySignal);
|
||||
}
|
||||
|
||||
|
||||
const get = (id: string) => collection.value[id];
|
||||
|
||||
return {
|
||||
collection,
|
||||
remove,
|
||||
|
||||
Reference in New Issue
Block a user