UNPKG

opal-components

Version:

[Rionite](https://github.com/Riim/Rionite) component set.

230 lines (184 loc) 5.46 kB
import { getText } from '@riim/gettext'; import { mixin } from '@riim/mixin'; import { nextTick } from '@riim/next-tick'; import { Cell, ObservableList } from 'cellx'; import { computed, observable } from 'cellx-decorators'; import { Component, IDisposableCallback, IDisposableTimeout } from 'rionite'; import './index.css'; import template from './template.nelm'; export interface IDataListItem { [name: string]: any; } export interface IDataProvider { getItems(query?: string): PromiseLike<{ items: Array<IDataListItem> }>; getItems( count: number, after?: string, query?: string ): PromiseLike<{ items: Array<IDataListItem>; total?: number; }>; } let defaultDataListItemSchema = Object.freeze({ value: 'id', text: 'name' }); @Component.Config<OpalLoadedList>({ elementIs: 'OpalLoadedList', params: { dataListItemSchema: { type: eval, default: defaultDataListItemSchema, readonly: true }, dataProvider: { type: Object, readonly: true }, count: 100, query: String, preloading: { default: false, readonly: true } }, i18n: { nothingFound: getText.t('Ничего не найдено') }, template }) export class OpalLoadedList extends Component { static defaultDataListItemSchema = defaultDataListItemSchema; @observable dataList = new ObservableList<IDataListItem>(); _dataListItemTextFieldName: string; @observable total: number | undefined; dataProvider: IDataProvider; _isScrollingInProcessing: boolean = false; @observable _isLoadingCheckPlanned = false; _loadingCheckTimeout: IDisposableTimeout; @observable loading = false; _requestCallback: IDisposableCallback; _lastRequestedQuery: string | null = null; _lastLoadedQuery: string | null = null; @computed get empty(): boolean { return !this.dataList.length; } @computed get isLoaderShown(): boolean { return this.total === undefined || this.dataList.length < this.total || this.loading; } @computed get isNothingFoundShown(): boolean { return this.total === 0 && !this._isLoadingCheckPlanned && !this.loading; } initialize() { let params = this.params; this._dataListItemTextFieldName = params.dataListItemSchema.text || (this.constructor as typeof OpalLoadedList).defaultDataListItemSchema.text; if (!params.$specified.has('dataProvider')) { throw new TypeError('Parameter "dataProvider" is required'); } let dataProvider = params.dataProvider; if (!dataProvider) { throw new TypeError('"dataProvider" is not defined'); } this.dataProvider = dataProvider; } elementAttached() { this.listenTo(this, 'param-query-change', this._onParamQueryChange); this.listenTo(this.element, 'scroll', this._onElementScroll); if (this.params.preloading) { this._load(); } else { this.checkLoading(); } } _onParamQueryChange() { if (this.loading) { this._requestCallback.cancel(); this.loading = false; } this.dataList.clear(); this.total = undefined; if (this._isLoadingCheckPlanned) { this._loadingCheckTimeout.clear(); } else { this._isLoadingCheckPlanned = true; } this._loadingCheckTimeout = this.setTimeout(() => { this._isScrollingInProcessing = false; this._isLoadingCheckPlanned = false; this.checkLoading(); }, 300); } _onElementScroll() { if (this._isScrollingInProcessing) { return; } this._isScrollingInProcessing = true; if (this._isLoadingCheckPlanned) { this._loadingCheckTimeout.clear(); } else { this._isLoadingCheckPlanned = true; } this._loadingCheckTimeout = this.setTimeout(() => { this._isScrollingInProcessing = false; this._isLoadingCheckPlanned = false; this.checkLoading(); }, 150); } checkLoading() { if ( this.params.query === this._lastRequestedQuery && (this.loading || (this.total !== undefined && this.dataList.length == this.total)) ) { return; } let elRect = this.element.getBoundingClientRect(); if ( elRect.height && elRect.bottom > this.$<Component>('loader')!.element.getBoundingClientRect().top ) { this._load(); } } _load() { if (this.loading) { this._requestCallback.cancel(); } let params = this.params; let infinite = this.dataProvider.getItems.length >= 2; let query: string | null = (this._lastRequestedQuery = params.query); let args = [query]; if (infinite) { args.unshift( params.count, this.dataList.length ? this.dataList.get(-1)![ params.dataListItemSchema.value || (this.constructor as typeof OpalLoadedList).defaultDataListItemSchema .value ] : null ); } this.loading = true; this.dataProvider.getItems.apply(this.dataProvider, args).then( (this._requestCallback = this.registerCallback(function( this: OpalLoadedList, data: { items: Array<IDataListItem>; total?: number } ) { this.loading = false; let items = data.items; this.total = infinite && data.total !== undefined ? data.total : items.length; if (query === this._lastLoadedQuery) { this.dataList.addRange(items); } else { this.dataList.clear().addRange(items); this._lastLoadedQuery = query; } Cell.forceRelease(); this.emit('loaded'); nextTick(() => { this.checkLoading(); }); })) ); } _getListItemContext( context: { [name: string]: any }, content: Component ): { [name: string]: any } { return mixin(Object.create(context), content.params.$context, ['$component']); } }