// // @description : // @author : Adarsh Pastakia // @copyright : 2016 // @license : MIT import {autoinject, bindable, containerless, bindingMode, customElement, computedFrom, inlineView, children, BindingEngine} from "aurelia-framework"; import {UIEvent} from "../../utils/ui-event"; import {UITreeOptions, UITreeModel} from "../../utils/ui-tree-model"; import * as _ from "lodash"; @autoinject() @customElement('ui-tree') @inlineView(``) export class UITreePanel { constructor(public element: Element, observer: BindingEngine) { this.__searchable = element.hasAttribute('searchable'); this.__subscribeSearch = observer.propertyObserver(this, 'searchText') .subscribe(v => this.__searchTextChanged(v)); } bind() { this.modelChanged(this.model || []); this.valueChanged(this.value); } attached() { UIEvent.queueTask(() => { // let x; // if ((x = this.element.querySelector('.ui-active')) !== null) x.scrollIntoView(); }); } detached() { this.__subscribeSearch.dispose(); } valueChanged(newValue) { if (this.__ignoreChange) return; if (!this.options.showCheckbox) this.__find(this.root.children, newValue, 'active', true, true); else { _.forEach(this.root.children, n => n.isChecked = false); _.forEach((newValue || '').split(','), v => this.__find(this.root.children, v, 'checked', true, true)); } } modelChanged(newValue) { this.root = new UITreeModel(-1, this.options.maxLevels, this.options.checkboxLevel, { id: '', name: this.options.rootLabel, children: newValue }, null); } getChecked() { return this.__getChecked(this.root.children); } @computedFrom('root') private get rootNodes() { return this.options.showRoot ? [this.root] : this.root.children; } private root: UITreeModel; private searchText: string = ''; private selectedNode: any = {}; __searchable; __ignoreChange; __subscribeSearch; @bindable({ defaultBindingMode: bindingMode.twoWay }) value; @bindable() model = []; @bindable() options: UITreeOptions = new UITreeOptions(); private __find(obj, id, field, value = true, expand = false) { var self = this; return _.find(obj, (n: UITreeModel) => { var found = n.id == id; if (!found && _.isArray(n.children)) { found = !_.isEmpty(self.__find(n.children, id, field, value)); if (expand && found) n.expanded = true; } else if (found) { if (field == 'active') self.__itemSelect(n); if (field == 'expanded') n.expanded = value; if (field == 'checked') n.isChecked = value ? 1 : 0; } return found; }); } private __getChecked(nodes, retVal = { checked: [], partial: [], unchecked: [] }) { var self = this; _.forEach(nodes, (n: UITreeModel) => { if (n.checked == 2) retVal.partial.push(n.id); if (n.checked == 1) retVal.checked.push(n.id); if (n.checked == 0) retVal.unchecked.push(n.id); if (_.isArray(n.children)) self.__getChecked(n.children, retVal); }); return retVal; } private __itemSelect(node) { if (UIEvent.fireEvent('beforeselect', this.element, node)) { let p; this.__ignoreChange = true; if (this.selectedNode) { (p = this.selectedNode).active = false; while (p = p.parent) p.childActive = false; } (p = this.selectedNode = node).active = true; while (p = p.parent) p.childActive = true; this.value = node.id; UIEvent.fireEvent('select', this.element, node); UIEvent.queueTask(() => this.__ignoreChange = false); } } private __itemChecked(node) { if (UIEvent.fireEvent('beforechecked', this.element, node)) { this.__ignoreChange = true; node.isChecked = !node.checked; let nodes = this.__getChecked(this.root.children); this.value = nodes.checked.join(','); UIEvent.fireEvent('checked', this.element, node); UIEvent.queueTask(() => this.__ignoreChange = false); } } private __itemClicked(node) { if (node.root) return; if (this.options.showCheckbox) { if (node.level >= this.options.checkboxLevel) { this.__itemChecked(node); } } else if (node.level < this.options.selectionLevel) { node.expanded = !node.expanded; } else if (node.level >= this.options.selectionLevel) { this.__itemSelect(node); } } private __searchTextChanged(newValue) { this.__filter(this.root.children, newValue); } private __filter(obj, value, parentVisible: boolean = false): boolean { var self = this, ret = false, rx = new RegExp(getAscii(value), 'gi'); _.forEach(obj, (n: UITreeModel) => { n.text = n.text.replace(//gi, '') .replace(/<\/b>/gi, ''); n.expanded = !_.isEmpty(value) && n.level <= 2 && parentVisible === false; if (_.isEmpty(value) && self.selectedNode.id == n.id && self.selectedNode.level == n.level) { var p = n.parent; while (p) { p.expanded = true; p = p.parent; } } var match = rx.test(getAscii(n.text)); if (!_.isEmpty(value) && match) { n.parent.expanded = true; let start = getAscii(n.text).search(rx); let name = n.text.substr(0, start + value.length) + '' + n.text.substr(start + value.length); n.text = name.substr(0, start) + '' + name.substr(start); } n.isVisible = n.children.length > 0 ? self.__filter(n.children, value, match || parentVisible) : (match || parentVisible); ret = ret || n.isVisible; }); return ret; } } @autoinject() @inlineView(``) export class TreeNode { @bindable node: UITreeModel; @bindable options: UITreeOptions; constructor(public element: Element) { } fireClicked() { UIEvent.fireEvent('nodeclick', this.element, this.node); } }