-
Notifications
You must be signed in to change notification settings - Fork 92
/
create-context.ts
88 lines (67 loc) 路 2.13 KB
/
create-context.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import { ComponentConstructor, ComponentCreator } from './component';
import { contextEvent } from './symbols';
import { useContext } from './use-context';
interface ConsumerProps<T> {
render: (value: T) => unknown,
}
interface Creator {
<T>(defaultValue: T): Context<T>;
}
interface Context<T> {
Provider: ComponentConstructor<{}>;
Consumer: ComponentConstructor<ConsumerProps<T>>;
defaultValue: T;
}
interface ContextDetail<T> {
Context: Context<T>;
callback: (value: T) => void;
// These properties will not exist if a context consumer lacks a provider
value: T;
unsubscribe?: (this: Context<T>) => void;
}
function makeContext(component: ComponentCreator): Creator {
return <T>(defaultValue: T): Context<T> => {
const Context: Context<T> = {
Provider: class extends HTMLElement {
listeners: Set<(value: T) => void>;
_value!: T;
constructor() {
super();
this.listeners = new Set();
this.addEventListener(contextEvent, this);
}
disconnectedCallback(): void {
this.removeEventListener(contextEvent, this);
}
handleEvent(event: CustomEvent<ContextDetail<T>>): void {
const { detail } = event;
if (detail.Context === Context) {
detail.value = this.value;
detail.unsubscribe = this.unsubscribe.bind(this, detail.callback);
this.listeners.add(detail.callback);
event.stopPropagation();
}
}
unsubscribe(callback: (value: T) => void): void {
this.listeners.delete(callback);
}
set value(value: T) {
this._value = value;
for (let callback of this.listeners) {
callback(value);
}
}
get value(): T {
return this._value;
}
},
Consumer: component<ConsumerProps<T>>(function({ render }: ConsumerProps<T>): unknown {
const context = useContext(Context);
return render(context);
}),
defaultValue,
};
return Context;
};
}
export { makeContext, Creator as ContextCreator, Context, ContextDetail };