-
Notifications
You must be signed in to change notification settings - Fork 1
/
create-context.js
116 lines (88 loc) · 2.67 KB
/
create-context.js
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
const includes = (arr, item) => arr.indexOf(item) !== -1;
const contexts = new WeakMap();
class BaseProvider extends HTMLElement {
constructor() {
super();
const { defaultValue, eventName } = this.constructor;
this.__listeners = [];
this.value = defaultValue;
this.addEventListener(eventName, (event) => {
// eslint-disable-next-line no-param-reassign
event.detail.unsubscribe = this.subscribe(event.detail.callback);
event.stopPropagation();
});
}
connectedCallback() {
if (this.connectedBefore) {
throw new Error('provider has to be static, change the value property for dynamic behavior');
}
this.connectedBefore = true;
}
subscribe(callback) {
if (!includes(this.__listeners, callback)) {
this.__listeners.push(callback);
} else {
throw new Error(`consumer ${callback} already subscribed`, this);
}
callback(this.value);
return () => this.__listeners.splice(this.__listeners.indexOf(callback), 1);
}
set value(context) {
this.__listeners.forEach((callback) => {
callback(context);
});
contexts.set(this, context);
}
get value() {
return contexts.get(this);
}
}
export default function createContext(contextName, defaultValue) {
const eventName = `request-context-${contextName}`;
class Provider extends BaseProvider {
static get defaultValue() {
return defaultValue;
}
static get eventName() {
return eventName;
}
}
const consumer = baseElement => class Consumer extends baseElement {
constructor() {
super();
this._onContextChange = this._onContextChange.bind(this);
}
connectedCallback() {
const event = new CustomEvent(eventName, {
// we will provide provider here
detail: { callback: this._onContextChange },
bubbles: true,
cancelable: true,
// Has to pass shadow dom boundaries
// for browsers not supporting shadowDom and less mental overhead in usage
composed: true,
});
this.dispatchEvent(event);
this.__unsubscribeContext = event.detail.unsubscribe;
if (!this.__unsubscribeContext) {
throw new Error(`no provider found for ${contextName} consumer`, this);
}
if (super.connectedCallback) {
super.connectedCallback();
}
}
disconnectedCallback() {
if (this.__unsubscribeContext) {
this.__unsubscribeContext();
}
if (super.disconnectedCallback) {
super.disconnectedCallback();
}
}
};
return {
Provider,
consumer: dedupingMixin(consumer),
};
}