@nstudio/ui-collectionview
Version:
Customized NativeScript CollectionView for high performance lists. Supports vertical and horizontal modes, templating, and more.
670 lines • 24.8 kB
JavaScript
var CollectionViewBase_1;
import { Builder, CSSType, Label, Observable, ObservableArray, PercentLength, Property, ProxyViewContainer, Trace, Utils, View, addWeakEventListener, booleanConverter, heightProperty, makeParser, makeValidator, profile, removeWeakEventListener, widthProperty } from '@nativescript/core';
export const CollectionViewTraceCategory = 'NativescriptCollectionView';
// iOS only
export var ContentInsetAdjustmentBehavior;
(function (ContentInsetAdjustmentBehavior) {
ContentInsetAdjustmentBehavior[ContentInsetAdjustmentBehavior["Always"] = 0] = "Always";
ContentInsetAdjustmentBehavior[ContentInsetAdjustmentBehavior["Automatic"] = 1] = "Automatic";
ContentInsetAdjustmentBehavior[ContentInsetAdjustmentBehavior["Never"] = 2] = "Never";
ContentInsetAdjustmentBehavior[ContentInsetAdjustmentBehavior["ScrollableAxes"] = 3] = "ScrollableAxes";
})(ContentInsetAdjustmentBehavior || (ContentInsetAdjustmentBehavior = {}));
export var CLogTypes;
(function (CLogTypes) {
CLogTypes[CLogTypes["log"] = 0] = "log";
CLogTypes[CLogTypes["info"] = 1] = "info";
CLogTypes[CLogTypes["warning"] = 2] = "warning";
CLogTypes[CLogTypes["error"] = 3] = "error";
})(CLogTypes || (CLogTypes = {}));
export const CLog = (type, ...args) => {
Trace.write(args.map((a) => (a && typeof a === 'object' ? a.toString() : a)).join(' '), CollectionViewTraceCategory, type);
};
const autoEffectiveRowHeight = 0;
const autoEffectiveColWidth = 0;
// export * from 'ui/core/view';
export var ListViewViewTypes;
(function (ListViewViewTypes) {
ListViewViewTypes[ListViewViewTypes["ItemView"] = 0] = "ItemView";
})(ListViewViewTypes || (ListViewViewTypes = {}));
export var knownTemplates;
(function (knownTemplates) {
knownTemplates.itemTemplate = 'itemTemplate';
})(knownTemplates || (knownTemplates = {}));
export var knownMultiTemplates;
(function (knownMultiTemplates) {
knownMultiTemplates.itemTemplates = 'itemTemplates';
})(knownMultiTemplates || (knownMultiTemplates = {}));
function toDevicePixels(length, auto = Number.NaN, parentAvailableWidth = Number.NaN) {
if (length === 'auto') {
// tslint:disable-line
return auto;
}
if (typeof length === 'number') {
return Math.floor(Utils.layout.toDevicePixels(length));
}
if (!length) {
return auto;
}
switch (length.unit) {
case 'px':
return Math.floor(length.value);
case '%':
return Math.floor(parentAvailableWidth * length.value);
case 'dip':
default:
return Math.floor(Utils.layout.toDevicePixels(length.value));
}
}
let CollectionViewBase = CollectionViewBase_1 = class CollectionViewBase extends View {
static registerPlugin(key, plugin) {
this.plugins[key] = plugin;
}
static registerLayoutStyle(style, generator) {
this.layoutStyles[style] = generator;
}
constructor() {
super();
this._innerWidth = 0;
this._innerHeight = 0;
this._dataUpdatesSuspended = false;
this.layoutStyle = 'grid';
this.plugins = [];
// public _itemIdGenerator: (item: any, index: number, items: any) => number = (_item: any, index: number) => index;
this._itemIdGenerator = null;
this.onSpanSizeChanged = (oldValue, newValue) => {
this.spanSize = newValue;
this.refresh();
};
this.onItemOverlapChanged = (oldValue, newValue) => {
this.itemOverlap = newValue;
this.refresh();
};
this._isDataDirty = false;
this._defaultTemplate = {
key: 'default',
createView: () => {
if (this.itemTemplate) {
return Builder.parse(this.itemTemplate, this);
}
return undefined;
},
};
this._itemTemplatesInternal = new Map();
this._itemTemplatesInternal.set(this._defaultTemplate.key, this._defaultTemplate);
}
notifyForItemAtIndex(eventName, view, index, bindingContext, native) {
throw new Error('Method not implemented.');
}
updateInnerSize() {
const lastInnerWidth = this._innerWidth;
const lastInnerHeight = this._innerHeight;
const width = this.getMeasuredWidth();
const height = this.getMeasuredHeight();
if (width === 0 || height === 0) {
return false;
}
this._innerWidth = width - this.effectivePaddingLeft - this.effectivePaddingRight;
if (this.colWidth) {
let newValue = toDevicePixels(this.colWidth, autoEffectiveColWidth, this._innerWidth); // We cannot use 0 for auto as it throws for android.
if (__ANDROID__) {
newValue = Math.floor(newValue);
}
if (newValue !== this._effectiveColWidth) {
this._effectiveColWidth = newValue;
}
}
this._innerHeight = height - this.effectivePaddingTop - this.effectivePaddingBottom;
if (this.rowHeight) {
let newValue = toDevicePixels(this.rowHeight, autoEffectiveRowHeight, this._innerHeight);
if (__ANDROID__) {
newValue = Math.floor(newValue);
}
if (newValue !== this._effectiveRowHeight) {
this._effectiveRowHeight = newValue;
}
}
return lastInnerWidth !== this._innerWidth || lastInnerHeight !== this._innerHeight;
}
onMeasure(widthMeasureSpec, heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
this.updateInnerSize();
}
_prepareItem(view, index) {
const context = this.getItemAtIndex(index);
if (view) {
// we check old bindingContext to see if properties disappeared.
// if so we set them to null for the View to update
if (view.bindingContext && view.bindingContext !== context && typeof context === 'object') {
Object.keys(view.bindingContext).forEach((k) => {
if (!context.hasOwnProperty(k)) {
context[k] = null;
}
});
view.bindingContext = context;
}
}
return context;
}
notifyLoading(args) {
this.notify(args);
}
getItemAtIndex(index) {
// will be overriden in onItemsChangedInternal
const thisItems = this.items;
return thisItems.getItem ? thisItems.getItem(index) : thisItems[index];
}
isHorizontal() {
return this.orientation === 'horizontal';
}
computeSpanCount() {
let spanCount = 1;
if (this.isHorizontal()) {
if (this._effectiveRowHeight) {
spanCount = Math.max(Math.floor(this._innerHeight / this._effectiveRowHeight), 1) || 1;
}
}
else {
if (this._effectiveColWidth) {
spanCount = Math.max(Math.floor(this._innerWidth / this._effectiveColWidth), 1) || 1;
}
}
return spanCount;
}
_onRowHeightPropertyChanged(oldValue, newValue) {
this.refresh();
}
_onColWidthPropertyChanged(oldValue, newValue) {
this.refresh();
}
onItemViewLoaderChanged() { }
get itemViewLoader() {
return this._itemViewLoader;
}
set itemViewLoader(value) {
if (this._itemViewLoader !== value) {
this._itemViewLoader = value;
this.onItemViewLoaderChanged();
}
}
get padding() {
return this.style.padding;
}
set padding(value) {
this.style.padding = value;
}
get paddingTop() {
return this.style.paddingTop;
}
set paddingTop(value) {
this.style.paddingTop = value;
}
get paddingRight() {
return this.style.paddingRight;
}
set paddingRight(value) {
this.style.paddingRight = value;
}
get paddingBottom() {
return this.style.paddingBottom;
}
set paddingBottom(value) {
this.style.paddingBottom = value;
}
get paddingLeft() {
return this.style.paddingLeft;
}
set paddingLeft(value) {
this.style.paddingLeft = value;
}
resolveTemplateView(template) {
return Builder.parse(template, this);
}
_getDefaultItemContent() {
const lbl = new Label();
lbl['defaultItemView'] = true;
lbl.bind({
targetProperty: 'text',
sourceProperty: '$value',
});
return lbl;
}
getTemplateFromSelector(templateKey) {
const key = templateKey.toLowerCase();
if (this._itemTemplatesInternal.has(key)) {
return this._itemTemplatesInternal.get(key);
}
return this._itemTemplatesInternal.get('default');
}
getViewForViewType(viewType, templateKey) {
let newView;
if (templateKey) {
const template = this.getTemplateFromSelector(templateKey);
newView = template.createView();
}
if (!newView && this._itemViewLoader !== undefined) {
newView = this._itemViewLoader(templateKey);
}
if (newView) {
return newView;
}
let templateString;
switch (viewType) {
case ListViewViewTypes.ItemView:
templateString = this.itemTemplate;
if (templateString === undefined) {
return undefined;
// return this._getDefaultItemContent();
}
break;
}
return templateString === undefined ? undefined : this.resolveTemplateView(templateString);
}
onItemTemplateSelectorChanged(oldValue, newValue) {
if (typeof newValue === 'string') {
if (!this._itemTemplateSelectorBindable) {
this._itemTemplateSelectorBindable = new ProxyViewContainer();
}
this._itemTemplateSelectorBindable.bind({
sourceProperty: null,
targetProperty: 'templateKey',
expression: newValue,
});
this._itemTemplateSelector = function (item, index, items) {
item['$index'] = index;
this._itemTemplateSelectorBindable.bindingContext = item;
return this._itemTemplateSelectorBindable.get('templateKey');
};
}
else if (typeof newValue === 'function') {
this._itemTemplateSelector = newValue;
}
}
onItemIdGeneratorChanged(oldValue, newValue) {
if (typeof newValue === 'string') {
if (!this._itemIdGeneratorBindable) {
this._itemIdGeneratorBindable = new ProxyViewContainer();
}
this._itemIdGeneratorBindable.bind({
sourceProperty: null,
targetProperty: 'itemId',
expression: newValue,
});
this._itemIdGenerator = function (item, index, items) {
item['$index'] = index;
this._itemIdGeneratorBindable.bindingContext = item;
return this._itemIdGeneratorBindable.get('itemId');
};
}
else if (typeof newValue === 'function') {
this._itemIdGenerator = newValue;
}
}
onTemplateAdded(t) { }
onTemplateRemoved(key) { }
addTemplate(key, t) {
if (!t.key) {
t.key = t._key;
delete t._key;
}
this._itemTemplatesInternal.set(t.key.toLowerCase(), t);
this.onTemplateAdded(t);
}
removeTemplate(key) {
const didDelete = this._itemTemplatesInternal.delete(key.toLowerCase());
if (didDelete) {
this.onTemplateRemoved(key);
}
}
onItemTemplatesChanged(oldValue, newValue) {
this._itemTemplatesInternal = new Map();
if (newValue) {
newValue.forEach((t) => {
if (!t.key) {
t.key = t._key;
delete t._key;
}
this._itemTemplatesInternal.set(t.key, t);
});
}
if (!this._itemTemplatesInternal.has(this._defaultTemplate.key)) {
this._itemTemplatesInternal.set(this._defaultTemplate.key, this._defaultTemplate);
}
}
onItemTemplateChanged(oldValue, newValue) { }
// onItemTemplateSelectorPropertyChanged(oldValue, newValue) {
// this.onItemTemplateSelectorChanged(oldValue, newValue);
// }
getItemSourceAtIndex(index) {
return this.items.getItem(index);
}
getItemArrayAtIndex(index) {
return this.items[index];
}
onItemsChanged(oldValue, newValue) {
const getItem = newValue && newValue.getItem;
this.isItemsSourceIn = typeof getItem === 'function';
// we override the method to prevent the test on every getItem
this.getItemAtIndex = this.isItemsSourceIn ? this.getItemSourceAtIndex.bind(this) : this.getItemArrayAtIndex.bind(this);
if (oldValue instanceof Observable) {
removeWeakEventListener(oldValue, ObservableArray.changeEvent, this.onSourceCollectionChangedInternal, this);
}
if (newValue instanceof Observable) {
addWeakEventListener(newValue, ObservableArray.changeEvent, this.onSourceCollectionChangedInternal, this);
}
this.refresh();
}
onLoaded() {
super.onLoaded();
if (this._isDataDirty && this._effectiveColWidth !== undefined && this._effectiveRowHeight !== undefined) {
this.refresh();
}
}
onSourceCollectionChanged(event) {
this.refresh();
}
onSourceCollectionChangedInternal(event) {
if (this._dataUpdatesSuspended === false) {
this.onSourceCollectionChanged(event);
}
}
// onItemsChanged(oldValue, newValue) {
// this.onItemsChangedInternal(oldValue, newValue);
// }
[widthProperty.getDefault]() {
return '100%';
}
[heightProperty.getDefault]() {
return '100%';
}
suspendUpdates() {
this._dataUpdatesSuspended = true;
}
updatesSuspended() {
return this._dataUpdatesSuspended;
}
resumeUpdates(refresh) {
this._dataUpdatesSuspended = false;
if (refresh === true) {
this.refresh();
}
}
_callItemReorderedEvent(oldPosition, newPosition, item) {
const args = {
eventName: CollectionViewBase_1.itemReorderedEvent,
object: this,
index: oldPosition,
item,
data: { targetIndex: newPosition },
view: this.draggingView,
};
this.notify(args);
this.draggingView = null;
}
_canReorderToPosition(oldPosition, newPosition, item) {
const args = {
returnValue: true,
eventName: CollectionViewBase_1.itemReorderCheckEvent,
object: this,
index: oldPosition,
item,
data: { targetIndex: newPosition },
view: this.draggingView,
};
this.notify(args);
return args.returnValue;
}
_reorderItemInSource(oldPosition, newPosition, callEvents = true) {
this.suspendUpdates();
const ownerSource = this.items;
const item = this.getItemAtIndex(oldPosition);
ownerSource.splice(oldPosition, 1);
ownerSource.splice(newPosition, 0, item);
this.resumeUpdates(false);
if (callEvents) {
this._callItemReorderedEvent(oldPosition, newPosition, item);
}
}
shouldMoveItemAtIndex(index) {
// if (!this.reorderEnabled) {
// return false;
// }
const item = this.getItemAtIndex(index);
const view = (this.draggingView = this.getViewForItemAtIndex(index));
let args = {
returnValue: true,
eventName: CollectionViewBase_1.itemReorderStartingEvent,
object: this,
index,
item,
view,
};
this.notify(args);
if (!args.returnValue) {
return false;
}
args = {
eventName: CollectionViewBase_1.itemReorderStartedEvent,
object: this,
index,
item,
view,
};
this.notify(args);
return true;
}
};
CollectionViewBase.itemLoadingEvent = 'itemLoading';
CollectionViewBase.itemRecyclingEvent = 'itemRecycling';
CollectionViewBase.itemDisposingEvent = 'itemDisposing';
CollectionViewBase.bindedEvent = 'binded';
CollectionViewBase.scrollEvent = 'scroll';
CollectionViewBase.scrollStartEvent = 'scrollStart';
CollectionViewBase.scrollEndEvent = 'scrollEnd';
CollectionViewBase.itemTapEvent = 'itemTap';
CollectionViewBase.itemHighlightEvent = 'itemHighlight';
CollectionViewBase.itemHighlightEndEvent = 'itemHighlightEnd';
CollectionViewBase.displayItemEvent = 'displayItem';
CollectionViewBase.itemReorderedEvent = 'itemReordered';
CollectionViewBase.itemReorderCheckEvent = 'itemReorderCheck';
CollectionViewBase.itemReorderStartingEvent = 'itemReorderStarting';
CollectionViewBase.itemReorderStartedEvent = 'itemReorderStarted';
CollectionViewBase.loadMoreItemsEvent = 'loadMoreItems';
CollectionViewBase.dataPopulatedEvent = 'dataPopulated';
CollectionViewBase.knownFunctions = ['itemTemplateSelector', 'itemIdGenerator', 'spanSize']; // See component-builder.ts isKnownFunction
CollectionViewBase.plugins = {};
CollectionViewBase.layoutStyles = {};
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", [View, Number]),
__metadata("design:returntype", void 0)
], CollectionViewBase.prototype, "_prepareItem", null);
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], CollectionViewBase.prototype, "notifyLoading", null);
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], CollectionViewBase.prototype, "onItemIdGeneratorChanged", null);
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], CollectionViewBase.prototype, "onItemTemplatesChanged", null);
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], CollectionViewBase.prototype, "onItemsChanged", null);
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], CollectionViewBase.prototype, "onSourceCollectionChanged", null);
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], CollectionViewBase.prototype, "onSourceCollectionChangedInternal", null);
CollectionViewBase = CollectionViewBase_1 = __decorate([
CSSType('CollectionView'),
__metadata("design:paramtypes", [])
], CollectionViewBase);
export { CollectionViewBase };
const defaultRowHeight = 'auto';
export const rowHeightProperty = new Property({
name: 'rowHeight',
defaultValue: defaultRowHeight,
equalityComparer: PercentLength.equals,
valueConverter: PercentLength.parse,
valueChanged: (target, oldValue, newValue) => {
if (target._innerHeight !== 0) {
target._effectiveRowHeight = PercentLength.toDevicePixels(newValue, autoEffectiveRowHeight, target._innerHeight);
target._onRowHeightPropertyChanged(oldValue, newValue);
}
},
});
rowHeightProperty.register(CollectionViewBase);
const defaultColWidth = { unit: '%', value: 1 };
export const colWidthProperty = new Property({
name: 'colWidth',
defaultValue: defaultColWidth,
equalityComparer: PercentLength.equals,
valueConverter: PercentLength.parse,
valueChanged: (target, oldValue, newValue) => {
if (target._innerWidth !== 0) {
target._effectiveColWidth = PercentLength.toDevicePixels(newValue, autoEffectiveColWidth, target._innerWidth);
target._onColWidthPropertyChanged(oldValue, newValue);
}
},
});
colWidthProperty.register(CollectionViewBase);
const converter = makeParser(makeValidator('horizontal', 'vertical'));
export const orientationProperty = new Property({
name: 'orientation',
defaultValue: 'vertical',
affectsLayout: true,
valueChanged: (target, oldValue, newValue) => {
target.refresh();
},
valueConverter: converter,
});
orientationProperty.register(CollectionViewBase);
export const itemTemplateProperty = new Property({
name: 'itemTemplate',
valueChanged(target, oldValue, newValue) {
target.onItemTemplateChanged(oldValue, newValue);
},
});
itemTemplateProperty.register(CollectionViewBase);
export const itemTemplatesProperty = new Property({
name: 'itemTemplates',
valueConverter: (value) => {
if (typeof value === 'string') {
return Builder.parseMultipleTemplates(value);
}
return value;
},
valueChanged(target, oldValue, newValue) {
target.onItemTemplatesChanged(oldValue, newValue);
},
});
itemTemplatesProperty.register(CollectionViewBase);
export const itemTemplateSelectorProperty = new Property({
name: 'itemTemplateSelector',
defaultValue: undefined,
valueChanged(target, oldValue, newValue) {
target.onItemTemplateSelectorChanged(oldValue, newValue);
},
});
itemTemplateSelectorProperty.register(CollectionViewBase);
export const itemIdGeneratorProperty = new Property({
name: 'itemIdGenerator',
defaultValue: undefined,
valueChanged(target, oldValue, newValue) {
target.onItemIdGeneratorChanged(oldValue, newValue);
},
});
itemIdGeneratorProperty.register(CollectionViewBase);
export const itemsProperty = new Property({
name: 'items',
defaultValue: undefined,
valueChanged(target, oldValue, newValue) {
target.onItemsChanged(oldValue, newValue);
},
});
itemsProperty.register(CollectionViewBase);
export const spanSizeProperty = new Property({
name: 'spanSize',
defaultValue: undefined,
valueChanged(target, oldValue, newValue) {
target.onSpanSizeChanged(oldValue, newValue);
},
});
spanSizeProperty.register(CollectionViewBase);
export const isScrollEnabledProperty = new Property({
name: 'isScrollEnabled',
defaultValue: true,
valueConverter: booleanConverter,
});
isScrollEnabledProperty.register(CollectionViewBase);
export const isBounceEnabledProperty = new Property({
name: 'isBounceEnabled',
defaultValue: true,
valueConverter: booleanConverter,
});
isBounceEnabledProperty.register(CollectionViewBase);
export const reverseLayoutProperty = new Property({
name: 'reverseLayout',
defaultValue: false,
valueConverter: booleanConverter,
});
reverseLayoutProperty.register(CollectionViewBase);
export const loadMoreThresholdProperty = new Property({
name: 'loadMoreThreshold',
defaultValue: 1,
valueConverter: (v) => parseInt(v, 10),
});
loadMoreThresholdProperty.register(CollectionViewBase);
export const reorderingEnabledProperty = new Property({
name: 'reorderEnabled',
defaultValue: false,
valueConverter: booleanConverter,
});
reorderingEnabledProperty.register(CollectionViewBase);
export const reorderLongPressEnabledProperty = new Property({
name: 'reorderLongPressEnabled',
defaultValue: false,
valueConverter: booleanConverter,
});
reorderLongPressEnabledProperty.register(CollectionViewBase);
export const scrollBarIndicatorVisibleProperty = new Property({
name: 'scrollBarIndicatorVisible',
defaultValue: true,
valueConverter: booleanConverter,
});
scrollBarIndicatorVisibleProperty.register(CollectionViewBase);
export const autoReloadItemOnLayoutProperty = new Property({
name: 'autoReloadItemOnLayout',
defaultValue: false,
valueConverter: booleanConverter,
});
autoReloadItemOnLayoutProperty.register(CollectionViewBase);
export const itemOverlapProperty = new Property({
name: 'itemOverlap',
defaultValue: undefined,
valueChanged(target, oldValue, newValue) {
target.onItemOverlapChanged(oldValue, newValue);
},
});
itemOverlapProperty.register(CollectionViewBase);
//# sourceMappingURL=common.js.map