@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.
- package/README.md +57 -68
- package/components/Badge.js +2 -2
- package/components/BottomAppBar.js +3 -5
- package/components/Box.js +33 -3
- package/components/Button.js +48 -21
- package/components/Button.md +9 -9
- package/components/Card.js +9 -16
- package/components/Checkbox.js +45 -36
- package/components/CheckboxIcon.js +2 -2
- package/components/Chip.js +1 -1
- package/components/Dialog.js +228 -359
- package/components/DialogActions.js +2 -2
- package/components/Divider.js +3 -3
- package/components/ExtendedFab.js +4 -8
- package/components/Fab.js +1 -2
- package/components/FilterChip.js +4 -4
- package/components/Headline.js +1 -1
- package/components/Icon.js +8 -8
- package/components/IconButton.js +9 -14
- package/components/Input.js +273 -1
- package/components/Layout.js +485 -16
- package/components/List.js +6 -4
- package/components/ListItem.js +12 -12
- package/components/ListOption.js +21 -5
- package/components/Listbox.js +239 -0
- package/components/Menu.js +77 -526
- package/components/MenuItem.js +12 -14
- package/components/Nav.js +0 -2
- package/components/NavBar.js +8 -79
- package/components/NavDrawer.js +12 -11
- package/components/NavDrawerItem.js +2 -1
- package/components/NavItem.js +18 -8
- package/components/NavRail.js +15 -7
- package/components/NavRailItem.js +3 -1
- package/components/Popup.js +20 -0
- package/components/Progress.js +24 -23
- package/components/Radio.js +42 -35
- package/components/RadioIcon.js +3 -3
- package/components/Ripple.js +2 -3
- package/components/Search.js +85 -0
- package/components/SegmentedButton.js +1 -10
- package/components/SegmentedButtonGroup.js +16 -10
- package/components/Select.js +4 -4
- package/components/Shape.js +1 -1
- package/components/Slider.js +43 -50
- package/components/Snackbar.js +4 -5
- package/components/Surface.js +3 -3
- package/components/Switch.js +55 -21
- package/components/SwitchIcon.js +10 -8
- package/components/Tab.js +11 -9
- package/components/TabContent.js +4 -3
- package/components/TabList.js +2 -2
- package/components/TabPanel.js +11 -8
- package/components/TextArea.js +38 -35
- package/components/Tooltip.js +2 -2
- package/components/TopAppBar.js +65 -147
- package/core/Composition.js +985 -628
- package/core/CompositionAdapter.js +315 -0
- package/core/CustomElement.js +153 -90
- package/core/DomAdapter.js +586 -0
- package/core/ICustomElement.d.ts +2 -2
- package/core/css.js +8 -7
- package/core/customTypes.js +53 -31
- package/{utils → core}/jsonMergePatch.js +36 -14
- package/core/observe.js +111 -57
- package/core/optimizations.js +23 -0
- package/core/template.js +17 -11
- package/core/test.js +126 -0
- package/core/typings.d.ts +11 -5
- package/core/uid.js +13 -0
- package/dist/index.min.js +83 -152
- package/dist/index.min.js.map +4 -4
- package/dist/meta.json +1 -1
- package/mixins/AriaReflectorMixin.js +1 -2
- package/mixins/AriaToolbarMixin.js +2 -3
- package/mixins/ControlMixin.js +25 -17
- package/mixins/DensityMixin.js +0 -1
- package/mixins/FlexableMixin.js +1 -2
- package/mixins/FormAssociatedMixin.js +13 -10
- package/mixins/InputMixin.js +2 -9
- package/mixins/KeyboardNavMixin.js +14 -1
- package/mixins/PopupMixin.js +757 -0
- package/mixins/RTLObserverMixin.js +0 -1
- package/mixins/ResizeObserverMixin.js +0 -1
- package/mixins/RippleMixin.js +3 -4
- package/mixins/ScrollListenerMixin.js +41 -32
- package/mixins/SemiStickyMixin.js +151 -0
- package/mixins/ShapeMixin.js +29 -24
- package/mixins/StateMixin.js +11 -6
- package/mixins/SurfaceMixin.js +3 -57
- package/mixins/TextFieldMixin.js +57 -65
- package/mixins/ThemableMixin.js +78 -156
- package/mixins/TooltipTriggerMixin.js +7 -13
- package/mixins/TouchTargetMixin.js +4 -3
- package/package.json +9 -5
- package/theming/index.js +1 -1
- package/theming/themableMixinLoader.js +12 -0
- package/utils/{hct → material-color}/blend.js +8 -10
- package/utils/{hct → material-color/hct}/Cam16.js +196 -69
- package/utils/{hct → material-color/hct}/Hct.js +61 -19
- package/utils/{hct → material-color/hct}/ViewingConditions.js +3 -3
- package/utils/{hct → material-color/hct}/hctSolver.js +9 -16
- package/utils/{hct → material-color}/helper.js +11 -18
- package/utils/{hct → material-color/palettes}/CorePalette.js +79 -19
- package/utils/{hct → material-color/palettes}/TonalPalette.js +12 -4
- package/utils/material-color/scheme/Scheme.js +376 -0
- package/utils/{hct/colorUtils.js → material-color/utils/color.js} +61 -1
- package/utils/popup.js +46 -25
- package/components/ListSelect.js +0 -220
- package/components/Option.js +0 -91
- package/components/Pane.js +0 -281
- package/core/identify.js +0 -40
- package/utils/hct/Scheme.js +0 -587
- /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
|
+
}
|
package/core/ICustomElement.d.ts
CHANGED
|
@@ -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 ?
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
}
|