Skip to content

Commit 4fbe2f3

Browse files
committed
enhance shadow-objects creation api
- add useProperties() - add useResource() - allow signal as input to provideContext() and provideGlobalContext()
1 parent 8876a45 commit 4fbe2f3

2 files changed

Lines changed: 75 additions & 12 deletions

File tree

packages/shadow-objects/src/in-the-dark/Kernel.ts

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
createMemo,
66
createSignal,
77
destroySignal,
8+
isSignal,
89
link,
910
Signal,
1011
type CompareFunc,
@@ -345,16 +346,35 @@ export class Kernel {
345346

346347
const propertyReaders = new Map<string, SignalReader<any>>();
347348

349+
const getUseProperty = <T = any>(name: string, compare?: CompareFunc<T>): SignalReader<T> => {
350+
let propReader = propertyReaders.get(name);
351+
if (propReader === undefined) {
352+
propReader = createSignal<any>(undefined, compare ? {compare} : undefined).get;
353+
propertyReaders.set(name, propReader);
354+
const con = link(entry.entity.getPropertyReader(name), propReader);
355+
unsubscribeSecondary.add(con.destroy.bind(con));
356+
}
357+
return propReader;
358+
};
359+
348360
const shadowObject = eventize(
349361
new constructor({
350362
entity: entry.entity,
351363

352-
provideContext<T = unknown>(name: string | symbol, initialValue?: T, isEqual?: CompareFunc<T>) {
364+
provideContext<T = unknown>(name: string | symbol, sourceOrInitialValue?: T | SignalReader<T>, isEqual?: CompareFunc<T>) {
353365
let ctxProvider = contextProviders.get(name);
354366

355367
if (ctxProvider == null) {
368+
const isSig = isSignal(sourceOrInitialValue);
369+
const initialValue = isSig ? undefined : (sourceOrInitialValue as T);
370+
356371
ctxProvider = createSignal(initialValue, isEqual ? {compare: isEqual} : undefined);
357372

373+
if (isSig) {
374+
const ln = link(sourceOrInitialValue as SignalReader<T>, ctxProvider);
375+
unsubscribeSecondary.add(ln.destroy.bind(ln));
376+
}
377+
358378
const ln = link(ctxProvider, entry.entity.provideContext(name));
359379
unsubscribeSecondary.add(ln.destroy.bind(ln));
360380
contextProviders.set(name, ctxProvider);
@@ -363,12 +383,20 @@ export class Kernel {
363383
return ctxProvider;
364384
},
365385

366-
provideGlobalContext<T = unknown>(name: string | symbol, initialValue?: T, isEqual?: CompareFunc<T>) {
386+
provideGlobalContext<T = unknown>(name: string | symbol, sourceOrInitialValue?: T | SignalReader<T>, isEqual?: CompareFunc<T>) {
367387
let ctxProvider = contextRootProviders.get(name);
368388

369389
if (ctxProvider == null) {
390+
const isSig = isSignal(sourceOrInitialValue);
391+
const initialValue = isSig ? undefined : (sourceOrInitialValue as T);
392+
370393
ctxProvider = createSignal(initialValue, isEqual ? {compare: isEqual} : undefined);
371394

395+
if (isSig) {
396+
const ln = link(sourceOrInitialValue as SignalReader<T>, ctxProvider);
397+
unsubscribeSecondary.add(ln.destroy.bind(ln));
398+
}
399+
372400
const ln = link(ctxProvider, entry.entity.provideGlobalContext(name));
373401
unsubscribeSecondary.add(ln.destroy.bind(ln));
374402
contextRootProviders.set(name, ctxProvider);
@@ -399,15 +427,39 @@ export class Kernel {
399427
return ctxReader;
400428
},
401429

402-
useProperty<T = any>(name: string, compare?: CompareFunc<T>): SignalReader<T> {
403-
let propReader = propertyReaders.get(name);
404-
if (propReader === undefined) {
405-
propReader = createSignal<any>(undefined, compare ? {compare} : undefined).get;
406-
propertyReaders.set(name, propReader);
407-
const con = link(entry.entity.getPropertyReader(name), propReader);
408-
unsubscribeSecondary.add(con.destroy.bind(con));
430+
useProperty: getUseProperty,
431+
432+
useProperties<K extends string>(props: Record<K, string>): Record<K, SignalReader<any>> {
433+
const result = {} as Record<K, SignalReader<any>>;
434+
for (const key in props) {
435+
if (Object.prototype.hasOwnProperty.call(props, key)) {
436+
result[key] = getUseProperty(props[key]);
437+
}
409438
}
410-
return propReader;
439+
return result;
440+
},
441+
442+
useResource<T>(factory: () => T | undefined, cleanup?: (resource: T) => void): Signal<T | undefined> {
443+
const resourceSignal = createSignal<T | undefined>(undefined);
444+
unsubscribeSecondary.add(() => destroySignal(resourceSignal));
445+
446+
const effect = createEffect(() => {
447+
const resource = factory();
448+
resourceSignal.set(resource);
449+
450+
if (resource !== undefined && cleanup) {
451+
return () => {
452+
cleanup(resource);
453+
resourceSignal.set(undefined);
454+
};
455+
}
456+
return () => {
457+
resourceSignal.set(undefined);
458+
};
459+
});
460+
unsubscribeSecondary.add(effect.destroy);
461+
462+
return resourceSignal;
411463
},
412464

413465
createEffect(...args: Parameters<typeof createEffect>): ReturnType<typeof createEffect> {

packages/shadow-objects/src/types.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,23 @@ export type EntityApi = Pick<
9696
export interface ShadowObjectParams {
9797
entity: EntityApi;
9898

99-
provideContext<T = unknown>(name: string | symbol, initialValue?: T, isEqual?: CompareFunc<T>): Signal<T>;
100-
provideGlobalContext<T = unknown>(name: string | symbol, initialValue?: T, isEqual?: CompareFunc<T>): Signal<T>;
99+
provideContext<T = unknown>(
100+
name: string | symbol,
101+
sourceOrInitialValue?: T | SignalReader<T>,
102+
isEqual?: CompareFunc<T>,
103+
): Signal<T>;
104+
provideGlobalContext<T = unknown>(
105+
name: string | symbol,
106+
sourceOrInitialValue?: T | SignalReader<T>,
107+
isEqual?: CompareFunc<T>,
108+
): Signal<T>;
101109

102110
useContext<T = any>(name: string | symbol, isEqual?: CompareFunc<T>): SignalReader<T>;
103111
useParentContext<T = any>(name: string | symbol, isEqual?: CompareFunc<T>): SignalReader<T>;
104112
useProperty<T = any>(name: string, isEqual?: CompareFunc<T>): SignalReader<T>;
113+
useProperties<K extends string>(props: Record<K, string>): Record<K, SignalReader<any>>;
114+
115+
useResource<T>(factory: () => T | undefined, cleanup?: (resource: T) => void): Signal<T | undefined>;
105116

106117
createEffect(...args: Parameters<typeof createEffect>): ReturnType<typeof createEffect>;
107118
createSignal<T = unknown>(...args: Parameters<typeof createSignal<T>>): ReturnType<typeof createSignal<T>>;

0 commit comments

Comments
 (0)