-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAbstractComponent.js
More file actions
137 lines (116 loc) · 4.2 KB
/
AbstractComponent.js
File metadata and controls
137 lines (116 loc) · 4.2 KB
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
const IDENTITY_FUNCTION = input => input;
export default class AbstractComponent extends HTMLElement {
static get is() {
throw new TypeError('The id static property getter is abstract and must be overridden');
}
static get observedAttributes() {
return [];
}
constructor(dataSource, elementReferencesProvider, renderer, renderingRootProvider) {
super();
const instance = this;
const props = {};
const attributes = this.attributes;
for (let i = 0, length = attributes.length; i < length; i++) {
const attribute = attributes.item(i);
props[attribute.name] = attribute.value;
}
this._AbstractComponent = {
mounted: false,
props,
data: null,
dataFragments: null,
elementReferencesProvider,
renderer,
renderingRootProvider,
dataChangeObserver: () => {
this._AbstractComponent.update(this._AbstractComponent.props);
},
update(nextProps) {
const nextDataFragments = instance._AbstractComponent.getDataFragments();
const dataFragmentsChanged = nextDataFragments.some(
(fragment, index) => fragment !== instance._AbstractComponent.dataFragments[index],
);
const privates = instance._AbstractComponent;
const nextData = dataFragmentsChanged ? privates.getData(nextDataFragments) : privates.data;
instance.componentWillReceiveProps(nextProps, nextData);
if (!instance.shouldComponentUpdate(nextProps, nextData)) {
return;
}
instance.componentWillUpdate(nextProps, nextData);
const prevProps = instance._AbstractComponent.props;
const prevData = instance._AbstractComponent.data;
instance._AbstractComponent.props = nextProps;
instance._AbstractComponent.dataFragments = nextDataFragments;
instance._AbstractComponent.data = nextData;
instance._AbstractComponent.render();
instance.componentDidUpdate(prevProps, prevData);
},
render() {
const ui = instance.render();
renderer(instance, renderingRootProvider, ui);
},
getDataFragments() {
const rawData = dataSource.getData();
return instance.dataSelectors.map(
selector => selector(rawData),
);
},
getData(dataFragments) {
return Object.assign({}, ...dataFragments);
},
};
dataSource.addListener(this._AbstractComponent.dataChangeObserver);
}
get props() {
return this._AbstractComponent.props;
}
get data() {
return this._AbstractComponent.data;
}
get elements() {
return this._AbstractComponent.elementReferencesProvider(
this,
this._AbstractComponent.renderingRootProvider,
this._AbstractComponent.props,
this._AbstractComponent.data,
);
}
get dataSelectors() {
return [IDENTITY_FUNCTION];
}
render() {
throw new TypeError('The render method is abstract and must be overridden');
}
componentDidMount() {}
componentWillReceiveProps(nextProps, nextData) {}
shouldComponentUpdate(nextProps, nextData) {
return this._AbstractComponent.props !== nextProps || this._AbstractComponent.data !== nextData;
}
componentWillUpdate(nextProps, nextData) {}
componentDidUpdate(prevProps, prevData) {}
componentDidUnmount() {}
connectedCallback() {
this._AbstractComponent.dataFragments = this._AbstractComponent.getDataFragments();
this._AbstractComponent.data = this._AbstractComponent.getData(this._AbstractComponent.dataFragments);
this._AbstractComponent.mounted = true;
this._AbstractComponent.render();
this.componentDidMount();
}
disconnectedCallback() {
this._AbstractComponent.mounted = false;
this._AbstractComponent.dataSource.removeListener(this._AbstractComponent.dataChangeObserver);
this.componentDidUnmount();
}
attributeChangedCallback(attributeName, oldValue, newValue, namespace) {
if (!this._AbstractComponent.mounted) {
this.props[`${namespace ? `${namespace}:` : ''}${attributeName}`] = newValue;
return;
}
const nextProps = {
...this._AbstractComponent.props,
[`${namespace ? `${namespace}:` : ''}${attributeName}`]: newValue,
};
this._AbstractComponent.update(nextProps);
}
}