@shortfuse/materialdesignweb 0.7.6 → 0.8.0

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 (114) hide show
  1. package/README.md +57 -68
  2. package/components/Badge.js +2 -2
  3. package/components/BottomAppBar.js +3 -5
  4. package/components/Box.js +33 -3
  5. package/components/Button.js +48 -21
  6. package/components/Button.md +9 -9
  7. package/components/Card.js +9 -16
  8. package/components/Checkbox.js +45 -36
  9. package/components/CheckboxIcon.js +2 -2
  10. package/components/Chip.js +1 -1
  11. package/components/Dialog.js +228 -359
  12. package/components/DialogActions.js +2 -2
  13. package/components/Divider.js +3 -3
  14. package/components/ExtendedFab.js +4 -8
  15. package/components/Fab.js +1 -2
  16. package/components/FilterChip.js +4 -4
  17. package/components/Headline.js +1 -1
  18. package/components/Icon.js +8 -8
  19. package/components/IconButton.js +9 -14
  20. package/components/Input.js +273 -1
  21. package/components/Layout.js +485 -16
  22. package/components/List.js +6 -4
  23. package/components/ListItem.js +12 -12
  24. package/components/ListOption.js +21 -5
  25. package/components/Listbox.js +239 -0
  26. package/components/Menu.js +77 -526
  27. package/components/MenuItem.js +12 -14
  28. package/components/Nav.js +0 -2
  29. package/components/NavBar.js +8 -79
  30. package/components/NavDrawer.js +12 -11
  31. package/components/NavDrawerItem.js +2 -1
  32. package/components/NavItem.js +18 -8
  33. package/components/NavRail.js +15 -7
  34. package/components/NavRailItem.js +3 -1
  35. package/components/Popup.js +20 -0
  36. package/components/Progress.js +24 -23
  37. package/components/Radio.js +42 -35
  38. package/components/RadioIcon.js +3 -3
  39. package/components/Ripple.js +2 -3
  40. package/components/Search.js +85 -0
  41. package/components/SegmentedButton.js +1 -10
  42. package/components/SegmentedButtonGroup.js +16 -10
  43. package/components/Select.js +4 -4
  44. package/components/Shape.js +1 -1
  45. package/components/Slider.js +43 -50
  46. package/components/Snackbar.js +4 -5
  47. package/components/Surface.js +3 -3
  48. package/components/Switch.js +55 -21
  49. package/components/SwitchIcon.js +10 -8
  50. package/components/Tab.js +11 -9
  51. package/components/TabContent.js +4 -3
  52. package/components/TabList.js +2 -2
  53. package/components/TabPanel.js +11 -8
  54. package/components/TextArea.js +38 -35
  55. package/components/Tooltip.js +2 -2
  56. package/components/TopAppBar.js +65 -147
  57. package/core/Composition.js +985 -628
  58. package/core/CompositionAdapter.js +315 -0
  59. package/core/CustomElement.js +153 -90
  60. package/core/DomAdapter.js +586 -0
  61. package/core/ICustomElement.d.ts +2 -2
  62. package/core/css.js +8 -7
  63. package/core/customTypes.js +53 -31
  64. package/{utils → core}/jsonMergePatch.js +36 -14
  65. package/core/observe.js +111 -57
  66. package/core/optimizations.js +23 -0
  67. package/core/template.js +17 -11
  68. package/core/test.js +126 -0
  69. package/core/typings.d.ts +11 -5
  70. package/core/uid.js +13 -0
  71. package/dist/index.min.js +83 -152
  72. package/dist/index.min.js.map +4 -4
  73. package/dist/meta.json +1 -1
  74. package/mixins/AriaReflectorMixin.js +1 -2
  75. package/mixins/AriaToolbarMixin.js +2 -3
  76. package/mixins/ControlMixin.js +25 -17
  77. package/mixins/DensityMixin.js +0 -1
  78. package/mixins/FlexableMixin.js +1 -2
  79. package/mixins/FormAssociatedMixin.js +13 -10
  80. package/mixins/InputMixin.js +2 -9
  81. package/mixins/KeyboardNavMixin.js +14 -1
  82. package/mixins/PopupMixin.js +757 -0
  83. package/mixins/RTLObserverMixin.js +0 -1
  84. package/mixins/ResizeObserverMixin.js +0 -1
  85. package/mixins/RippleMixin.js +3 -4
  86. package/mixins/ScrollListenerMixin.js +41 -32
  87. package/mixins/SemiStickyMixin.js +151 -0
  88. package/mixins/ShapeMixin.js +29 -24
  89. package/mixins/StateMixin.js +11 -6
  90. package/mixins/SurfaceMixin.js +3 -57
  91. package/mixins/TextFieldMixin.js +57 -65
  92. package/mixins/ThemableMixin.js +78 -156
  93. package/mixins/TooltipTriggerMixin.js +7 -13
  94. package/mixins/TouchTargetMixin.js +4 -3
  95. package/package.json +9 -5
  96. package/theming/index.js +1 -1
  97. package/theming/themableMixinLoader.js +12 -0
  98. package/utils/{hct → material-color}/blend.js +8 -10
  99. package/utils/{hct → material-color/hct}/Cam16.js +196 -69
  100. package/utils/{hct → material-color/hct}/Hct.js +61 -19
  101. package/utils/{hct → material-color/hct}/ViewingConditions.js +3 -3
  102. package/utils/{hct → material-color/hct}/hctSolver.js +9 -16
  103. package/utils/{hct → material-color}/helper.js +11 -18
  104. package/utils/{hct → material-color/palettes}/CorePalette.js +79 -19
  105. package/utils/{hct → material-color/palettes}/TonalPalette.js +12 -4
  106. package/utils/material-color/scheme/Scheme.js +376 -0
  107. package/utils/{hct/colorUtils.js → material-color/utils/color.js} +61 -1
  108. package/utils/popup.js +46 -25
  109. package/components/ListSelect.js +0 -220
  110. package/components/Option.js +0 -91
  111. package/components/Pane.js +0 -281
  112. package/core/identify.js +0 -40
  113. package/utils/hct/Scheme.js +0 -587
  114. /package/utils/{hct/mathUtils.js → material-color/utils/math.js} +0 -0
