export class ListCursor { constructor({highlight_class, list}) { const config_ok = highlight_class && list && list.scroll_container_sel && list.find_li && list.first_key && list.prev_key && list.next_key; if (!config_ok) { blueslip.error("Programming error"); return; } this.highlight_class = highlight_class; this.list = list; } clear() { if (this.curr_key === undefined) { return; } const row = this.get_row(this.curr_key); if (row) { row.clear(); } this.curr_key = undefined; } get_key() { return this.curr_key; } get_row(key) { // TODO: The list class should probably do more of the work // here, so we're not so coupled to jQuery, and // so we instead just get back a widget we can say // something like widget.trigger("select") on. This will // be especially important if we do lazy rendering. // It would also give the caller more flexibility on // the actual styling. if (key === undefined) { return undefined; } const li = this.list.find_li({ key, force_render: true, }); if (!li || li.length === 0) { return undefined; } return { highlight: () => { li.addClass(this.highlight_class); this.adjust_scroll(li); }, clear: () => { li.removeClass(this.highlight_class); }, }; } adjust_scroll(li) { const scroll_container = $(this.list.scroll_container_sel); scroll_util.scroll_element_into_container(li, scroll_container); } redraw() { // We should only call this for situations like the buddy // list where we redraw the whole list without necessarily // changing it, so we just want to re-highlight the current // row in the new DOM. If you are filtering, for now you // should call the 'reset()' method. const row = this.get_row(this.curr_key); if (row === undefined) { return; } row.highlight(); } go_to(key) { if (key === undefined) { blueslip.error("Caller is not checking keys for ListCursor.go_to"); return; } if (key === this.curr_key) { return; } this.clear(); const row = this.get_row(key); if (row === undefined) { blueslip.error("Cannot highlight key for ListCursor: " + key); return; } this.curr_key = key; row.highlight(); } reset() { this.clear(); const key = this.list.first_key(); if (key === undefined) { this.curr_key = undefined; return; } this.go_to(key); } prev() { if (this.curr_key === undefined) { return; } const key = this.list.prev_key(this.curr_key); if (key === undefined) { // leave the current key return; } this.go_to(key); } next() { if (this.curr_key === undefined) { // This is sort of a special case where we went from // an empty filter to having data. this.reset(); return; } const key = this.list.next_key(this.curr_key); if (key === undefined) { // leave the current key return; } this.go_to(key); } }