UNPKG

@web-atoms/core

Version:
710 lines (709 loc) • 24.9 kB
System.register(["../../core/AtomBinder", "../../core/AtomEnumerator", "../../core/AtomList", "../../core/XNode", "../../web/core/AtomUI", "./AtomControl"], function (_export, _context) { "use strict"; var AtomBinder, AtomEnumerator, XNode, AtomUI, ChildEnumerator, AtomControl, AtomItemsControl, AtomItemsControlItemTemplate; _export("AtomItemsControl", void 0); return { setters: [function (_coreAtomBinder) { AtomBinder = _coreAtomBinder.AtomBinder; }, function (_coreAtomEnumerator) { AtomEnumerator = _coreAtomEnumerator.default; }, function (_coreAtomList) {}, function (_coreXNode) { XNode = _coreXNode.default; }, function (_webCoreAtomUI) { AtomUI = _webCoreAtomUI.AtomUI; ChildEnumerator = _webCoreAtomUI.ChildEnumerator; }, function (_AtomControl) { AtomControl = _AtomControl.AtomControl; }], execute: function () { _export("AtomItemsControl", AtomItemsControl = class AtomItemsControl extends AtomControl { constructor() { super(...arguments); this.mValue = undefined; this.mFilter = undefined; this.mFirstChild = null; this.mLastChild = null; this.mScrollerSetup = false; this.mScopes = null; this.mItemsDisposable = null; this.isUpdating = false; } get itemsPresenter() { return this.mItemsPresenter || (this.mItemsPresenter = this.element); } set itemsPresenter(v) { this.mItemsPresenter = v; AtomBinder.refreshValue(this, "itemsPresenter"); } get value() { if (this.allowMultipleSelection) { let items = this.mSelectedItems; if (items.length === 0) { if (this.mValue !== undefined) { return this.mValue; } return null; } items = items.map(m => m[this.valuePath]); if (this.valueSeparator) { items = items.join(this.valueSeparator); } return items; } let s = this.selectedItem; if (!s) { if (this.mValue !== undefined) { return this.mValue; } return null; } if (this.valuePath) { s = s[this.valuePath]; } return s; } set value(v) { this.mValue = v; const dataItems = this.items; if (!dataItems) { return; } const sItems = this.selectedItems; if (v === undefined || v === null) { AtomBinder.clear(sItems); return; } if (this.allowMultipleSelection && this.valueSeparator) { if (typeof v !== "string") { v = "" + v; } v = v.split(this.valueSeparator); } else { v = [v]; } sItems.length = 0; const vp = this.valuePath; for (const item of v) { const dataItem = dataItems.find(i => i[vp] == v); if (dataItem) { sItems.push(dataItem); } } AtomBinder.refreshItems(sItems); } get items() { return this.mItems; } set items(v) { if (this.mItemsDisposable) { this.mItemsDisposable.dispose(); this.mItemsDisposable = null; } this.mItems = v; if (v != null) { this.mItemsDisposable = this.registerDisposable(AtomBinder.add_CollectionChanged(v, (target, key, index, item) => { this.onCollectionChangedInternal(key, index, item); })); } AtomBinder.refreshValue(this, "items"); } get selectedItem() { if (this.selectedItems.length > 0) { return this.selectedItems[0]; } return null; } set selectedItem(value) { if (value !== undefined && value !== null) { this.mSelectedItems.length = 1; this.mSelectedItems[0] = value; } else { this.mSelectedItems.length = 0; } AtomBinder.refreshItems(this.mSelectedItems); } get selectedItems() { return this.mSelectedItems || (this.selectedItems = []); } set selectedItems(v) { if (this.mSelectedItemsWatcher) { this.mSelectedItemsWatcher.dispose(); this.mSelectedItemsWatcher = null; } this.mSelectedItems = v; if (v) { this.mSelectedItemsWatcher = this.registerDisposable(AtomBinder.add_CollectionChanged(v, (t, k, i, item) => { this.onSelectedItemsChanged(k, i, item); })); } } get selectedIndex() { if (!this.mItems) { return -1; } const item = this.selectedItem; return this.mItems.indexOf(item); } set selectedIndex(n) { if (!this.mItems) { return; } if (n <= -1 || n >= this.mItems.length) { this.selectedItem = null; return; } this.selectedItem = this.mItems[n]; } hasProperty(name) { if (/^(items|itemsPresenter|value|valuePath|valueSeparator|label|labelPath|selectedItems|selectedItem|selectedIndex|uiVirtualize|viewModel|localViewModel|data)$/.test(name)) { return true; } return super.hasProperty(name); } dispose(e) { this.items = null; this.selectedItems = null; super.dispose(e); } onPropertyChanged(name) { switch (name) { case "itemsPresenter": case "itemTemplate": case "labelPath": case "valuePath": case "items": case "filter": case "sort": if (this.mItems) { this.invalidateItems(); } break; } } set selectAll(v) { if (v === undefined || v === null) { return; } this.mSelectedItems.length = 0; const items = this.mItems; if (v && items) { for (const itm of items) { this.mSelectedItems.push(itm); } } this.mSelectAll = true; AtomBinder.refreshItems(this.mSelectedItems); } resetVirtualContainer() { const ip = this.itemsPresenter; if (ip) { this.disposeChildren(ip); } this.mFirstChild = null; this.mLastChild = null; this.mScrollerSetup = false; this.mScopes = null; this.unbindEvent(this.mVirtualContainer, "scroll"); } postVirtualCollectionChanged() { this.app.callLater(() => { this.onVirtualCollectionChanged(); }); } onVirtualCollectionChanged() { const ip = this.itemsPresenter; const items = this.items; if (!items.length) { this.resetVirtualContainer(); return; } this.validateScroller(); const fc = this.mFirstChild; const lc = this.mLastChild; const vc = this.mVirtualContainer; const vcHeight = AtomUI.innerHeight(vc); const vcScrollHeight = vc.scrollHeight; if (isNaN(vcHeight) || vcHeight <= 0 || vcScrollHeight <= 0) { setTimeout(() => { this.onVirtualCollectionChanged(); }, 1000); return; } const vcWidth = AtomUI.innerWidth(vc); let avgHeight = this.mAvgHeight; let avgWidth = this.mAvgWidth; const itemsHeight = vc.scrollHeight - AtomUI.outerHeight(fc) - AtomUI.outerHeight(lc); const itemsWidth = AtomUI.innerWidth(ip); const element = this.element; let ce; let ae = new AtomEnumerator(items); if (this.mTraining) { if (vcHeight >= itemsHeight) { ce = lc.previousElementSibling; if (ce !== fc) { const data = ce.atomControl.data; while (ae.next()) { if (ae.current === data) { break; } } } if (ae.next()) { const data = ae.current; const elementChild = this.createChild(null, data); ip.insertBefore(elementChild.element, lc); this.postVirtualCollectionChanged(); } } else { let totalVisibleItems = 0; ce = fc.nextElementSibling; let allHeight = 0; let allWidth = 0; while (ce !== lc) { totalVisibleItems++; allHeight += AtomUI.outerHeight(ce); allWidth += AtomUI.outerWidth(ce); ce = ce.nextElementSibling; } avgHeight = allHeight / totalVisibleItems; avgWidth = allWidth / totalVisibleItems; totalVisibleItems--; this.mAvgHeight = avgHeight; this.mAvgWidth = avgWidth; const columns = Math.floor(vcWidth / avgWidth); const allRows = Math.ceil(items.length / columns); const visibleRows = Math.ceil(totalVisibleItems / columns); console.log({ avgWidth, avgHeight, totalVisibleItems, allRows, columns }); this.mAllRows = allRows; this.mColumns = columns; this.mVisibleRows = visibleRows; this.mVisibleHeight = visibleRows * avgHeight; lc.style.height = (allRows - visibleRows + 1) * avgHeight + "px"; this.mTraining = false; this.mReady = true; this.postVirtualCollectionChanged(); } return; } const self = this; this.lastScrollTop = vc.scrollTop; if (this.mIsChanging) { return; } this.mIsChanging = true; const block = Math.floor(this.mVisibleHeight / avgHeight); const itemsInBlock = this.mVisibleRows * this.mColumns; const index = Math.floor(vc.scrollTop / this.mVisibleHeight); const itemIndex = index * itemsInBlock; if (itemIndex >= items.length) { this.mIsChanging = false; return; } const lastIndex = Math.min((Math.max(index, 0) + 3) * itemsInBlock - 1, items.length - 1); const firstIndex = Math.max(0, index * itemsInBlock); ce = fc.nextElementSibling; const firstItem = fc.nextElementSibling; const lastItem = lc.previousElementSibling; if (firstItem !== lastItem) { const firstVisibleIndex = items.indexOf(firstItem.atomControl.data); const lastVisibleIndex = items.indexOf(lastItem.atomControl.data); console.log({ firstVisibleIndex, firstIndex, lastVisibleIndex, lastIndex }); if (firstIndex >= firstVisibleIndex && lastIndex <= lastVisibleIndex) { console.log("All items are visible..."); this.mIsChanging = false; return; } } const remove = []; const cache = {}; while (ce !== lc) { const c = ce; ce = ce.nextElementSibling; const s = items.indexOf(c.atomControl.data); cache[s] = c; remove.push(c); } this.app.dispatcher.pause(); ae = new AtomEnumerator(items); for (let i = 0; i < firstIndex; i++) { ae.next(); } let after = fc; let last = null; const add = []; for (let i = firstIndex; i <= lastIndex; i++) { if (!ae.next()) { break; } const index2 = ae.currentIndex; const data = ae.current; let elementChild = cache[index2]; if (elementChild && element.atomControl.data === data) { cache[index2] = null; } else { elementChild = this.createChild(null, data).element; } elementChild.before = after; add.push(elementChild); after = elementChild; last = index2; } const h = (this.mAllRows - block * 3) * avgHeight - index * this.mVisibleHeight; console.log("last child height = " + h); this.app.callLater(() => { const oldHeight = AtomUI.outerHeight(fc); const newHeight = index * this.mVisibleHeight; const diff = newHeight - oldHeight; const oldScrollTop = vc.scrollTop; const a = new AtomEnumerator(add); while (a.next()) { const ec = a.current; ip.insertBefore(ec, ec.before.nextElementSibling); ec.before = null; } fc.style.height = newHeight + "px"; for (const iterator of remove) { if (!iterator.before) { iterator.atomControl.dispose(); } iterator.remove(); } lc.style.height = h + "px"; console.log(`Old: ${oldScrollTop} Diff: ${diff} Old Height: ${oldHeight} Height: ${newHeight}`); this.mIsChanging = false; }); this.app.dispatcher.start(); AtomBinder.refreshValue(this, "childAtomControls"); } isSelected(item) { let selectedItem = null; for (const iterator of this.mSelectedItems) { selectedItem = iterator; if (selectedItem === item) { return true; } } return false; } bringIntoView(data) { this.app.callLater(() => { for (let item of ChildEnumerator.enumerate(this.itemsPresenter || this.element)) { const dataItem = item.atomControl ? item.atomControl.data : item; if (dataItem === data) { item.scrollIntoView(); return; } } }); } bringSelectionIntoView() { if (this.uiVirtualize) { const index = this.selectedIndex; if (!this.mReady) { setTimeout(() => { this.bringSelectionIntoView(); }, 1000); return; } const avgHeight = this.mAvgHeight; const vcHeight = AtomUI.innerHeight(this.mVirtualContainer); const block = Math.ceil(vcHeight / avgHeight); const itemsInBlock = block * this.mColumns; const scrollTop = Math.floor(index / itemsInBlock); AtomUI.scrollTop(this.mVirtualContainer, scrollTop * vcHeight); return; } for (let item of ChildEnumerator.enumerate(this.itemsPresenter || this.element)) { const dataItem = item.atomControl ? item.atomControl.data : item; if (this.isSelected(dataItem)) { setTimeout(() => { item.scrollIntoView(); }, 1000); return; } } } updateSelectionBindings() { this.version = this.version + 1; if (this.mSelectedItems && this.mSelectedItems.length) { this.mValue = undefined; } AtomBinder.refreshValue(this, "value"); AtomBinder.refreshValue(this, "selectedItem"); AtomBinder.refreshValue(this, "selectedItems"); AtomBinder.refreshValue(this, "selectedIndex"); if (!this.mSelectedItems.length) { if (this.mSelectAll === true) { this.mSelectAll = false; AtomBinder.refreshValue(this, "selectAll"); } } } onSelectedItemsChanged(type, index, item) { if (!this.mOnUIChanged) { if (this.autoScrollToSelection) { this.bringSelectionIntoView(); } } this.updateSelectionBindings(); } hasItems() { return this.mItems !== undefined && this.mItems !== null; } invalidateItems() { if (this.pendingInits || this.isUpdating) { setTimeout(() => { this.invalidateItems(); }, 5); return; } if (this.itemsInvalidated) { clearTimeout(this.itemsInvalidated); this.itemsInvalidated = 0; } this.itemsInvalidated = setTimeout(() => { this.itemsInvalidated = 0; this.onCollectionChangedInternal("refresh", -1, null); }, 5); } onCollectionChanged(key, index, item) { if (!this.mItems) { return; } if (!this.itemTemplate) { return; } if (!this.itemsPresenter) { this.itemsPresenter = this.element; } this.version = this.version + 1; if (/reset|refresh/i.test(key)) { this.resetVirtualContainer(); } if (/remove/gi.test(key)) { const ip = this.itemsPresenter || this.element; for (let ce of ChildEnumerator.enumerate(ip)) { const c = ce; if (c.atomControl && c.atomControl.data === item) { c.atomControl.dispose(); ce.remove(); break; } } return; } if (this.uiVirtualize) { this.onVirtualCollectionChanged(); return; } const parentScope = undefined; let items = this.mFilter ? this.mItems.filter(this.mFilter) : this.mItems; let s = this.sort; if (s) { if (typeof s === "string") { const sp = s; s = (l, r) => { const lv = (l[sp] || "").toString(); const rv = (r[sp] || "").toString(); return lv.toLowerCase().localeCompare(rv.toLowerCase()); }; } items = items.sort(s); } if (/add/gi.test(key)) { const lastItem = items[index]; let last = this.itemsPresenter.children.item(index); const df2 = document.createDocumentFragment(); this.createChild(df2, lastItem); if (last) { this.itemsPresenter.insertBefore(df2, last); } else { this.itemsPresenter.appendChild(df2); } return; } const element = this.itemsPresenter; this.disposeChildren(this.itemsPresenter); const added = []; const ip = this.itemsPresenter || this.element; for (const mItem of items) { const data = mItem; const ac = this.createChild(null, data); ip.appendChild(ac.element); } } preCreate() { this.mAllowSelectFirst = false; this.allowMultipleSelection = false; this.valuePath = "value"; this.labelPath = "label"; this.version = 1; this.autoScrollToSelection = false; this.sort = null; this.valueSeparator = ", "; this.uiVirtualize = false; this.mSelectAll = false; this.mItems = null; this.selectedItems = []; this.itemTemplate = AtomItemsControlItemTemplate; super.preCreate(); } onCollectionChangedInternal(key, index, item) { const value = this.value; try { this.isUpdating = true; this.onCollectionChanged(key, index, item); if (value) { if (!(value || this.mAllowSelectFirst)) { AtomBinder.clear(this.mSelectedItems); } } if (value != null) { this.value = value; if (this.selectedIndex !== -1) { return; } else { this.mValue = undefined; } } } finally { this.app.callLater(() => { this.isUpdating = false; }); } } set allowSelectFirst(b) { b = b ? b !== "false" : b; this.mAllowSelectFirst = b; } set filter(f) { if (f === this.mFilter) { return; } this.mFilter = f; AtomBinder.refreshValue(this, "filter"); } onScroll() { if (this.scrollTimeout) { clearTimeout(this.scrollTimeout); } this.scrollTimeout = setTimeout(() => { this.scrollTimeout = 0; this.onVirtualCollectionChanged(); }, 10); } toggleSelection(data) { this.mOnUIChanged = true; this.mValue = undefined; if (this.allowMultipleSelection) { if (this.mSelectedItems.indexOf(data) !== -1) { AtomBinder.removeItem(this.mSelectedItems, data); } else { AtomBinder.addItem(this.mSelectedItems, data); } } else { this.mSelectedItems.length = 1; this.mSelectedItems[0] = data; AtomBinder.refreshItems(this.mSelectedItems); } this.mOnUIChanged = false; } validateScroller() { if (this.mScrollerSetup) { return; } const ip = this.itemsPresenter; const e = this.element; let vc = this.mVirtualContainer; if (!vc) { if (ip === e && !/table/i.test(e.nodeName)) { throw new Error("virtualContainer presenter not found," + "you must put itemsPresenter inside a virtualContainer in order for Virtualization to work"); } else { vc = this.mVirtualContainer = this.element; } } vc.style.overflow = "auto"; this.bindEvent(vc, "scroll", () => { this.onScroll(); }); ip.style.overflow = "hidden"; const isTable = /tbody/i.test(ip.nodeName); let fc; let lc; if (isTable) { fc = document.createElement("TR"); lc = document.createElement("TR"); } else { fc = document.createElement("DIV"); lc = document.createElement("DIV"); } fc.classList.add("sticky"); fc.classList.add("first-child"); lc.classList.add("sticky"); lc.classList.add("last-child"); fc.style.position = "relative"; fc.style.height = "0"; fc.style.width = "100%"; fc.style.clear = "both"; lc.style.position = "relative"; lc.style.height = "0"; lc.style.width = "100%"; lc.style.clear = "both"; this.mFirstChild = fc; this.mLastChild = lc; ip.appendChild(fc); ip.appendChild(lc); this.mTraining = true; this.mScrollerSetup = true; } createChild(df, data) { const t = this.itemTemplate; const ac = this.app.resolve(t, true); const e = ac.element; e._logicalParent = this.element; e._templateParent = this; if (df) { df.appendChild(ac.element); } ac.data = data; this.element.dispatchEvent(new CustomEvent("item-created", { bubbles: false, cancelable: false, detail: data })); return ac; } disposeChildren(e) { for (let iterator of ChildEnumerator.enumerate(e)) { const ac = iterator.atomControl; if (ac) { ac.dispose(); } } e.innerHTML = ""; } }); AtomItemsControl.itemTemplate = XNode.prepare("itemTemplate", true, true); AtomItemsControlItemTemplate = class AtomItemsControlItemTemplate extends AtomControl { create() { this.runAfterInit(() => { const tp = this.element._templateParent; this.element.textContent = this.data[tp.valuePath]; }); } }; } }; }); //# sourceMappingURL=AtomItemsControl.js.map