p-elements-core 2.0.14 → 2.1.0-rc10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/p-elements-core.js +1 -72
  2. package/package.json +56 -56
  3. package/readme.md +386 -0
  4. package/typings/custom-element-controller.d.ts +18 -0
  5. package/typings/custom-element-controller.d.ts.map +1 -0
  6. package/typings/custom-element.d.ts +140 -0
  7. package/typings/custom-element.d.ts.map +1 -0
  8. package/typings/decorators/bind.d.ts +6 -0
  9. package/typings/decorators/bind.d.ts.map +1 -0
  10. package/typings/decorators/custom-element-config.d.ts +8 -0
  11. package/typings/decorators/custom-element-config.d.ts.map +1 -0
  12. package/typings/decorators/property.d.ts +213 -0
  13. package/typings/decorators/property.d.ts.map +1 -0
  14. package/typings/decorators/query.d.ts +2 -0
  15. package/typings/decorators/query.d.ts.map +1 -0
  16. package/typings/decorators/render-property-on-set.d.ts +2 -0
  17. package/typings/decorators/render-property-on-set.d.ts.map +1 -0
  18. package/typings/maquette/cache.d.ts +10 -0
  19. package/typings/maquette/cache.d.ts.map +1 -0
  20. package/typings/maquette/dom.d.ts +60 -0
  21. package/typings/maquette/dom.d.ts.map +1 -0
  22. package/typings/maquette/h.d.ts +35 -0
  23. package/typings/maquette/h.d.ts.map +1 -0
  24. package/typings/maquette/index.d.ts +12 -0
  25. package/typings/maquette/index.d.ts.map +1 -0
  26. package/typings/maquette/interfaces.d.ts +475 -0
  27. package/typings/maquette/interfaces.d.ts.map +1 -0
  28. package/typings/maquette/jsx.d.ts +6 -0
  29. package/typings/maquette/jsx.d.ts.map +1 -0
  30. package/typings/maquette/mapping.d.ts +14 -0
  31. package/typings/maquette/mapping.d.ts.map +1 -0
  32. package/typings/maquette/projection.d.ts +9 -0
  33. package/typings/maquette/projection.d.ts.map +1 -0
  34. package/typings/maquette/projector.d.ts +27 -0
  35. package/typings/maquette/projector.d.ts.map +1 -0
  36. package/typings/p-elements-core.d.ts +225 -0
  37. package/typings/p-elements-core.d.ts.map +1 -0
  38. package/.editorconfig +0 -17
  39. package/.gitlab-ci.yml +0 -16
  40. package/dist/p-elements-core-modern.js +0 -72
  41. package/dist/sample.js +0 -20
  42. package/index.html +0 -171
  43. package/karma.conf.js +0 -32
  44. package/p-elements-core.d.ts +0 -140
  45. package/screen.css +0 -16
  46. package/src/bar.css +0 -3
  47. package/src/cache.ts +0 -35
  48. package/src/commonjs.js +0 -182
  49. package/src/custom-element.ts +0 -185
  50. package/src/custom-inline-style-element.ts +0 -42
  51. package/src/custom-style-element.ts +0 -27
  52. package/src/decorators/bind.ts +0 -40
  53. package/src/decorators/custom-element-config.ts +0 -17
  54. package/src/decorators/property-render-on-set.ts +0 -49
  55. package/src/decorators/reflect-to-attribute.ts +0 -51
  56. package/src/dom.ts +0 -100
  57. package/src/h.ts +0 -93
  58. package/src/index.tsx +0 -84
  59. package/src/interfaces.ts +0 -455
  60. package/src/mapping.ts +0 -55
  61. package/src/projection.ts +0 -537
  62. package/src/projector.ts +0 -168
  63. package/src/sample.css +0 -19
  64. package/src/sample.spec.ts +0 -112
  65. package/src/sample.tsx +0 -187
  66. package/src/tsconfig.json +0 -16
  67. package/theme.css +0 -12
  68. package/tsconfig.json +0 -17
  69. package/tslint.json +0 -33
  70. package/webpack.config.js +0 -100
  71. package/webpack.config.karma.js +0 -35
