UNPKG

jiro-ui

Version:

A Mithril.js UI library based from construct-ui

265 lines (264 loc) 12.5 kB
// Credits go to Blueprintjs for API structure // https://github.com/palantir/blueprint/blob/develop/packages/select/src/components/query-list/queryList.tsx import { __assign, __extends, __rest } from "tslib"; import m from 'mithril'; import classnames from 'classnames'; import debounce from 'lodash.debounce'; import { Classes, isFunction, Keys, safeCall, getClosest } from '../../_shared'; import { AbstractComponent } from '../abstract-component'; import { Icon, Icons } from '../icon'; import { List, ListItem } from '../list'; import { Input } from '../input'; import { ControlGroup } from '../control-group'; var QueryList = /** @class */ (function (_super) { __extends(QueryList, _super); function QueryList() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.filteredItems = []; _this.renderItem = function (item, index) { var _a = _this.attrs, itemRender = _a.itemRender, disableArrowKeys = _a.disableArrowKeys, checkmark = _a.checkmark, listAttrs = _a.listAttrs; var listItem = itemRender(item, index); listItem.attrs = listItem.attrs || {}; listItem.attrs.onclick = function (e) { return _this.handleSelect(index, listItem.attrs.disabled, e); }; if (!disableArrowKeys && _this.activeIndex === index) { listItem.attrs.class = classnames(listItem.attrs.className, listItem.attrs.class, Classes.ACTIVE); } if (listItem.tag === ListItem) { if (listItem.attrs.selected && checkmark) { listItem.attrs.contentLeft = m(Icon, { name: Icons.CHECK, size: listAttrs.size }); } } return listItem; }; _this.handleInput = function (e) { _this.handleSearchDebounce(e); e.redraw = false; }; _this.handleSearchDebounce = debounce(function (e) { var value = e.target.value; _this.updateQuery(value); _this.filteredItems = _this.getFilteredItems(); _this.updateActiveIndex(0); m.redraw(); }, 200); _this.handleInputClear = function () { _this.updateQuery(''); _this.updateActiveIndex(0); _this.filteredItems = _this.getFilteredItems(); _this.scrollToActiveItem(); if (_this.inputEl) { _this.inputEl.focus(); } }; _this.handleSelect = function (index, isDisabled, e) { var onSelect = _this.attrs.onSelect; var target = e.target; var selectedItem = _this.filteredItems[index]; var actionsEl = getClosest(target, "." + Classes.LIST_ITEM_CONTENT_RIGHT); if (selectedItem != null && !actionsEl && !isDisabled) { _this.updateActiveIndex(index); safeCall(onSelect, selectedItem, e, index); } else e.redraw = false; }; _this.handleKeyDown = function (e) { var key = e.which; switch (key) { case Keys.ARROW_UP: case Keys.ARROW_DOWN: if (!_this.attrs.disableArrowKeys) { e.preventDefault(); _this.moveActiveIndex(key === Keys.ARROW_UP ? 'up' : 'down'); m.redraw(); } break; case Keys.ESCAPE: if (_this.query) { _this.handleInputClear(); m.redraw(); } break; case Keys.ENTER: _this.handleEnterKey(e); m.redraw(); break; default: break; } e.redraw = false; }; return _this; } QueryList.ofType = function () { return QueryList; }; QueryList.prototype.getDefaultAttrs = function () { return { cacheItems: true, checkmark: true, inputAttrs: {}, listAttrs: {}, filterable: true, controlGroupAttrs: {}, emptyContent: 'No items available.' }; }; QueryList.prototype.oninit = function (vnode) { _super.prototype.oninit.call(this, vnode); this.query = this.attrs.defaultQuery || ''; this.activeIndex = this.attrs.defaultActiveIndex || 0; this.setControlledAttrs(); this.filteredItems = this.getFilteredItems(); }; QueryList.prototype.oncreate = function (_a) { var dom = _a.dom; this.listEl = dom.querySelector("." + Classes.LIST); this.inputEl = dom.querySelector("." + Classes.INPUT); this.scrollToActiveItem(); }; QueryList.prototype.onbeforeupdate = function (vnode, old) { _super.prototype.onbeforeupdate.call(this, vnode, old); this.setControlledAttrs(); if (vnode.attrs.items !== old.attrs.items || vnode.attrs.query !== old.attrs.query || vnode.attrs.activeIndex !== old.attrs.activeIndex || !vnode.attrs.cacheItems) { this.filteredItems = this.getFilteredItems(); this.scrollToActiveItem(); } }; QueryList.prototype.view = function () { var _a = this.attrs, activeIndex = _a.activeIndex, cacheItems = _a.cacheItems, checkmark = _a.checkmark, className = _a.class, controlGroupAttrs = _a.controlGroupAttrs, contentLeft = _a.contentLeft, contentRight = _a.contentRight, defaultActiveIndex = _a.defaultActiveIndex, defaultQuery = _a.defaultQuery, emptyContent = _a.emptyContent, eventCallbacks = _a.eventCallbacks, filterable = _a.filterable, initialContent = _a.initialContent, inputAttrs = _a.inputAttrs, itemPredicate = _a.itemPredicate, itemListPredicate = _a.itemListPredicate, itemListRender = _a.itemListRender, itemRender = _a.itemRender, items = _a.items, listAttrs = _a.listAttrs, onActiveItemChange = _a.onActiveItemChange, onSelect = _a.onSelect, query = _a.query, onQueryChange = _a.onQueryChange, htmlAttrs = __rest(_a, ["activeIndex", "cacheItems", "checkmark", "class", "controlGroupAttrs", "contentLeft", "contentRight", "defaultActiveIndex", "defaultQuery", "emptyContent", "eventCallbacks", "filterable", "initialContent", "inputAttrs", "itemPredicate", "itemListPredicate", "itemListRender", "itemRender", "items", "listAttrs", "onActiveItemChange", "onSelect", "query", "onQueryChange"]); var classes = classnames(Classes.QUERY_LIST, checkmark && Classes.QUERY_LIST_CHECKMARK, className); safeCall(eventCallbacks, { handleKeyDown: this.handleKeyDown }); var innerContent = [ filterable && this.renderControlGroup(), this.renderList() ]; return m('', __assign(__assign({}, htmlAttrs), { class: classes, onkeydown: this.handleKeyDown, tabindex: 0 }), innerContent); }; QueryList.prototype.renderControlGroup = function () { var _a = this.attrs, inputAttrs = _a.inputAttrs, controlGroupAttrs = _a.controlGroupAttrs, contentLeft = _a.contentLeft, contentRight = _a.contentRight; return m(ControlGroup, __assign(__assign({}, this.attrs.controlGroupAttrs), { class: classnames(Classes.FLUID, controlGroupAttrs.class) }), [ contentLeft, m(Input, __assign(__assign({ placeholder: 'Search items...' }, inputAttrs), { oninput: this.handleInput, contentRight: (this.query.length !== 0) ? m(Icon, { name: Icons.X, onclick: this.handleInputClear }) : inputAttrs.contentRight, value: this.query })), contentRight ]); }; QueryList.prototype.renderList = function () { var _a = this.attrs, listAttrs = _a.listAttrs, emptyContent = _a.emptyContent, initialContent = _a.initialContent; this.itemNodes = this.filteredItems.map(this.renderItem); var isEmpty = this.filteredItems.length === 0; var hasInitialContent = initialContent && this.query === ''; var classes = classnames(isEmpty && Classes.QUERY_LIST_EMPTY, hasInitialContent && Classes.QUERY_LIST_INITIAL, listAttrs.class); var emptyOrInitialContent = m("." + Classes.QUERY_LIST_MESSAGE, (hasInitialContent && !isEmpty) && initialContent, isEmpty && emptyContent); var content = (hasInitialContent || isEmpty) ? emptyOrInitialContent : this.itemNodes; return m(List, __assign(__assign({}, listAttrs), { class: classes }), content); }; QueryList.prototype.setControlledAttrs = function () { var _a = this.attrs, activeIndex = _a.activeIndex, query = _a.query; if (query != null) { this.query = query; } if (activeIndex != null) { this.activeIndex = activeIndex === -1 ? 0 : activeIndex; } }; QueryList.prototype.scrollToActiveItem = function () { var _a = this, listEl = _a.listEl, activeIndex = _a.activeIndex; if (listEl && activeIndex >= 0) { var activeEl = listEl.children[activeIndex]; if (!activeEl) return; var activeTop = activeEl.offsetTop, activeHeight = activeEl.offsetHeight; var listScrollTop = listEl.scrollTop, listHeight = listEl.clientHeight; var activeBottomEdge = activeTop + activeHeight; var activeTopEdge = activeTop; if (activeBottomEdge >= listScrollTop + listHeight) { listEl.scrollTop = activeBottomEdge + activeHeight - listHeight; } else if (activeTopEdge <= listScrollTop) { listEl.scrollTop = activeTopEdge - activeHeight; } } }; Object.defineProperty(QueryList.prototype, "activeItem", { get: function () { return this.filteredItems[this.activeIndex]; }, enumerable: false, configurable: true }); QueryList.prototype.updateQuery = function (text) { var _a = this.attrs, query = _a.query, onQueryChange = _a.onQueryChange; if (query != null) { safeCall(onQueryChange, text); } else this.query = text; }; QueryList.prototype.moveActiveIndex = function (direction) { var activeIndex = this.activeIndex; var index = getNextIndex(activeIndex, this.itemNodes, direction); this.updateActiveIndex(index); this.scrollToActiveItem(); }; QueryList.prototype.updateActiveIndex = function (index) { var _a = this.attrs, activeIndex = _a.activeIndex, onActiveItemChange = _a.onActiveItemChange; var currentIndex = index > this.filteredItems.length ? 0 : index; if (activeIndex != null) { safeCall(onActiveItemChange, this.activeItem, currentIndex); } else this.activeIndex = currentIndex; }; QueryList.prototype.handleEnterKey = function (e) { var item = this.activeItem; if (item != null) { safeCall(this.attrs.onSelect, item, e); } }; QueryList.prototype.getFilteredItems = function () { var _this = this; var _a = this.attrs, items = _a.items, itemPredicate = _a.itemPredicate, itemListPredicate = _a.itemListPredicate; if (isFunction(itemListPredicate)) { return itemListPredicate(this.query, items); } if (isFunction(itemPredicate)) { return items.filter(function (item, index) { return itemPredicate(_this.query, item, index); }); } return items; }; return QueryList; }(AbstractComponent)); export { QueryList }; function getNextIndex(currentIndex, vnodes, direction) { var maxIndex = vnodes.length - 1; var index = currentIndex; var flag = true; if (index < 0 || maxIndex <= 0) { return 0; } while (flag) { index = direction === 'up' ? index === 0 ? maxIndex : index - 1 : index === maxIndex ? 0 : index + 1; var vnode = vnodes[index]; var attrs = vnode && vnode.attrs; if (attrs && !attrs.disabled) { flag = false; } } return index; }