import m from 'mithril'; import classnames from 'classnames'; import { Classes, safeCall } from '../../_shared'; import { Popover, IPopoverAttrs } from '../popover'; import { AbstractComponent } from '../abstract-component'; import { Spinner } from '../spinner'; import { QueryList, IQueryListAttrs } from '../query-list'; export interface ISelectListAttrs extends IQueryListAttrs { /** * Closes popover on item select * @default true */ closeOnSelect?: boolean; /** * Attrs passed through to Popover component * @default {} */ popoverAttrs?: Partial; /** Trigger element */ trigger: m.Vnode; /** Header content */ header?: m.Children; /** Footer content */ footer?: m.Children; /** Toggles loading state of inner list */ loading?: boolean; } export class SelectList extends AbstractComponent> { public queryList = QueryList.ofType(); private isOpen: boolean; private activeIndex: number = 0; public static ofType() { return SelectList as new () => SelectList; } public getDefaultAttrs() { return { closeOnSelect: true, popoverAttrs: {}, inputAttrs: {} } as ISelectListAttrs; } public oninit(vnode: m.Vnode>) { super.oninit(vnode); const { isOpen, defaultIsOpen } = vnode.attrs.popoverAttrs!; this.isOpen = isOpen != null ? isOpen : defaultIsOpen != null ? defaultIsOpen : false; } public onbeforeupdate(vnode: m.Vnode>, old: m.VnodeDOM>) { super.onbeforeupdate(vnode, old); const isOpen = vnode.attrs.popoverAttrs!.isOpen; const wasOpen = old.attrs.popoverAttrs!.isOpen; if (isOpen && !wasOpen) { this.isOpen = true; } else if (!isOpen && wasOpen) { this.isOpen = false; } } public view() { const { class: className, popoverAttrs, header, footer, trigger, closeOnSelect, loading, ...queryListAttrs } = this.attrs; const queryList = m(this.queryList, { activeIndex: this.activeIndex, onActiveItemChange: this.handleActiveItemChange, ...queryListAttrs, inputAttrs: { ...queryListAttrs.inputAttrs, autofocus: true }, onSelect: this.handleSelect }); const content = [ header, m(Spinner, { active: loading, background: true, fill: true }), queryList, footer ]; return m(Popover, { autofocus: true, position: 'bottom-start', closeOnEscapeKey: false, ...popoverAttrs, class: classnames(Classes.SELECT_LIST, className, popoverAttrs!.class), isOpen: this.isOpen, content, onInteraction: this.handlePopoverInteraction, trigger }); } private handleActiveItemChange = (activeItem: T, index: number) => { this.activeIndex = index; safeCall(this.attrs.onActiveItemChange, activeItem, index); }; private handleSelect = (item: T, e: Event, index: number) => { const { onSelect, closeOnSelect } = this.attrs; if (closeOnSelect) { this.isOpen = false; } safeCall(onSelect, item, e, index); }; private handlePopoverInteraction = (nextOpenState: boolean, e: Event) => { const { isOpen, onInteraction } = this.attrs.popoverAttrs!; if (isOpen != null) { safeCall(onInteraction, nextOpenState, e); } else this.isOpen = nextOpenState; }; }