package/src/projection.ts DELETED
@@ -1,537 +0,0 @@
1
- /**
2
- * Exports here are NOT re-exported to maquette
3
- */
4
- import { Projection, ProjectionOptions, VNode, VNodeProperties } from './interfaces';
5
-
6
- /* tslint:disable no-http-string */
7
- const NAMESPACE_W3 = 'http://www.w3.org/';
8
- /* tslint:enable no-http-string */
9
- const NAMESPACE_SVG = `${NAMESPACE_W3}2000/svg`;
10
- const NAMESPACE_XLINK = `${NAMESPACE_W3}1999/xlink`;
11
-
12
- let emptyArray = <VNode[]>[];
13
-
14
- export let extend = <T>(base: T, overrides: any): T => {
15
- let result = {} as any;
16
- Object.keys(base).forEach((key) => {
17
- result[key] = (base as any)[key];
18
- });
19
- if (overrides) {
20
- Object.keys(overrides).forEach((key) => {
21
- result[key] = overrides[key];
22
- });
23
- }
24
- return result;
25
- };
26
-
27
- let same = (vnode1: VNode, vnode2: VNode) => {
28
- if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
29
- return false;
30
- }
31
- if (vnode1.properties && vnode2.properties) {
32
- if (vnode1.properties.key !== vnode2.properties.key) {
33
- return false;
34
- }
35
- return vnode1.properties.bind === vnode2.properties.bind;
36
- }
37
- return !vnode1.properties && !vnode2.properties;
38
- };
39
-
40
- let checkStyleValue = (styleValue: Object) => {
41
- if (typeof styleValue !== 'string') {
42
- throw new Error('Style values must be strings');
43
- }
44
- };
45
-
46
- let findIndexOfChild = (children: VNode[], sameAs: VNode, start: number) => {
47
- if (sameAs.vnodeSelector !== '') {
48
- // Never scan for text-nodes
49
- for (let i = start; i < children.length; i++) {
50
- if (same(children[i], sameAs)) {
51
- return i;
52
- }
53
- }
54
- }
55
- return -1;
56
- };
57
-
58
- let checkDistinguishable = (childNodes: VNode[], indexToCheck: number, parentVNode: VNode, operation: string) => {
59
- let childNode = childNodes[indexToCheck];
60
- if (childNode.vnodeSelector === '') {
61
- return; // Text nodes need not be distinguishable
62
- }
63
- let properties = childNode.properties;
64
- let key = properties ? (properties.key === undefined ? properties.bind : properties.key) : undefined;
65
- if (!key) { // A key is just assumed to be unique
66
- for (let i = 0; i < childNodes.length; i++) {
67
- if (i !== indexToCheck) {
68
- let node = childNodes[i];
69
- if (same(node, childNode)) {
70
- throw new Error(`${parentVNode.vnodeSelector} had a ${childNode.vnodeSelector} child ${
71
- operation === 'added' ? operation : 'removed'
72
- }, but there is now more than one. You must add unique key properties to make them distinguishable.`);
73
- }
74
- }
75
- }
76
- }
77
- };
78
-
79
- let nodeAdded = (vNode: VNode) => {
80
- if (vNode.properties) {
81
- let enterAnimation = vNode.properties.enterAnimation;
82
- if (enterAnimation) {
83
- enterAnimation(vNode.domNode as Element, vNode.properties);
84
- }
85
- }
86
- };
87
-
88
- let removedNodes: VNode[] = [];
89
- let requestedIdleCallback = false;
90
-
91
- let visitRemovedNode = (node: VNode) => {
92
- (node.children || []).forEach(visitRemovedNode);
93
-
94
- if (node.properties && node.properties.afterRemoved) {
95
- node.properties.afterRemoved.apply(
96
- node.properties.bind || node.properties,
97
- [<Element>node.domNode]
98
- );
99
- }
100
- };
101
-
102
- let processPendingNodeRemovals = (): void => {
103
- requestedIdleCallback = false;
104
-
105
- removedNodes.forEach(visitRemovedNode);
106
- removedNodes.length = 0;
107
- };
108
-
109
- let scheduleNodeRemoval = (vNode: VNode): void => {
110
- removedNodes.push(vNode);
111
-
112
- if (!requestedIdleCallback) {
113
- requestedIdleCallback = true;
114
- if (typeof window !== 'undefined' && 'requestIdleCallback' in window) {
115
- (window as any).requestIdleCallback(processPendingNodeRemovals, { timeout: 16 });
116
- } else {
117
- setTimeout(processPendingNodeRemovals, 16);
118
- }
119
- }
120
- };
121
-
122
- let nodeToRemove = (vNode: VNode) => {
123
- let domNode: Node = vNode.domNode!;
124
- if (vNode.properties) {
125
- let exitAnimation = vNode.properties.exitAnimation;
126
- if (exitAnimation) {
127
- (domNode as HTMLElement).style.pointerEvents = 'none';
128
- let removeDomNode = () => {
129
- if (domNode.parentNode) {
130
- domNode.parentNode.removeChild(domNode);
131
- scheduleNodeRemoval(vNode);
132
- }
133
- };
134
- exitAnimation(domNode as Element, removeDomNode, vNode.properties);
135
- return;
136
- }
137
- }
138
- if (domNode.parentNode) {
139
- domNode.parentNode.removeChild(domNode);
140
- scheduleNodeRemoval(vNode);
141
- }
142
- };
143
-
144
- let setProperties = (domNode: Node, properties: VNodeProperties | undefined, projectionOptions: ProjectionOptions) => {
145
- if (!properties) {
146
- return;
147
- }
148
- let eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
149
- let propNames = Object.keys(properties);
150
- let propCount = propNames.length;
151
- for (let i = 0; i < propCount; i++) {
152
- let propName = propNames[i];
153
- let propValue = properties[propName];
154
- if (propName === 'className') {
155
- throw new Error('Property "className" is not supported, use "class".');
156
- } else if (propName === 'class') {
157
- (propValue as string).split(/\s+/).forEach(token => (domNode as Element).classList.add(token));
158
- } else if (propName === 'classes') {
159
- // object with string keys and boolean values
160
- let classNames = Object.keys(propValue);
161
- let classNameCount = classNames.length;
162
- for (let j = 0; j < classNameCount; j++) {
163
- let className = classNames[j];
164
- if (propValue[className]) {
165
- (domNode as Element).classList.add(className);
166
- }
167
- }
168
- } else if (propName === 'styles') {
169
- // object with string keys and string (!) values
170
- let styleNames = Object.keys(propValue);
171
- let styleCount = styleNames.length;
172
- for (let j = 0; j < styleCount; j++) {
173
- let styleName = styleNames[j];
174
- let styleValue = propValue[styleName];
175
- if (styleValue) {
176
- checkStyleValue(styleValue);
177
- projectionOptions.styleApplyer!(<HTMLElement>domNode, styleName, styleValue);
178
- }
179
- }
180
- } else if (propName !== 'key' && propValue !== null && propValue !== undefined) {
181
- let type = typeof propValue;
182
- if (type === 'function') {
183
- if (propName.lastIndexOf('on', 0) === 0) { // lastIndexOf(,0)===0 -> startsWith
184
- if (eventHandlerInterceptor) {
185
- propValue = eventHandlerInterceptor(propName, propValue, domNode, properties); // intercept eventhandlers
186
- }
187
- if (propName === 'oninput') {
188
- /* tslint:disable no-this-keyword no-invalid-this only-arrow-functions no-void-expression */
189
- (function() {
190
- // record the evt.target.value, because IE and Edge sometimes do a requestAnimationFrame between changing value and running oninput
191
- let oldPropValue = propValue;
192
- propValue = function(this: HTMLElement, evt: Event) {
193
- oldPropValue.apply(this, [evt]);
194
- (evt.target as any)['oninput-value'] = (evt.target as HTMLInputElement).value; // may be HTMLTextAreaElement as well
195
- };
196
- }());
197
- /* tslint:enable */
198
- }
199
- (domNode as any)[propName] = propValue;
200
- }
201
- } else if (type === 'string' && propName !== 'value' && propName !== 'innerHTML') {
202
- if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
203
- (domNode as Element).setAttributeNS(NAMESPACE_XLINK, propName, propValue);
204
- } else {
205
- (domNode as Element).setAttribute(propName, propValue);
206
- }
207
- } else {
208
- (domNode as any)[propName] = propValue;
209
- }
210
- }
211
- }
212
- };
213
-
214
- let addChildren = (domNode: Node, children: VNode[] | undefined, projectionOptions: ProjectionOptions) => {
215
- if (!children) {
216
- return;
217
- }
218
- for (let child of children) {
219
- createDom(child, domNode, undefined, projectionOptions);
220
- }
221
- };
222
-
223
- export let initPropertiesAndChildren = (domNode: Node, vnode: VNode, projectionOptions: ProjectionOptions) => {
224
- addChildren(domNode, vnode.children, projectionOptions); // children before properties, needed for value property of <select>.
225
- if (vnode.text) {
226
- domNode.textContent = vnode.text;
227
- }
228
- setProperties(domNode, vnode.properties, projectionOptions);
229
- if (vnode.properties && vnode.properties.afterCreate) {
230
- vnode.properties.afterCreate.apply(
231
- vnode.properties.bind || vnode.properties,
232
- [domNode as Element, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children]
233
- );
234
- }
235
- };
236
-
237
- export let createDom = (
238
- vnode: VNode,
239
- parentNode: Node,
240
- insertBefore: Node | null | undefined,
241
- projectionOptions: ProjectionOptions
242
- ): void => {
243
- let domNode: Node | undefined;
244
- let start = 0;
245
- let vnodeSelector = vnode.vnodeSelector;
246
- let doc = parentNode.ownerDocument;
247
- if (vnodeSelector === '') {
248
- domNode = vnode.domNode = doc.createTextNode(vnode.text!);
249
- if (insertBefore !== undefined) {
250
- parentNode.insertBefore(domNode, insertBefore);
251
- } else {
252
- parentNode.appendChild(domNode);
253
- }
254
- } else {
255
- for (let i = 0; i <= vnodeSelector.length; ++i) {
256
- let c = vnodeSelector.charAt(i);
257
- if (i === vnodeSelector.length || c === '.' || c === '#') {
258
- let type = vnodeSelector.charAt(start - 1);
259
- let found = vnodeSelector.slice(start, i);
260
- if (type === '.') {
261
- (domNode! as HTMLElement).classList.add(found);
262
- } else if (type === '#') {
263
- (domNode! as Element).id = found;
264
- } else {
265
- if (found === 'svg') {
266
- projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
267
- }
268
- if (projectionOptions.namespace !== undefined) {
269
- domNode = vnode.domNode = doc.createElementNS(projectionOptions.namespace, found);
270
- } else {
271
- // support custom element is attribute
272
- let n: Node;
273
- if (vnode.properties && (vnode as any).properties.is) {
274
- // for ios9
275
- let tempElement = document.createElement('div');
276
- tempElement.style.display = 'none';
277
- document.body.appendChild(tempElement);
278
- // tslint:disable-next-line:no-inner-html
279
- tempElement.innerHTML = `<${found} is="${(vnode as any).properties.is}"></${found}>`;
280
- setTimeout(() => {
281
- document.body.removeChild(tempElement);
282
- }, 10);
283
- // end for ios9
284
-
285
- n = (doc as any).createElement(found, {is: (vnode as any).properties.is});
286
- } else {
287
- n = doc.createElement(found);
288
- }
289
-
290
- domNode = vnode.domNode = (vnode.domNode || n);
291
-
292
- if (found === 'input' && vnode.properties && vnode.properties.type !== undefined) {
293
- // IE8 and older don't support setting input type after the DOM Node has been added to the document
294
- (domNode as Element).setAttribute('type', vnode.properties.type);
295
- }
296
- }
297
- if (insertBefore !== undefined) {
298
- parentNode.insertBefore(domNode, insertBefore);
299
- } else if (domNode.parentNode !== parentNode) {
300
- parentNode.appendChild(domNode);
301
- }
302
- }
303
- start = i + 1;
304
- }
305
- }
306
- initPropertiesAndChildren(domNode!, vnode, projectionOptions);
307
- }
308
- };
309
-
310
- let updateDom: (previous: VNode, vnode: VNode, projectionOptions: ProjectionOptions) => boolean;
311
-
312
- /**
313
- * Adds or removes classes from an Element
314
- * @param domNode the element
315
- * @param classes a string separated list of classes
316
- * @param on true means add classes, false means remove
317
- */
318
- let toggleClasses = (domNode: HTMLElement, classes: string | null | undefined, on: boolean) => {
319
- if (!classes) {
320
- return;
321
- }
322
- classes.split(' ').forEach(c => domNode.classList.toggle(c, on));
323
- };
324
-
325
- let updateProperties = (
326
- domNode: Node, previousProperties: VNodeProperties | undefined,
327
- properties: VNodeProperties | undefined,
328
- projectionOptions: ProjectionOptions
329
- ) => {
330
- if (!properties) {
331
- return;
332
- }
333
- let propertiesUpdated = false;
334
- let propNames = Object.keys(properties);
335
- let propCount = propNames.length;
336
- for (let i = 0; i < propCount; i++) {
337
- let propName = propNames[i];
338
- // assuming that properties will be nullified instead of missing is by design
339
- let propValue = properties[propName];
340
- let previousValue = previousProperties![propName];
341
- if (propName === 'class') {
342
- if (previousValue !== propValue) {
343
- toggleClasses(domNode as HTMLElement, previousValue, false);
344
- toggleClasses(domNode as HTMLElement, propValue, true);
345
- }
346
- } else if (propName === 'classes') {
347
- let classList = (domNode as Element).classList;
348
- let classNames = Object.keys(propValue);
349
- let classNameCount = classNames.length;
350
- for (let j = 0; j < classNameCount; j++) {
351
- let className = classNames[j];
352
- let on = !!propValue[className];
353
- let previousOn = !!previousValue[className];
354
- if (on === previousOn) {
355
- continue;
356
- }
357
- propertiesUpdated = true;
358
- if (on) {
359
- classList.add(className);
360
- } else {
361
- classList.remove(className);
362
- }
363
- }
364
- } else if (propName === 'styles') {
365
- let styleNames = Object.keys(propValue);
366
- let styleCount = styleNames.length;
367
- for (let j = 0; j < styleCount; j++) {
368
- let styleName = styleNames[j];
369
- let newStyleValue = propValue[styleName];
370
- let oldStyleValue = previousValue[styleName];
371
- if (newStyleValue === oldStyleValue) {
372
- continue;
373
- }
374
- propertiesUpdated = true;
375
- if (newStyleValue) {
376
- checkStyleValue(newStyleValue);
377
- projectionOptions.styleApplyer!(domNode as HTMLElement, styleName, newStyleValue);
378
- } else {
379
- projectionOptions.styleApplyer!(domNode as HTMLElement, styleName, '');
380
- }
381
- }
382
- } else {
383
- if (!propValue && typeof previousValue === 'string') {
384
- propValue = '';
385
- }
386
- if (propName === 'value') { // value can be manipulated by the user directly and using event.preventDefault() is not an option
387
- let domValue = (domNode as any)[propName];
388
- if (
389
- domValue !== propValue // The 'value' in the DOM tree !== newValue
390
- && ((domNode as any)['oninput-value']
391
- ? domValue === (domNode as any)['oninput-value'] // If the last reported value to 'oninput' does not match domValue, do nothing and wait for oninput
392
- : propValue !== previousValue // Only update the value if the vdom changed
393
- )
394
- ) {
395
- // The edge cases are described in the tests
396
- (domNode as any)[propName] = propValue; // Reset the value, even if the virtual DOM did not change
397
- (domNode as any)['oninput-value'] = undefined;
398
- } // else do not update the domNode, otherwise the cursor position would be changed
399
- if (propValue !== previousValue) {
400
- propertiesUpdated = true;
401
- }
402
- } else if (propValue !== previousValue) {
403
- let type = typeof propValue;
404
- if (type !== 'function' || !projectionOptions.eventHandlerInterceptor) { // Function updates are expected to be handled by the EventHandlerInterceptor
405
- if (type === 'string' && propName !== 'innerHTML') {
406
- if (projectionOptions.namespace === NAMESPACE_SVG && propName === 'href') {
407
- (domNode as Element).setAttributeNS(NAMESPACE_XLINK, propName, propValue);
408
- } else if (propName === 'role' && propValue === '') {
409
- (domNode as any).removeAttribute(propName);
410
- } else {
411
- (domNode as Element).setAttribute(propName, propValue);
412
- }
413
- } else {
414
- if ((domNode as any)[propName] !== propValue) { // Comparison is here for side-effects in Edge with scrollLeft and scrollTop
415
- (domNode as any)[propName] = propValue;
416
- }
417
- }
418
- propertiesUpdated = true;
419
- }
420
- }
421
- }
422
- }
423
- return propertiesUpdated;
424
- };
425
-
426
- let updateChildren = (
427
- vnode: VNode,
428
- domNode: Node,
429
- oldChildren: VNode[] | undefined,
430
- newChildren: VNode[] | undefined,
431
- projectionOptions: ProjectionOptions
432
- ) => {
433
- if (oldChildren === newChildren) {
434
- return false;
435
- }
436
- oldChildren = oldChildren || emptyArray;
437
- newChildren = newChildren || emptyArray;
438
- let oldChildrenLength = oldChildren.length;
439
- let newChildrenLength = newChildren.length;
440
-
441
- let oldIndex = 0;
442
- let newIndex = 0;
443
- let i: number;
444
- let textUpdated = false;
445
- while (newIndex < newChildrenLength) {
446
- let oldChild = (oldIndex < oldChildrenLength) ? oldChildren[oldIndex] : undefined;
447
- let newChild = newChildren[newIndex];
448
- if (oldChild !== undefined && same(oldChild, newChild)) {
449
- textUpdated = updateDom(oldChild, newChild, projectionOptions) || textUpdated;
450
- oldIndex++;
451
- } else {
452
- let findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
453
- if (findOldIndex >= 0) {
454
- // Remove preceding missing children
455
- for (i = oldIndex; i < findOldIndex; i++) {
456
- nodeToRemove(oldChildren[i]);
457
- checkDistinguishable(oldChildren, i, vnode, 'removed');
458
- }
459
- textUpdated = updateDom(oldChildren[findOldIndex], newChild, projectionOptions) || textUpdated;
460
- oldIndex = findOldIndex + 1;
461
- } else {
462
- // New child
463
- createDom(newChild, domNode, (oldIndex < oldChildrenLength) ? oldChildren[oldIndex].domNode : undefined, projectionOptions);
464
- nodeAdded(newChild);
465
- checkDistinguishable(newChildren, newIndex, vnode, 'added');
466
- }
467
- }
468
- newIndex++;
469
- }
470
- if (oldChildrenLength > oldIndex) {
471
- // Remove child fragments
472
- for (i = oldIndex; i < oldChildrenLength; i++) {
473
- nodeToRemove(oldChildren[i]);
474
- checkDistinguishable(oldChildren, i, vnode, 'removed');
475
- }
476
- }
477
- return textUpdated;
478
- };
479
-
480
- updateDom = (previous, vnode, projectionOptions) => {
481
- let domNode = previous.domNode!;
482
- let textUpdated = false;
483
- if (previous === vnode) {
484
- return false; // By contract, VNode objects may not be modified anymore after passing them to maquette
485
- }
486
- let updated = false;
487
- if (vnode.vnodeSelector === '') {
488
- if (vnode.text !== previous.text) {
489
- let newTextNode = domNode.ownerDocument.createTextNode(vnode.text!);
490
- domNode.parentNode!.replaceChild(newTextNode, domNode);
491
- vnode.domNode = newTextNode;
492
- textUpdated = true;
493
- return textUpdated;
494
- }
495
- vnode.domNode = domNode;
496
- } else {
497
- if (vnode.vnodeSelector.lastIndexOf('svg', 0) === 0) { // lastIndexOf(needle,0)===0 means StartsWith
498
- projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
499
- }
500
- if (previous.text !== vnode.text) {
501
- updated = true;
502
- if (vnode.text === undefined) {
503
- domNode.removeChild(domNode.firstChild!); // the only textnode presumably
504
- } else {
505
- domNode.textContent = vnode.text;
506
- }
507
- }
508
- vnode.domNode = domNode;
509
- updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;
510
- updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;
511
- if (vnode.properties && vnode.properties.afterUpdate) {
512
- vnode.properties.afterUpdate.apply(
513
- vnode.properties.bind || vnode.properties,
514
- [<Element>domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children]
515
- );
516
- }
517
- }
518
- if (updated && vnode.properties && vnode.properties.updateAnimation) {
519
- vnode.properties.updateAnimation(<Element>domNode, vnode.properties, previous.properties);
520
- }
521
- return textUpdated;
522
- };
523
-
524
- export let createProjection = (vnode: VNode, projectionOptions: ProjectionOptions): Projection => {
525
- return {
526
- getLastRender: () => vnode,
527
- update: (updatedVnode: VNode) => {
528
- if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
529
- throw new Error('The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)');
530
- }
531
- let previousVNode = vnode;
532
- vnode = updatedVnode;
533
- updateDom(previousVNode, updatedVnode, projectionOptions);
534
- },
535
- domNode: <Element>vnode.domNode
536
- };
537
- };
package/src/projector.ts DELETED
@@ -1,168 +0,0 @@
1
- /**
2
- * A projector is used to create the real DOM from the the virtual DOM and to keep it up-to-date afterwards.
3
- *
4
- * You can call [[append]], [[merge]], [[insertBefore]] and [[replace]] to add the virtual DOM to the real DOM.
5
- * The `renderFunction` callbacks will be called to create the real DOM immediately.
6
- * Afterwards, the `renderFunction` callbacks will be called again to update the DOM on the next animation-frame after:
7
- *
8
- * - The Projector's [[scheduleRender]] function was called
9
- * - An event handler (like `onclick`) on a rendered [[VNode]] was called.
10
- *
11
- * The projector stops when [[stop]] is called or when an error is thrown during rendering.
12
- * It is possible to use `window.onerror` to handle these errors.
13
- * Instances of [[Projector]] can be created using [[createProjector]].
14
- */
15
- import {
16
- EventHandlerInterceptor, ProjectorPerformanceLogger, Projection, ProjectionOptions, ProjectorOptions, VNode, VNodeProperties, Projector
17
- } from './interfaces';
18
- import { applyDefaultProjectionOptions, dom } from './dom';
19
-
20
- let createParentNodePath = (node: Node, rootNode: Element) => {
21
- let parentNodePath: Node[] = [];
22
- while (node !== rootNode) {
23
- parentNodePath.push(node);
24
- node = node.parentNode!;
25
- }
26
- return parentNodePath;
27
- };
28
-
29
- let find: <T>(items: T[], predicate: (item: T) => boolean) => T | undefined;
30
- if (Array.prototype.find) {
31
- find = (items, predicate) => items.find(predicate);
32
- } else {
33
- find = (items, predicate) => items.filter(predicate)[0];
34
- }
35
-
36
- let findVNodeByParentNodePath = (vnode: VNode, parentNodePath: Node[]): VNode | undefined => {
37
- let result: VNode | undefined = vnode;
38
- parentNodePath.forEach(node => {
39
- result = (result && result.children) ? find(result.children, child => child.domNode === node) : undefined;
40
- });
41
- return result;
42
- };
43
-
44
- let createEventHandlerInterceptor = (
45
- projector: Projector,
46
- getProjection: () => Projection | undefined,
47
- performanceLogger: ProjectorPerformanceLogger
48
- ): EventHandlerInterceptor => {
49
- let modifiedEventHandler = function(this: Node, evt: Event) {
50
- performanceLogger('domEvent', evt);
51
- let projection = getProjection()!;
52
- let parentNodePath = createParentNodePath(evt.currentTarget as Element, projection.domNode);
53
- parentNodePath.reverse();
54
- let matchingVNode = findVNodeByParentNodePath(projection.getLastRender(), parentNodePath);
55
-
56
- projector.scheduleRender();
57
-
58
- let result: any;
59
- if (matchingVNode) {
60
- /* tslint:disable no-invalid-this */
61
- result = matchingVNode.properties![`on${evt.type}`].apply(matchingVNode.properties!.bind || this, arguments);
62
- /* tslint:enable no-invalid-this */
63
- }
64
- performanceLogger('domEventProcessed', evt);
65
- return result;
66
- };
67
- return (propertyName: string, eventHandler: Function, domNode: Node, properties: VNodeProperties) => modifiedEventHandler;
68
- };
69
-
70
- /**
71
- * Creates a [[Projector]] instance using the provided projectionOptions.
72
- *
73
- * For more information, see [[Projector]].
74
- *
75
- * @param projectorOptions Options that influence how the DOM is rendered and updated.
76
- */
77
- export let createProjector = (projectorOptions?: ProjectorOptions): Projector => {
78
- let projector: Projector;
79
- let projectionOptions = applyDefaultProjectionOptions(projectorOptions);
80
- let performanceLogger = projectionOptions.performanceLogger!;
81
- let renderCompleted = true;
82
- let scheduled: number | undefined;
83
- let stopped = false;
84
- let projections = [] as Projection[];
85
- let renderFunctions = [] as (() => VNode)[]; // matches the projections array
86
-
87
- let addProjection = (
88
- /* one of: dom.append, dom.insertBefore, dom.replace, dom.merge */
89
- domFunction: (node: Element, vnode: VNode, projectionOptions: ProjectionOptions) => Projection,
90
- /* the parameter of the domFunction */
91
- node: Element,
92
- renderFunction: () => VNode
93
- ): void => {
94
- let projection: Projection | undefined;
95
- let getProjection = () => projection;
96
- projectionOptions.eventHandlerInterceptor = createEventHandlerInterceptor(projector, getProjection, performanceLogger);
97
- projection = domFunction(node, renderFunction(), projectionOptions);
98
- projections.push(projection);
99
- renderFunctions.push(renderFunction);
100
- };
101
-
102
- let doRender = () => {
103
- scheduled = undefined;
104
- if (!renderCompleted) {
105
- return; // The last render threw an error, it should have been logged in the browser console.
106
- }
107
- renderCompleted = false;
108
- performanceLogger('renderStart', undefined);
109
- for (let i = 0; i < projections.length; i++) {
110
- let updatedVnode = renderFunctions[i]();
111
- performanceLogger('rendered', undefined);
112
- projections[i].update(updatedVnode);
113
- performanceLogger('patched', undefined);
114
- }
115
- performanceLogger('renderDone', undefined);
116
- renderCompleted = true;
117
- };
118
-
119
- projector = {
120
- renderNow: doRender,
121
- scheduleRender: () => {
122
- if (!scheduled && !stopped) {
123
- scheduled = requestAnimationFrame(doRender);
124
- }
125
- },
126
- stop: () => {
127
- if (scheduled) {
128
- cancelAnimationFrame(scheduled);
129
- scheduled = undefined;
130
- }
131
- stopped = true;
132
- },
133
-
134
- resume: () => {
135
- stopped = false;
136
- renderCompleted = true;
137
- projector.scheduleRender();
138
- },
139
-
140
- append: (parentNode, renderFunction) => {
141
- addProjection(dom.append, parentNode, renderFunction);
142
- },
143
-
144
- insertBefore: (beforeNode, renderFunction) => {
145
- addProjection(dom.insertBefore, beforeNode, renderFunction);
146
- },
147
-
148
- merge: (domNode, renderFunction) => {
149
- addProjection(dom.merge, domNode, renderFunction);
150
- },
151
-
152
- replace: (domNode, renderFunction) => {
153
- addProjection(dom.replace, domNode, renderFunction);
154
- },
155
-
156
- detach: (renderFunction) => {
157
- for (let i = 0; i < renderFunctions.length; i++) {
158
- if (renderFunctions[i] === renderFunction) {
159
- renderFunctions.splice(i, 1);
160
- return projections.splice(i, 1)[0];
161
- }
162
- }
163
- throw new Error('renderFunction was not found');
164
- }
165
-
166
- };
167
- return projector;
168
- };