import React from 'react'; import PropTypes from 'prop-types'; import { VirtualScrollList } from 'dynamic-virtual-scroll/VirtualScrollList.js'; import { Spinner24 } from './LoadingOverlay.js'; /** * Наследование компонентов react-toolbox'а делается, но через такую жопууууу.... */ import { Input } from 'react-toolbox/lib/input'; import { Chip } from 'react-toolbox/lib/chip'; import { autocompleteFactory } from 'react-toolbox/lib/autocomplete/Autocomplete.js'; import autocomplete_theme from 'react-toolbox/lib/autocomplete/theme.css'; import { themr } from 'react-css-themr'; import { AUTOCOMPLETE } from 'react-toolbox/lib/identifiers.js'; const RawAutocomplete = autocompleteFactory(Chip, Input); /** * Автокомплит для отображения БОЛЬШОГО ДЕРЕВА */ class RawVirtualTreeAutocomplete extends RawAutocomplete { static propTypes = { ...RawAutocomplete.constructor.propTypes, maxHeight: PropTypes.number, parentIdField: PropTypes.string.isRequired, leafOnly: PropTypes.bool, renderItem: PropTypes.func, } tree = [] renderSuggestion = (idx) => { const { theme } = this.props; const item = this.tree[idx]; if (!item) { return null; } const key = item.item[this.props.valueKey]; const enabled = !this.props.leafOnly || !this.by_parent[key]; const style = { paddingLeft: (10+item.level*16)+'px', color: enabled ? '' : 'gray' }; if (this.valueHash[key]) { style.background = '#e5e8ea'; } let text; if (this.props.renderItem) { text = this.props.renderItem(item.item, item.level, style); } else { text = item.item[this.props.labelKey]; } return (
{text}
); } setListScroll = (e) => { if (e) { let k = null; for (let i in this.valueHash) { k = i; break; } if (k) { // Если есть значение, при изначальном появлении списка проскроллим к нему let pos = this.tree.findIndex(e => e.item[this.props.valueKey] == k); e.scrollToItem(pos); } } } renderSuggestionList() { const { theme } = this.props; const { top, bottom, maxHeight, left, width } = this.state; let maxh = Number((''+maxHeight).replace('px', '')); if (this.props.maxHeight && maxh > this.props.maxHeight) { maxh = this.props.maxHeight; } return (
Идёт загрузка...
: null)} totalItems={(this.tree||[]).length} minRowHeight={36} viewportHeight={maxh} renderItem={this.renderSuggestion} ref={this.setListScroll} /> ); } addItems(parent, level, add_all) { for (let item of this.by_parent[parent]||[]) { if (add_all || !this.filtered || this.filtered[item[this.props.valueKey]]) { this.tree.push({ item, level }); this.addItems(item[this.props.valueKey], level+1, add_all || this.filtered && this.filtered[item[this.props.valueKey]] == 2); } } } render() { if (this.state.focus) { if (this.props.source != this.prevSource) { this.state.expanded = {}; const pf = this.props.parentIdField; const idf = this.props.valueKey; let by_parent = {}; let by_id = {}; for (let item of this.props.source) { by_id[item[idf]] = item; by_parent[item[pf]||''] = by_parent[item[pf]||''] || []; by_parent[item[pf]||''].push(item); } this.by_id = by_id; this.by_parent = by_parent; } if (this.state.expanded != this.prevExpanded || this.state.query != this.prevQuery) { if (this.state.query != this.prevQuery || this.props.source != this.prevSource) { this.filtered = null; if (this.state.query) { const pf = this.props.parentIdField; this.filtered = {}; for (let k of this.suggestions().keys()) { this.filtered[k] = 2; let c = k; while (c) { this.filtered[c] = this.filtered[c] || 1; if (!this.by_id[c]) break; c = this.by_id[c][pf]; } } } } // FIXME Здесь могло быть ваше раскрытие узлов дерева this.tree = []; this.addItems('', 0, false); } if (!this.valueHash || this.props.value != this.prevValue) { if (!this.props.value) { this.valueHash = {}; } else if (this.props.multiple) { this.valueHash = (this.props.value instanceof Array ? this.props.value.reduce((a, c) => { a[c] = true; return a; }, {}) : this.props.value); } else { this.valueHash = { [this.props.value]: true }; } } this.prevExpanded = this.state.expanded; this.prevQuery = this.state.query; this.prevSource = this.props.source; this.prevValue = this.props.value; } return super.render(); } } export const VirtualTreeAutocomplete = themr(AUTOCOMPLETE, autocomplete_theme, { withRef: true })(RawVirtualTreeAutocomplete);