@web-atoms/core
Version:
710 lines (709 loc) • 24.9 kB
JavaScript
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