@@ -0,0 +1,586 @@
1
+ /** @return {HTMLElement} */
2
+ function AnyDomAdapterCreator() {
3
+ return document.createElement('div');
4
+ }
5
+
6
+ /**
7
+ * @param {HTMLElement} element
8
+ * @param {any} data
9
+ * @return {void}
10
+ */
11
+ function AnyDomAdapterRenderer(element, data) {
12
+ let s = '';
13
+ if (data != null) {
14
+ s = data.toString ? data.toString() : String(data).toString();
15
+ }
16
+ if (element.textContent !== s) {
17
+ element.textContent = s;
18
+ }
19
+ }
20
+
21
+ /**
22
+ * @typedef {Object} DomAdapterRecycleOptions
23
+ * @prop {HTMLElement} scroller Scrolling container element
24
+ * @prop {boolean} [equalSize=false] All elements are equally sized
25
+ * @prop {boolean} [block=false] All elements are block-like
26
+ * @prop {boolean} [fastMeasure=false] Use integer precision for layout
27
+ * @prop {boolean} [deferRender=false] Render on scroll idle
28
+ */
29
+
30
+ /**
31
+ * @template T1
32
+ * @template {HTMLElement} T2
33
+ * @typedef {Object} DomAdapterCreateOptions
34
+ * @prop {HTMLElement} element
35
+ * @prop {Array<T1>} datasource
36
+ * @prop {DomAdapterRecycleOptions} [recycle]
37
+ * @prop {function(T1):T2} [create={function(T1):HTMLElement}]
38
+ * @prop {function(T2, T1, number):void} [render={function(HTMLElement,T1):void}]
39
+ */
40
+
41
+ /**
42
+ * @typedef {Object} DomAdapterBounds
43
+ * @prop {number} top
44
+ * @prop {number=} right
45
+ * @prop {number=} bottom
46
+ * @prop {number=} left
47
+ * @prop {number=} height
48
+ * @prop {number=} width
49
+ */
50
+
51
+ /**
52
+ * @template T1
53
+ * @template {HTMLElement} T2
54
+ */
55
+ export default class DomAdapter {
56
+ /** @param {DomAdapterCreateOptions<T1, T2>} options */
57
+ constructor(options) {
58
+ this.element = options.element;
59
+ this.datasource = options.datasource;
60
+ /** @type {Map<T1, T2>} */
61
+ this.dataElementMap = new Map();
62
+ /** @type {WeakMap<T2, T1>} */
63
+ this.elementDataMap = new WeakMap();
64
+ /** @type {WeakMap<any, DomAdapterBounds>} */
65
+ this.dataBoundsMap = new WeakMap();
66
+ this.create = options.create || AnyDomAdapterCreator;
67
+ this.render = options.render || AnyDomAdapterRenderer;
68
+ this.onScrollerScrollListener = () => this.onScrollerScroll();
69
+ this.setupRecycleOptions(options.recycle);
70
+ }
71
+
72
+ /**
73
+ * @param {DomAdapterRecycleOptions} recycleOptions
74
+ * @return {void}
75
+ */
76
+ setupRecycleOptions(recycleOptions) {
77
+ this.recycle = recycleOptions;
78
+ if (!this.recycle) {
79
+ return;
80
+ }
81
+ this.recycle.scroller.addEventListener('scroll', this.onScrollerScrollListener, { passive: true });
82
+ const scrollerStyle = window.getComputedStyle(this.recycle.scroller);
83
+ if (scrollerStyle.position === 'static') {
84
+ this.recycle.scroller.style.setProperty('position', 'relative');
85
+ }
86
+ }
87
+
88
+ onScrollerScroll() {
89
+ this.drawViewport(false);
90
+ if (this.deferRenderDebounce) {
91
+ clearTimeout(this.deferRenderDebounce);
92
+ this.deferRenderDebounce = null;
93
+ }
94
+ if (this.recycle && this.recycle.deferRender) {
95
+ this.deferRenderDebounce = setTimeout(() => {
96
+ this.drawViewport(true);
97
+ }, 50);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * @param {boolean} [checkForRemovedItems=true]
103
+ * @return {void}
104
+ */
105
+ refresh(checkForRemovedItems = true) {
106
+ if (checkForRemovedItems) {
107
+ /** @type {T1[]} */
108
+ const unlinkedDataItems = [];
109
+ /** @type {T2[]} */
110
+ const orphanedElements = [];
111
+ for (const data of this.dataElementMap.keys()) {
112
+ if (!this.datasource.includes(data)) {
113
+ unlinkedDataItems.push(data);
114
+ }
115
+ }
116
+ let i = this.element.children.length;
117
+ while (i--) {
118
+ const child = /** @type {T2} */ (this.element.children.item(i));
119
+ const data = this.elementDataMap.get(child);
120
+ if (data && !this.datasource.includes(data)) {
121
+ orphanedElements.push(child);
122
+ }
123
+ }
124
+ for (const data of unlinkedDataItems) {
125
+ this.removeItem(data);
126
+ }
127
+ for (const el of orphanedElements) {
128
+ this.removeElement(el);
129
+ }
130
+ }
131
+ if (this.recycle) {
132
+ this.element.style.removeProperty('box-sizing');
133
+ this.element.style.removeProperty('padding-top');
134
+ this.element.style.removeProperty('padding-bottom');
135
+ this.element.style.removeProperty('height');
136
+ this.clear();
137
+ this.drawViewport(true);
138
+ } else {
139
+ for (const data of this.datasource) {
140
+ this.refreshItem(data);
141
+ }
142
+ }
143
+ }
144
+
145
+ /** @return {void} */
146
+ clear() {
147
+ this.maxBottomBounds = 0;
148
+ this.dataElementMap.clear();
149
+ this.dataBoundsMap = new WeakMap();
150
+ this.elementDataMap = new WeakMap();
151
+ while (this.element.lastChild) {
152
+ this.element.removeChild(this.element.lastChild);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * @param {boolean} [idle=true]
158
+ * @return {void}
159
+ */
160
+ drawViewport(idle = true) {
161
+ if (!this.recycle || !this.recycle.scroller) {
162
+ return;
163
+ }
164
+ const itemsToDraw = [];
165
+ let foundVisibleItem = false;
166
+ let expectedTop = null;
167
+ let paddingTopSet = false;
168
+ let lastBottom = 0;
169
+ const additionalPrerenderSize = (window.screen.height - this.recycle.scroller.clientHeight) / 2;
170
+ const viewportTop = this.recycle.scroller.scrollTop;
171
+ const viewportBottom = (this.recycle.scroller.scrollTop + this.recycle.scroller.offsetHeight);
172
+ const preRenderTop = viewportTop - additionalPrerenderSize;
173
+ const preRenderBottom = viewportBottom + additionalPrerenderSize;
174
+ for (let i = 0; i < this.datasource.length; i += 1) {
175
+ const data = this.datasource[i];
176
+ let dataBounds = this.getBounds(data, i);
177
+ let newBounds = false;
178
+ let element = null;
179
+ if (!dataBounds || dataBounds.top == null || dataBounds.bottom == null) {
180
+ if (!this.recycle.block && !paddingTopSet) {
181
+ if (expectedTop != null) {
182
+ this.element.style.setProperty('padding-top', `${expectedTop}px`);
183
+ } else {
184
+ this.element.style.setProperty('padding-top', `${lastBottom}px`);
185
+ }
186
+ paddingTopSet = true;
187
+ }
188
+ element = this.refreshItem(data, {
189
+ create: true,
190
+ render: !this.recycle.deferRender,
191
+ invalidate: false,
192
+ });
193
+ dataBounds = this.storeBoundsCache(data, element);
194
+ newBounds = true;
195
+ }
196
+ lastBottom = dataBounds.bottom;
197
+ const isBottomInPrerender = (dataBounds.bottom >= preRenderTop
198
+ && dataBounds.bottom <= preRenderBottom);
199
+ const isTopInPrerender = (dataBounds.top >= preRenderTop
200
+ && dataBounds.top <= preRenderBottom);
201
+ if (isBottomInPrerender || isTopInPrerender) {
202
+ if (expectedTop == null) {
203
+ expectedTop = dataBounds.top;
204
+ }
205
+ itemsToDraw.push({
206
+ data,
207
+ index: i,
208
+ });
209
+ if (!foundVisibleItem) {
210
+ const isBottomVisible = (dataBounds.bottom >= viewportTop
211
+ && dataBounds.bottom <= viewportBottom);
212
+ const isTopVisible = (dataBounds.top >= viewportTop
213
+ && dataBounds.top <= viewportBottom);
214
+ foundVisibleItem = (isBottomVisible || isTopVisible);
215
+ }
216
+ } else {
217
+ if (!element && !paddingTopSet) {
218
+ element = this.dataElementMap.get(data);
219
+ if (element && element.parentElement) {
220
+ this.removeElement(element);
221
+ }
222
+ }
223
+ if (foundVisibleItem) {
224
+ if (newBounds) {
225
+ this.dataBoundsMap.delete(data);
226
+ }
227
+ break;
228
+ }
229
+ }
230
+ }
231
+ const renderedElements = [];
232
+ let newPaddingTop = 0;
233
+ let lastRenderedBounds;
234
+ for (let i = 0; i < itemsToDraw.length; i += 1) {
235
+ const item = itemsToDraw[i];
236
+ let element = this.dataElementMap.get(item.data);
237
+
238
+ if (!element) {
239
+ element = this.refreshItem(item.data, {
240
+ create: true,
241
+ render: (this.recycle.deferRender ? idle : true),
242
+ invalidate: false,
243
+ });
244
+ } else if (this.recycle.deferRender && idle) {
245
+ this.render(element, item.data, item.index);
246
+ }
247
+ const cachedBounds = this.getBounds(item.data);
248
+ if (i === 0 && !this.recycle.block) {
249
+ newPaddingTop = cachedBounds.top;
250
+ }
251
+ if (i === itemsToDraw.length - 1) {
252
+ lastRenderedBounds = cachedBounds;
253
+ }
254
+ renderedElements.push(element);
255
+ }
256
+ let i = this.element.children.length;
257
+ while (i--) {
258
+ const child = /** @type {T2} */ (this.element.children.item(i));
259
+ if (!renderedElements.includes(child)) {
260
+ this.removeElement(child);
261
+ }
262
+ }
263
+ this.element.style.setProperty('padding-top', `${newPaddingTop}px`);
264
+ const lastItem = this.datasource[this.datasource.length - 1];
265
+ const lastItemBounds = this.dataBoundsMap.get(lastItem);
266
+
267
+ if (lastItemBounds) {
268
+ this.element.style.setProperty('box-sizing', 'border-box');
269
+ this.element.style.setProperty('height', `${lastItemBounds.bottom}px`);
270
+ this.element.style.removeProperty('padding-bottom');
271
+ } else if (this.recycle.equalSize) {
272
+ this.element.style.setProperty('box-sizing', 'border-box');
273
+ this.element.style.setProperty('height', `${lastRenderedBounds.height * this.datasource.length}px`);
274
+ this.element.style.removeProperty('padding-bottom');
275
+ } else {
276
+ if (this.maxBottomBounds && lastRenderedBounds
277
+ && lastRenderedBounds.bottom <= this.maxBottomBounds) {
278
+ this.maxBottomBounds = lastRenderedBounds.bottom;
279
+ }
280
+ const height = this.maxBottomBounds - newPaddingTop;
281
+ this.element.style.setProperty('box-sizing', 'content-box');
282
+ this.element.style.setProperty('height', `${height}px`);
283
+ this.element.style.setProperty('padding-bottom', `${64}px`);
284
+ }
285
+ }
286
+
287
+ /**
288
+ * @param {T1} data
289
+ * @param {number} [indexHint]
290
+ * @return {?DomAdapterBounds}
291
+ */
292
+ getBounds(data, indexHint) {
293
+ const cached = this.dataBoundsMap.get(data);
294
+ if (cached) {
295
+ return cached;
296
+ }
297
+ let index = -1;
298
+ index = indexHint === -1 || indexHint == null ? this.datasource.indexOf(data) : indexHint;
299
+ if (index === -1) {
300
+ return null;
301
+ }
302
+ let top = null;
303
+ if (index === 0) {
304
+ const cache = {
305
+ top: 0,
306
+ };
307
+ this.dataBoundsMap.set(data, cache);
308
+ return cache;
309
+ }
310
+ if (!this.recycle.block) {
311
+ return null;
312
+ }
313
+ let height = null;
314
+ const previousBounds = this.dataBoundsMap.get(this.datasource[index - 1]);
315
+ if (previousBounds && previousBounds.bottom) {
316
+ top = previousBounds.bottom;
317
+ } else if (this.recycle.equalSize) {
318
+ const firstBounds = this.dataBoundsMap.get(this.datasource[0]);
319
+ if (firstBounds && firstBounds.height) {
320
+ top = firstBounds.height * index;
321
+ height = firstBounds.height;
322
+ } else {
323
+ return null;
324
+ }
325
+ } else {
326
+ return null;
327
+ }
328
+ if (height == null) {
329
+ const cache = {
330
+ top,
331
+ };
332
+ this.dataBoundsMap.set(data, cache);
333
+ return cache;
334
+ }
335
+ const cache = {
336
+ top,
337
+ height,
338
+ bottom: top + height,
339
+ };
340
+ this.dataBoundsMap.set(data, cache);
341
+ return cache;
342
+ }
343
+
344
+ /**
345
+ * @param {T2} element
346
+ * @return {DomAdapterBounds} bounds
347
+ */
348
+ measureElementBounds(element) {
349
+ if (this.recycle.fastMeasure) {
350
+ return {
351
+ top: element.offsetTop,
352
+ right: element.offsetWidth + element.offsetLeft,
353
+ bottom: element.offsetHeight + element.offsetTop,
354
+ left: element.offsetLeft,
355
+ height: element.clientHeight,
356
+ width: element.clientWidth,
357
+ };
358
+ }
359
+ const offsetParentRect = element.offsetParent.getBoundingClientRect();
360
+ const elementRect = element.getBoundingClientRect();
361
+ const top = elementRect.top - offsetParentRect.top + element.offsetParent.scrollTop;
362
+ const left = elementRect.left - offsetParentRect.left + element.offsetParent.scrollLeft;
363
+ return {
364
+ top,
365
+ right: elementRect.width + left,
366
+ bottom: elementRect.height + top,
367
+ left,
368
+ height: elementRect.height,
369
+ width: elementRect.width,
370
+ };
371
+ }
372
+
373
+ /**
374
+ * @param {T1} data
375
+ * @param {T2} element
376
+ * @return {DomAdapterBounds} bounds
377
+ */
378
+ storeBoundsCache(data, element) {
379
+ if (!this.recycle) {
380
+ return null;
381
+ }
382
+ const cache = this.measureElementBounds(element);
383
+ this.dataBoundsMap.set(data, cache);
384
+ return cache;
385
+ }
386
+
387
+ invalidateAll() {
388
+ /** @type {WeakMap<any, DomAdapterBounds>} */
389
+ this.dataBoundsMap = new WeakMap();
390
+ }
391
+
392
+ /**
393
+ * @param {T1} data
394
+ * @param {Object} [options]
395
+ * @param {boolean} [options.checkPosition=true]
396
+ * @param {boolean} [options.create] Automatic
397
+ * @param {boolean} [options.render] Automatic based on deferRender
398
+ * @param {boolean} [options.invalidate] Automatic
399
+ * @return {T2} element
400
+ */
401
+ refreshItem(data, options = {}) {
402
+ const dataIndex = this.datasource.indexOf(data);
403
+ if (dataIndex === -1) {
404
+ // Item has been removed
405
+ this.removeItem(data);
406
+ return null;
407
+ }
408
+ let elementIndex = -1;
409
+ let element = this.dataElementMap.get(data);
410
+ let invalidate = false;
411
+ if (element && this.recycle && this.recycle.deferRender && options.invalidate === true) {
412
+ // Deferred Render has a forced invalidation
413
+ // Item should be recreated
414
+ this.removeElement(element);
415
+ element = null;
416
+ }
417
+ if (!element) {
418
+ // Element does not exist, assume size changed
419
+ invalidate = true;
420
+ element = /** @type {T2} */ (this.create(data));
421
+ this.dataElementMap.set(data, element);
422
+ this.elementDataMap.set(element, data);
423
+ }
424
+ if (element && this.recycle && this.recycle.block) {
425
+ const bounds = this.getBounds(data, dataIndex);
426
+ element.style.setProperty('position', 'absolute');
427
+ element.style.setProperty('left', '0');
428
+ element.style.setProperty('right', '0');
429
+ if (bounds) {
430
+ element.style.setProperty('top', `${bounds.top}px`);
431
+ } else {
432
+ this.removeElement(element);
433
+ element = null;
434
+ invalidate = true;
435
+ }
436
+ }
437
+ if (element && options.checkPosition === false) {
438
+ if (!element.parentElement) {
439
+ this.element.appendChild(element);
440
+ }
441
+ } else if (element) {
442
+ if (element.parentElement === this.element) {
443
+ // Element is in DOM
444
+ let sibling = this.element.firstElementChild;
445
+ let siblingIndex = -1;
446
+ while (sibling) {
447
+ siblingIndex += 1;
448
+ if (sibling === element) {
449
+ elementIndex = siblingIndex;
450
+ break;
451
+ }
452
+ sibling = sibling.nextElementSibling;
453
+ }
454
+ }
455
+ if (elementIndex !== dataIndex) {
456
+ if (!this.element.hasChildNodes()) {
457
+ if (element.parentElement) {
458
+ element.remove();
459
+ }
460
+ this.element.appendChild(element);
461
+ } else {
462
+ // Iterate through datasource to previous sibling element
463
+ let siblingIndex = dataIndex - 1;
464
+ let inserted = false;
465
+ let previousDataObject;
466
+ do {
467
+ previousDataObject = this.datasource[siblingIndex];
468
+ const previousElement = this.dataElementMap.get(previousDataObject);
469
+ if (previousElement) {
470
+ if (element.previousElementSibling !== previousElement) {
471
+ if (element.parentElement) {
472
+ element.remove();
473
+ }
474
+ previousElement.after(element);
475
+ }
476
+ inserted = true;
477
+ } else {
478
+ siblingIndex -= 1;
479
+ }
480
+ } while (previousDataObject && !inserted);
481
+ if (!inserted) {
482
+ if (element.parentElement) {
483
+ element.remove();
484
+ }
485
+ this.element.insertBefore(element, this.element.firstElementChild);
486
+ }
487
+ }
488
+ }
489
+ }
490
+ if ((options.render === false)
491
+ || (options.render !== true && this.recycle && this.recycle.deferRender)) {
492
+ invalidate = true;
493
+ } else if (element
494
+ && (options.render === true || (!this.recycle || !this.recycle.deferRender))) {
495
+ let prevClientWidth;
496
+ let prevClientHeight;
497
+ if (!invalidate && options.invalidate !== false) {
498
+ // Store width and height for later comparison
499
+ prevClientWidth = element.clientWidth;
500
+ prevClientHeight = element.clientHeight;
501
+ }
502
+ this.render(element, data, dataIndex);
503
+ if (!invalidate && options.invalidate !== false
504
+ && (element.clientWidth !== prevClientWidth || element.clientHeight !== prevClientHeight)) {
505
+ // Element width or height has changed
506
+ invalidate = true;
507
+ }
508
+ }
509
+ if (options.invalidate === true || (invalidate && options.invalidate !== false)) {
510
+ this.invalidateItem(data, dataIndex);
511
+ }
512
+ return element;
513
+ }
514
+
515
+ /**
516
+ * @param {T2} element
517
+ * @return {void}
518
+ */
519
+ removeElement(element) {
520
+ if (element.parentElement) {
521
+ element.remove();
522
+ }
523
+ const data = this.elementDataMap.get(element);
524
+ if (data) {
525
+ this.dataElementMap.delete(data);
526
+ }
527
+ this.elementDataMap.delete(element);
528
+ }
529
+
530
+ /**
531
+ * @param {T1} data
532
+ * @param {number} [indexHint]
533
+ * @return {boolean} change
534
+ */
535
+ invalidateItem(data, indexHint) {
536
+ if (!this.recycle || this.recycle.equalSize) {
537
+ return false;
538
+ }
539
+ // Don't auto-create on non-block layouts
540
+ // Invalidate in case of size change
541
+ const currentBounds = this.dataBoundsMap.get(data);
542
+ if (!currentBounds) {
543
+ return false;
544
+ }
545
+ this.dataBoundsMap.delete(data);
546
+ const dataIndex = (indexHint != null ? indexHint : this.datasource.indexOf(data));
547
+ if (dataIndex === -1) {
548
+ // Item was removed
549
+ return true;
550
+ }
551
+ // Invalidate previous items at same Y position
552
+ for (let j = dataIndex - 1; j >= 0; j -= 1) {
553
+ const previousBounds = this.dataBoundsMap.get(this.datasource[j]);
554
+ if (!previousBounds || previousBounds.top < currentBounds.top) {
555
+ break;
556
+ }
557
+ this.dataBoundsMap.delete(this.datasource[j]);
558
+ }
559
+ // Invalidate next items
560
+ for (let j = dataIndex + 1; j < this.datasource.length; j += 1) {
561
+ if (!this.dataBoundsMap.delete(this.datasource[j])) {
562
+ break;
563
+ }
564
+ }
565
+ return true;
566
+ }
567
+
568
+ /**
569
+ * @param {T1} data
570
+ * @param {number} [previousIndex]
571
+ * @return {void}
572
+ */
573
+ removeItem(data, previousIndex) {
574
+ this.invalidateItem(data, previousIndex);
575
+ this.dataElementMap.delete(data);
576
+ const element = this.dataElementMap.get(data);
577
+ if (element) {
578
+ this.removeElement(element);
579
+ }
580
+ }
581
+
582
+ detach() {
583
+ this.element = null;
584
+ this.datasource = null;
585
+ }
586
+ }
@@ -108,7 +108,7 @@ export declare const ICustomElement: {
108
108
  this: T,
109
109
  string: TemplateStringsArray,
110
110
  // eslint-disable-next-line no-shadow
111
- ...substitutions: (string|Element|((this:InstanceType<T>, data:InstanceType<T> & T['schema']) => any))[]
111
+ ...substitutions: (string|Element|((this:InstanceType<T>, data:InstanceType<T> & T['schema'], injections?:any) => any))[]
112
112
  ): T
113
113
 
114
114
  css<
@@ -116,7 +116,7 @@ export declare const ICustomElement: {
116
116
  T2 extends TemplateStringsArray|HTMLStyleElement|CSSStyleSheet>(
117
117
  this: T1,
118
118
  array: T2,
119
- ...rest: T2 extends TemplateStringsArray ? string[] : (HTMLStyleElement|CSSStyleSheet)[]
119
+ ...rest: T2 extends TemplateStringsArray ? any[] : (HTMLStyleElement|CSSStyleSheet)[]
120
120
  ): T1
121
121
 
122
122
  define<
package/core/css.js CHANGED
@@ -36,14 +36,15 @@ export function* generateHTMLStyleElements(styles) {
36
36
  // console.log('Cloning parent HTMLStyleElement instead');
37
37
  // @ts-ignore Skip cast
38
38
  yield style.ownerNode.cloneNode(true);
39
+ } else if (styleElementWrappers.has(style)) {
40
+ // @ts-ignore Skip cast
41
+ yield styleElementWrappers.get(style).cloneNode(true);
39
42
  } else {
40
- let styleElement = styleElementWrappers.get(style);
41
- if (!styleElement) {
42
- console.warn('Manually constructing HTMLStyleElement', [...style.cssRules].map((r) => r.cssText).join('\n'));
43
- styleElement = document.createElement('style');
44
- styleElement.textContent = [...style.cssRules].map((r) => r.cssText).join('');
45
- styleElementWrappers.set(style, styleElement);
46
- }
43
+ console.warn('Manually constructing HTMLStyleElement', [...style.cssRules].map((r) => r.cssText).join('\n'));
44
+ const styleElement = document.createElement('style');
45
+ styleElement.textContent = [...style.cssRules].map((r) => r.cssText).join('');
46
+ styleElementWrappers.set(style, styleElement);
47
+
47
48
  // @ts-ignore Skip cast
48
49
  yield styleElement.cloneNode(true);
49
50
  }