-
Notifications
You must be signed in to change notification settings - Fork 5
/
cosmoz-omnitable-templatize-mixin.html
212 lines (188 loc) · 5.93 KB
/
cosmoz-omnitable-templatize-mixin.html
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
<script>
(() => {
'use strict';
/**
* Prepares instances of templates and re-uses recycled instances.
* @polymer
* @mixinFunction
*/
Cosmoz.OmnitableTemplatizeMixin = Polymer.dedupingMixin(base => class extends base {
constructor() {
super();
this._templateCtors = {};
this._templateInstances = [];
this._recycledInstance = [];
}
/**
* Creates a new template instance of the required type.
*
* The light and shadow DOM is searched for templates matching the selector
* `template.<type>`.
*
* @param {String} type the type of the template
* @param {Object} props the instance's properties
* @return {TemplateInstance} the template instance
*/
getTemplateInstance(type, props) {
// first search in the recycled instances pool
let instance = this._recycledInstance.find(i => i._type === type);
if (instance) {
// reuse recycled instance
instance = this._reuseInstance(instance, props);
} else {
// if no recycled instance is available, create a new one
instance = this._createInstance(type, props);
}
// add to active instances pool
this._templateInstances.push(instance);
return instance;
}
/**
* Marks an instance for re-use.
*
* @param {TemplateInstance} instance an instance
* @return {TemplateInstance} the detached instance
*/
recycleInstance(instance) {
const index = this._templateInstances.indexOf(instance);
if (index < 0) {
return instance;
}
// remove from active instances pool
this._templateInstances.splice(index, 1);
this._recycledInstance.push(instance);
return instance;
}
/**
* Tests whether the instance is marked for re-use.
* @param {TemplateInstance} instance an instance
* @return {Boolean} true if instance is recycled
*/
isRecycledInstance(instance) {
return this._recycledInstance.includes(instance);
}
/**
* Cleans up references to recycled instances when the element is detached.
*
* @return {void}
*/
disconnectedCallback() {
super.disconnectedCallback();
this._recycledInstance = [];
}
/**
* Reuses an already created instance.
*
* @param {TemplateInstance} instance an instance
* @param {Object} props the instances properties
* @return {TemplateInstance} the instance
*/
_reuseInstance(instance, props) {
// remove from recycled pool
this._recycledInstance.splice(this._recycledInstance.indexOf(instance), 1);
// update props
this._forwardProperties(instance, props);
// add children to DOM
const {children, root} = instance;
for (let i = 0; i < children.length; i++) {
const child = children[i];
root.appendChild(child);
}
return instance;
}
/**
* Creates a new instance of the required type.
*
* @param {String} type the type of the template
* @param {Object} props the instance's properies
* @return {TemplateInstance} the instance
*/
_createInstance(type, props) {
// initialize a new instance of the template
const instance = new (this._getTemplateCtor(type))(props);
// store the type of the template on the instance, for future reference
instance._type = type;
return instance;
}
/**
* Searches for a template node of the required type and templatizes it.
*
* @see https://polymer-library.polymer-project.org/2.0/api/namespaces/Polymer.Templatize
* @param {String} type the type of the template
* @return {TemplateInstanceBase} the templatized template
*/
_getTemplateCtor(type) {
if (this._templateCtors[type]) {
return this._templateCtors[type];
}
// search for the template in the light and shadow DOM
const template = this.querySelector('template.' + type) || this.root.querySelector('template.' + type);
// throw error if template does not exist
if (!template) {
throw new Error('No template of type ' + type + ' found.');
}
// templatize the DOM template node
this._templateCtors[type] = Polymer.Templatize.templatize(template, this, {
// populate events with event.model
parentModel: true,
// forward host property changes to the instances
forwardHostProp: this._forwardHostProp(type)
});
return this._templateCtors[type];
}
/**
* Generates a function that forwards properties to instances of a certain type.
*
* @param {String} type the type of the template
* @return {Function} a function that forwards props to instances
*/
_forwardHostProp(type) {
/**
* Forwards properties to instances.
*
* @this Cosmoz.OmnitableTemplatizeMixin
* @param {String} prop the host property
* @param {String} value the properties' value
* @return {void}
*/
return function (prop, value) {
this._templateInstances
.filter(i => i._type === type)
.forEach(instance => {
instance.forwardHostProp(prop, value);
});
};
}
/**
* Forward one property.
* @param {object} instance Instance.
* @param {string} name Property name.
* @param {any} value Property value.
* @param {boolean} flush Whether to flush properties.
* @returns {void}
*/
_forwardProperty(instance, name, value, flush = false) {
instance._setPendingProperty(name, value);
if (flush && instance._flushProperties) {
instance._flushProperties(true);
}
}
/**
* Forward many properties.
* @param {object} instance Instance.
* @param {object} props Properties to forward.
* @param {boolean} flush Whether to flush properties.
* @returns {void}
*/
_forwardProperties(instance, props = {}, flush = true) {
Object.keys(props).forEach(key => this._forwardProperty(instance, key, props[key]));
if (flush && instance._flushProperties) {
instance._flushProperties(true);
}
}
});
Cosmoz.OmnitableTemplatizeMixin.HEADER_TEMPLATE = 'header';
Cosmoz.OmnitableTemplatizeMixin.CELL_TEMPLATE = 'cell';
Cosmoz.OmnitableTemplatizeMixin.EDIT_TEMPLATE = 'edit-cell';
})();
</script>