UNPKG

@nstudio/ui-collectionview

Version:

Customized NativeScript CollectionView for high performance lists. Supports vertical and horizontal modes, templating, and more.

147 lines 6.41 kB
import { ContentView, profile } from '@nativescript/core'; import { NativeViewElementNode, TemplateElement, createElement, registerElement } from 'svelte-native/dom'; //@ts-ignore import { flush } from 'svelte/internal'; import { CollectionView } from '..'; const SVELTE_VIEW = '_svelteViewRef'; class SvelteKeyedTemplate { constructor(key, templateEl) { this._key = key; this._templateEl = templateEl; } get component() { return this._templateEl.component; } get key() { return this._key; } createView() { // create a proxy element to eventually contain our item (once we have one to render) // TODO is StackLayout the best choice here? // const wrapper = createElement('StackLayout') as NativeViewElementNode<View>; const nativeEl = new ContentView(); // because of the way {N} works we cant use that wrapper as the target for the component // it will trigger uncessary {N} component updates because the parent view is already attached nativeEl.__SvelteComponentBuilder__ = (parentView, props) => { nativeEl.__SvelteComponent__ = new this.component({ target: parentView, props, }); }; return nativeEl; } } export default class CollectionViewViewElement extends NativeViewElementNode { constructor() { super('collectionview', CollectionView); const nativeView = this.nativeView; nativeView.itemViewLoader = (viewType) => this.loadView(viewType); this.nativeView.on(CollectionView.itemLoadingEvent, this.updateListItem, this); this.nativeView.on(CollectionView.itemDisposingEvent, this.disposeListItem, this); } loadView(viewType) { if (Array.isArray(this.nativeElement.itemTemplates)) { const keyedTemplate = this.nativeElement.itemTemplates.find((t) => t.key === 'default'); if (keyedTemplate) { return keyedTemplate.createView(); } } const componentClass = this.getComponentForView(viewType); if (!componentClass) return null; const nativeEl = new ContentView(); const builder = (parentView, props) => { nativeEl.__SvelteComponent__ = new componentClass({ target: parentView, props, }); }; // in svelte we want to add the wrapper as a child of the collectionview ourselves nativeEl.__SvelteComponentBuilder__ = builder; return nativeEl; } // For some reason itemTemplateSelector isn't defined as a "property" on radListView, so when we set the property, it is lowercase (due to svelte's forced downcasing) // we intercept and fix the case here. setAttribute(fullkey, value) { if (fullkey.toLowerCase() === 'itemtemplateselector') { fullkey = 'itemTemplateSelector'; } super.setAttribute(fullkey, value); } getComponentForView(viewType) { const normalizedViewType = viewType.toLowerCase(); const templateEl = this.childNodes.find((n) => n.tagName === 'template' && String(n.getAttribute('type')).toLowerCase() === normalizedViewType); if (!templateEl) return null; return templateEl.component; } onInsertedChild(childNode, index) { super.onInsertedChild(childNode, index); if (childNode instanceof TemplateElement) { const key = childNode.getAttribute('key') || 'default'; // const templates = !this.nativeView.itemTemplates || typeof this.nativeView.itemTemplates === 'string' ? [] : (this.nativeView.itemTemplates as any[]); // we need to reassign or the update wont be seen this.nativeView.addTemplate(key, new SvelteKeyedTemplate(key, childNode)); // = templates.concat([new SvelteKeyedTemplate(key, childNode)]); } } onRemovedChild(childNode) { super.onRemovedChild(childNode); if (childNode instanceof TemplateElement) { const key = childNode.getAttribute('key') || 'default'; this.nativeView.removeTemplate(key); } } disposeListItem(args) { const _view = args.view; if (_view.__SvelteComponent__) { _view.__SvelteComponent__.$destroy(); _view.__SvelteComponent__ = null; } } updateListItem(args) { const _view = args.view; const props = { item: args.bindingContext, index: args.index }; const componentInstance = _view.__SvelteComponent__; if (!componentInstance) { if (_view.__SvelteComponentBuilder__) { const dummy = createElement('fragment'); _view.__SvelteComponentBuilder__(dummy, props); _view.__SvelteComponentBuilder__ = null; _view.__CollectionViewCurrentIndex__ = args.index; const nativeEl = dummy.firstElement().nativeView; _view.content = nativeEl; } } else { // ensure we dont do unnecessary tasks if index did not change // console.log('updateListItem', args.index, _view.__CollectionViewCurrentIndex__); _view.__CollectionViewCurrentIndex__ = args.index; // _suspendNativeUpdates with special parameters for Akylas fork to preven requestLayout //@ts-ignore _view._suspendNativeUpdates(0, true, true); _view._batchUpdate(() => { componentInstance.$set(props); flush(); // we need to flush to make sure update is applied right away }); //@ts-ignore _view._resumeNativeUpdates(0, true, true, true); } } static register() { registerElement('collectionview', () => new CollectionViewViewElement()); } } __decorate([ profile, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], CollectionViewViewElement.prototype, "disposeListItem", null); __decorate([ profile, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], CollectionViewViewElement.prototype, "updateListItem", null); //# sourceMappingURL=index.js.map